folder rename

This commit is contained in:
gsinghpal
2026-04-16 20:53:53 -04:00
parent 3f3ddcbab4
commit 7c7ef06057
634 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from . import fp_vehicle
from . import hr_employee
from . import fp_pickup_request
from . import fp_delivery
from . import fp_route
from . import fp_route_stop
from . import fp_chain_of_custody
from . import fp_proof_of_delivery

View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import api, fields, models
class FpChainOfCustody(models.Model):
"""A single custody event — the audit trail for parts in transit.
A chain of custody record is created every time parts change hands:
received from the customer, loaded on a vehicle, entered a facility,
transferred between facilities, or delivered to the customer.
These records are append-only in practice and form the evidence
trail that quality and compliance audits rely on. They are linked
to either a pickup request or a delivery (or both, for a full
round-trip).
"""
_name = 'fusion.plating.chain.of.custody'
_description = 'Fusion Plating — Chain of Custody Event'
_order = 'event_datetime desc, id desc'
_rec_name = 'display_name'
name = fields.Char(
string='Reference',
)
display_name = fields.Char(
compute='_compute_display_name',
store=True,
)
event_datetime = fields.Datetime(
string='Event Time',
required=True,
default=fields.Datetime.now,
)
event_type = fields.Selection(
[
('received_from_customer', 'Received from Customer'),
('entered_facility', 'Entered Facility'),
('exited_facility', 'Exited Facility'),
('loaded_on_vehicle', 'Loaded on Vehicle'),
('delivered_to_customer', 'Delivered to Customer'),
('transferred_between_facilities', 'Transferred Between Facilities'),
],
string='Event Type',
required=True,
)
from_party = fields.Char(
string='From',
)
to_party = fields.Char(
string='To',
)
pickup_request_id = fields.Many2one(
'fusion.plating.pickup.request',
string='Pickup Request',
ondelete='set null',
)
delivery_id = fields.Many2one(
'fusion.plating.delivery',
string='Delivery',
ondelete='set null',
)
facility_id = fields.Many2one(
'fusion.plating.facility',
string='Facility',
)
company_id = fields.Many2one(
'res.company',
related='facility_id.company_id',
store=True,
readonly=True,
)
recorded_by_id = fields.Many2one(
'res.users',
string='Recorded By',
default=lambda self: self.env.user,
)
notes = fields.Text(
string='Notes',
)
@api.depends('event_type', 'event_datetime', 'from_party', 'to_party')
def _compute_display_name(self):
selection = dict(self._fields['event_type']._description_selection(self.env))
for rec in self:
label = selection.get(rec.event_type, rec.event_type or '')
when = fields.Datetime.to_string(rec.event_datetime) if rec.event_datetime else ''
rec.display_name = f'{label} @ {when}' if when else label

View File

@@ -0,0 +1,211 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import api, fields, models
class FpDelivery(models.Model):
"""Scheduled delivery of finished parts back to a customer.
Lifecycle:
draft → scheduled → en_route → delivered
→ refused
→ returned
→ cancelled
A delivery references a job by soft reference (`job_ref`) because
the job module is not yet built. Once the job module ships, this
field can be deprecated in favour of a proper Many2one without
migration.
"""
_name = 'fusion.plating.delivery'
_description = 'Fusion Plating — Delivery'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'scheduled_date desc, id desc'
name = fields.Char(
string='Reference',
required=True,
copy=False,
default=lambda self: self._default_name(),
tracking=True,
)
partner_id = fields.Many2one(
'res.partner',
string='Customer',
required=True,
tracking=True,
)
delivery_address_id = fields.Many2one(
'res.partner',
string='Delivery Address',
help='Leave blank to use the customer default address.',
)
contact_name = fields.Char(
string='Contact Name',
)
contact_phone = fields.Char(
string='Contact Phone',
)
job_ref = fields.Char(
string='Job Reference',
help='Soft reference to the job this delivery belongs to. '
'Will become a Many2one once the job module ships.',
tracking=True,
)
scheduled_date = fields.Datetime(
string='Scheduled Date',
tracking=True,
)
assigned_driver_id = fields.Many2one(
'hr.employee',
string='Assigned Driver',
tracking=True,
domain=[('x_fc_is_driver', '=', True)],
)
vehicle_id = fields.Many2one(
'fusion.plating.vehicle',
string='Vehicle',
tracking=True,
)
source_facility_id = fields.Many2one(
'fusion.plating.facility',
string='Source Facility',
tracking=True,
)
company_id = fields.Many2one(
'res.company',
related='source_facility_id.company_id',
store=True,
readonly=True,
)
tdg_required = fields.Boolean(
string='TDG Required',
tracking=True,
)
coc_attachment_id = fields.Many2one(
'ir.attachment',
string='Certificate of Conformance',
)
packing_list_attachment_id = fields.Many2one(
'ir.attachment',
string='Packing List',
)
state = fields.Selection(
[
('draft', 'Draft'),
('scheduled', 'Scheduled'),
('en_route', 'En Route'),
('delivered', 'Delivered'),
('refused', 'Refused'),
('returned', 'Returned'),
('cancelled', 'Cancelled'),
],
string='Status',
default='draft',
required=True,
tracking=True,
)
delivered_at = fields.Datetime(
string='Delivered At',
readonly=True,
)
pod_id = fields.Many2one(
'fusion.plating.proof.of.delivery',
string='Proof of Delivery',
copy=False,
)
notes = fields.Html(
string='Notes',
)
custody_event_ids = fields.One2many(
'fusion.plating.chain.of.custody',
'delivery_id',
string='Custody Events',
)
custody_event_count = fields.Integer(
compute='_compute_custody_count',
)
@api.model
def _default_name(self):
seq = self.env['ir.sequence'].next_by_code('fusion.plating.delivery')
return seq or '/'
def _compute_custody_count(self):
for rec in self:
rec.custody_event_count = len(rec.custody_event_ids)
def _log_custody_event(self, event_type, from_party=None, to_party=None):
self.ensure_one()
self.env['fusion.plating.chain.of.custody'].create({
'event_datetime': fields.Datetime.now(),
'event_type': event_type,
'from_party': from_party or '',
'to_party': to_party or '',
'delivery_id': self.id,
'facility_id': self.source_facility_id.id,
'recorded_by_id': self.env.user.id,
})
# ==========================================================================
# Actions
# ==========================================================================
def action_schedule(self):
self.write({'state': 'scheduled'})
def action_start_route(self):
for rec in self:
rec.write({'state': 'en_route'})
rec._log_custody_event(
'loaded_on_vehicle',
from_party=(rec.source_facility_id.display_name or 'Facility'),
to_party=(rec.assigned_driver_id.display_name
or rec.vehicle_id.display_name
or 'Driver'),
)
def action_mark_delivered(self):
for rec in self:
rec.write({
'state': 'delivered',
'delivered_at': fields.Datetime.now(),
})
rec._log_custody_event(
'delivered_to_customer',
from_party=(rec.assigned_driver_id.display_name
or rec.vehicle_id.display_name
or 'Driver'),
to_party=rec.partner_id.display_name,
)
def action_mark_refused(self):
self.write({'state': 'refused'})
def action_mark_returned(self):
self.write({'state': 'returned'})
def action_cancel(self):
self.write({'state': 'cancelled'})
def action_reset_to_draft(self):
self.write({'state': 'draft'})
def action_create_pod(self):
"""Create a blank POD record for this delivery and open it."""
self.ensure_one()
pod = self.env['fusion.plating.proof.of.delivery'].create({
'delivery_id': self.id,
'delivered_at': fields.Datetime.now(),
})
self.pod_id = pod.id
return {
'type': 'ir.actions.act_window',
'res_model': 'fusion.plating.proof.of.delivery',
'res_id': pod.id,
'view_mode': 'form',
'target': 'current',
}

View File

@@ -0,0 +1,192 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import api, fields, models
class FpPickupRequest(models.Model):
"""Customer-initiated request for pickup of parts to be processed.
Lifecycle:
new → scheduled → en_route → picked_up → received → (cancelled)
A pickup request is created when a customer phones or emails asking
for parts to be collected. Dispatch schedules a driver + vehicle,
the driver updates status on the road, and the receiving facility
confirms arrival of the parts. Chain of custody events are written
automatically as the state transitions.
"""
_name = 'fusion.plating.pickup.request'
_description = 'Fusion Plating — Pickup Request'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'requested_date desc, id desc'
name = fields.Char(
string='Reference',
required=True,
copy=False,
default=lambda self: self._default_name(),
tracking=True,
)
partner_id = fields.Many2one(
'res.partner',
string='Customer',
required=True,
tracking=True,
)
pickup_address_id = fields.Many2one(
'res.partner',
string='Pickup Address',
help='Leave blank to use the customer default address.',
)
contact_name = fields.Char(
string='Contact Name',
)
contact_phone = fields.Char(
string='Contact Phone',
)
requested_date = fields.Datetime(
string='Requested Date',
default=fields.Datetime.now,
tracking=True,
)
scheduled_date = fields.Datetime(
string='Scheduled Date',
tracking=True,
)
assigned_driver_id = fields.Many2one(
'hr.employee',
string='Assigned Driver',
tracking=True,
domain=[('x_fc_is_driver', '=', True)],
)
vehicle_id = fields.Many2one(
'fusion.plating.vehicle',
string='Vehicle',
tracking=True,
)
destination_facility_id = fields.Many2one(
'fusion.plating.facility',
string='Destination Facility',
tracking=True,
)
company_id = fields.Many2one(
'res.company',
related='destination_facility_id.company_id',
store=True,
readonly=True,
)
item_description = fields.Html(
string='Items to Pick Up',
)
estimated_weight_kg = fields.Float(
string='Est. Weight (kg)',
)
tdg_required = fields.Boolean(
string='TDG Required',
tracking=True,
help='Check if the load qualifies as Transportation of Dangerous '
'Goods. Only TDG-certified drivers and vehicles may be '
'assigned.',
)
state = fields.Selection(
[
('new', 'New'),
('scheduled', 'Scheduled'),
('en_route', 'En Route'),
('picked_up', 'Picked Up'),
('received', 'Received at Facility'),
('cancelled', 'Cancelled'),
],
string='Status',
default='new',
required=True,
tracking=True,
)
picked_up_at = fields.Datetime(
string='Picked Up At',
readonly=True,
)
received_at = fields.Datetime(
string='Received At',
readonly=True,
)
notes = fields.Html(
string='Notes',
)
custody_event_ids = fields.One2many(
'fusion.plating.chain.of.custody',
'pickup_request_id',
string='Custody Events',
)
custody_event_count = fields.Integer(
compute='_compute_custody_count',
)
@api.model
def _default_name(self):
seq = self.env['ir.sequence'].next_by_code('fusion.plating.pickup.request')
return seq or '/'
def _compute_custody_count(self):
for rec in self:
rec.custody_event_count = len(rec.custody_event_ids)
def _log_custody_event(self, event_type, from_party=None, to_party=None):
self.ensure_one()
self.env['fusion.plating.chain.of.custody'].create({
'event_datetime': fields.Datetime.now(),
'event_type': event_type,
'from_party': from_party or '',
'to_party': to_party or '',
'pickup_request_id': self.id,
'facility_id': self.destination_facility_id.id,
'recorded_by_id': self.env.user.id,
})
# ==========================================================================
# Actions
# ==========================================================================
def action_schedule(self):
self.write({'state': 'scheduled'})
def action_start_route(self):
self.write({'state': 'en_route'})
def action_mark_picked_up(self):
for rec in self:
rec.write({
'state': 'picked_up',
'picked_up_at': fields.Datetime.now(),
})
rec._log_custody_event(
'received_from_customer',
from_party=rec.partner_id.display_name,
to_party=(rec.assigned_driver_id.display_name
or rec.vehicle_id.display_name
or 'Driver'),
)
def action_mark_received(self):
for rec in self:
rec.write({
'state': 'received',
'received_at': fields.Datetime.now(),
})
rec._log_custody_event(
'entered_facility',
from_party=(rec.assigned_driver_id.display_name
or rec.vehicle_id.display_name
or 'Driver'),
to_party=(rec.destination_facility_id.display_name
or 'Facility'),
)
def action_cancel(self):
self.write({'state': 'cancelled'})
def action_reset_to_new(self):
self.write({'state': 'new'})

View File

@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import api, fields, models
class FpProofOfDelivery(models.Model):
"""Proof of delivery record — captured at the delivery point.
Captures:
* recipient name
* recipient signature (binary image)
* photos of the delivered load
* GPS coordinates at time of capture
* delivery timestamp
A POD is typically created via the delivery's "Create POD" action,
which pre-fills the delivery_id and timestamps the record.
"""
_name = 'fusion.plating.proof.of.delivery'
_description = 'Fusion Plating — Proof of Delivery'
_order = 'delivered_at desc, id desc'
name = fields.Char(
string='Reference',
required=True,
copy=False,
default=lambda self: self._default_name(),
)
delivery_id = fields.Many2one(
'fusion.plating.delivery',
string='Delivery',
ondelete='cascade',
)
partner_id = fields.Many2one(
'res.partner',
related='delivery_id.partner_id',
store=True,
readonly=True,
)
recipient_name = fields.Char(
string='Recipient Name',
)
recipient_signature = fields.Binary(
string='Signature',
attachment=True,
)
delivered_at = fields.Datetime(
string='Delivered At',
default=fields.Datetime.now,
)
photo_ids = fields.Many2many(
'ir.attachment',
'fp_pod_photo_rel',
'pod_id',
'attachment_id',
string='Photos',
)
notes = fields.Text(
string='Notes',
)
gps_lat = fields.Float(
string='GPS Latitude',
digits=(9, 6),
)
gps_lon = fields.Float(
string='GPS Longitude',
digits=(9, 6),
)
@api.model
def _default_name(self):
seq = self.env['ir.sequence'].next_by_code('fusion.plating.proof.of.delivery')
return seq or '/'

View File

@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import api, fields, models
class FpRoute(models.Model):
"""A single run combining pickups and deliveries for one driver.
Lifecycle:
draft → planned → in_progress → completed → (cancelled)
Dispatch drops pickup requests and deliveries on the route as stops,
re-orders them using the `sequence` handle, then hands the route to
the driver. The driver ticks off stops as they work, and the route
captures total distance (km) and elapsed time for costing.
"""
_name = 'fusion.plating.route'
_description = 'Fusion Plating — Route'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'route_date desc, id desc'
name = fields.Char(
string='Reference',
required=True,
copy=False,
default=lambda self: self._default_name(),
tracking=True,
)
route_date = fields.Date(
string='Route Date',
required=True,
default=fields.Date.context_today,
tracking=True,
)
driver_id = fields.Many2one(
'hr.employee',
string='Driver',
required=True,
tracking=True,
domain=[('x_fc_is_driver', '=', True)],
)
vehicle_id = fields.Many2one(
'fusion.plating.vehicle',
string='Vehicle',
required=True,
tracking=True,
)
company_id = fields.Many2one(
'res.company',
related='vehicle_id.company_id',
store=True,
readonly=True,
)
state = fields.Selection(
[
('draft', 'Draft'),
('planned', 'Planned'),
('in_progress', 'In Progress'),
('completed', 'Completed'),
('cancelled', 'Cancelled'),
],
string='Status',
default='draft',
required=True,
tracking=True,
)
start_time = fields.Datetime(
string='Start Time',
)
end_time = fields.Datetime(
string='End Time',
)
total_km = fields.Float(
string='Total Distance (km)',
)
stop_ids = fields.One2many(
'fusion.plating.route.stop',
'route_id',
string='Stops',
)
stop_count = fields.Integer(
compute='_compute_stop_count',
)
notes = fields.Html(
string='Notes',
)
@api.model
def _default_name(self):
seq = self.env['ir.sequence'].next_by_code('fusion.plating.route')
return seq or '/'
def _compute_stop_count(self):
for rec in self:
rec.stop_count = len(rec.stop_ids)
# ==========================================================================
# Actions
# ==========================================================================
def action_plan(self):
self.write({'state': 'planned'})
def action_start(self):
self.write({
'state': 'in_progress',
'start_time': fields.Datetime.now(),
})
def action_complete(self):
self.write({
'state': 'completed',
'end_time': fields.Datetime.now(),
})
def action_cancel(self):
self.write({'state': 'cancelled'})
def action_reset_to_draft(self):
self.write({'state': 'draft'})

View File

@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import fields, models
class FpRouteStop(models.Model):
"""A single stop on a route.
A stop is one of: pickup, delivery, transfer (between facilities),
fuel, or break. Pickup and delivery stops link back to the source
records so the driver can open them from the route. Non-transactional
stops (fuel, break) just carry an address and a planned time.
"""
_name = 'fusion.plating.route.stop'
_description = 'Fusion Plating — Route Stop'
_order = 'route_id, sequence, id'
route_id = fields.Many2one(
'fusion.plating.route',
string='Route',
required=True,
ondelete='cascade',
)
sequence = fields.Integer(
string='Sequence',
default=10,
)
stop_type = fields.Selection(
[
('pickup', 'Pickup'),
('delivery', 'Delivery'),
('transfer', 'Facility Transfer'),
('fuel', 'Fuel'),
('break', 'Break'),
],
string='Stop Type',
default='pickup',
required=True,
)
pickup_request_id = fields.Many2one(
'fusion.plating.pickup.request',
string='Pickup Request',
)
delivery_id = fields.Many2one(
'fusion.plating.delivery',
string='Delivery',
)
address_id = fields.Many2one(
'res.partner',
string='Address',
)
planned_time = fields.Datetime(
string='Planned Time',
)
actual_time = fields.Datetime(
string='Actual Time',
)
state = fields.Selection(
[
('pending', 'Pending'),
('arrived', 'Arrived'),
('completed', 'Completed'),
('skipped', 'Skipped'),
],
string='Status',
default='pending',
required=True,
)
notes = fields.Text(
string='Notes',
)
def action_mark_arrived(self):
self.write({
'state': 'arrived',
'actual_time': fields.Datetime.now(),
})
def action_mark_completed(self):
self.write({'state': 'completed'})
def action_mark_skipped(self):
self.write({'state': 'skipped'})

View File

@@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import fields, models
class FpVehicle(models.Model):
"""Vehicle master for the plating shop's pickup & delivery fleet.
A vehicle belongs to a facility (its home base), carries insurance,
registration and service dates, and tracks its current driver and
state. Vehicles flagged `tdg_certified` are approved to carry TDG
(Transportation of Dangerous Goods) loads — e.g. stripped parts with
residue, waste drums, spent chemistry.
The module works without Odoo's Enterprise Fleet module: all
fleet-level concerns are modelled here so that CE-only installations
are fully supported.
"""
_name = 'fusion.plating.vehicle'
_description = 'Fusion Plating — Vehicle'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'name'
name = fields.Char(
string='Vehicle',
required=True,
tracking=True,
help='Internal name — e.g. "Van 1" or "Box Truck 07".',
)
license_plate = fields.Char(
string='License Plate',
required=True,
tracking=True,
)
make = fields.Char(
string='Make',
)
model_year = fields.Integer(
string='Model Year',
)
vehicle_type = fields.Selection(
[
('van', 'Van'),
('truck_box', 'Box Truck'),
('truck_pickup', 'Pickup Truck'),
('flatbed', 'Flatbed'),
('trailer', 'Trailer'),
('other', 'Other'),
],
string='Type',
default='van',
required=True,
tracking=True,
)
capacity_kg = fields.Float(
string='Capacity (kg)',
help='Maximum payload capacity in kilograms.',
)
facility_id = fields.Many2one(
'fusion.plating.facility',
string='Home Facility',
tracking=True,
)
company_id = fields.Many2one(
'res.company',
related='facility_id.company_id',
store=True,
readonly=True,
)
active = fields.Boolean(
default=True,
)
insurance_expiry = fields.Date(
string='Insurance Expiry',
tracking=True,
)
registration_expiry = fields.Date(
string='Registration Expiry',
tracking=True,
)
next_service_date = fields.Date(
string='Next Service',
tracking=True,
)
state = fields.Selection(
[
('available', 'Available'),
('in_use', 'In Use'),
('maintenance', 'Maintenance'),
('out_of_service', 'Out of Service'),
],
string='Status',
default='available',
required=True,
tracking=True,
)
current_driver_id = fields.Many2one(
'hr.employee',
string='Current Driver',
tracking=True,
domain=[('x_fc_is_driver', '=', True)],
)
tdg_certified = fields.Boolean(
string='TDG Certified',
tracking=True,
help='Vehicle is approved to carry Transportation of Dangerous '
'Goods loads.',
)
notes = fields.Text(
string='Notes',
)
_sql_constraints = [
(
'fp_vehicle_license_plate_uniq',
'unique(license_plate)',
'License plate must be unique.',
),
]
def action_mark_available(self):
self.write({'state': 'available'})
def action_mark_in_use(self):
self.write({'state': 'in_use'})
def action_mark_maintenance(self):
self.write({'state': 'maintenance'})
def action_mark_out_of_service(self):
self.write({'state': 'out_of_service'})

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import fields, models
class HrEmployee(models.Model):
"""Extend hr.employee with driver fields.
Drivers are just employees with the `x_fc_is_driver` flag set. This
avoids creating a parallel driver model and keeps time tracking, HR
records, and payroll integrations working out of the box.
Uses the `x_fc_` prefix per the Fusion Central field naming
convention for extensions of base Odoo models.
"""
_inherit = 'hr.employee'
x_fc_is_driver = fields.Boolean(
string='Is Driver',
help='Check if this employee is authorised to drive company vehicles '
'for pickups and deliveries.',
)
x_fc_driver_licence_class = fields.Char(
string='Licence Class',
help='Driver licence class — e.g. G, G2, AZ, DZ.',
)
x_fc_licence_expiry = fields.Date(
string='Licence Expiry',
)
x_fc_tdg_certified = fields.Boolean(
string='TDG Certified',
help='Certified to transport Transportation of Dangerous Goods '
'(hazmat) loads.',
)
x_fc_tdg_expiry = fields.Date(
string='TDG Certificate Expiry',
)
x_fc_pickup_request_ids = fields.One2many(
'fusion.plating.pickup.request',
'assigned_driver_id',
string='Pickup Requests',
)
x_fc_delivery_ids = fields.One2many(
'fusion.plating.delivery',
'assigned_driver_id',
string='Deliveries',
)
x_fc_pickup_request_count = fields.Integer(
compute='_compute_logistics_counts',
)
x_fc_delivery_count = fields.Integer(
compute='_compute_logistics_counts',
)
def _compute_logistics_counts(self):
for rec in self:
rec.x_fc_pickup_request_count = len(rec.x_fc_pickup_request_ids)
rec.x_fc_delivery_count = len(rec.x_fc_delivery_ids)