import json from sql import Literal, Null from sql.functions import CurrentTimestamp from trytond.model import ModelSQL, ModelView, fields from trytond.pool import Pool class FreightBookingShipmentReport(ModelSQL, ModelView): "Freight Booking Shipment Report" __name__ = 'freight.booking.shipment.report' automation_document = fields.Many2One( 'automation.document', "Automation Document") bl_number = fields.Function(fields.Char("BL Number"), 'get_bl_number') shipment = fields.Function(fields.Many2One( 'stock.shipment.in', "Shipment"), 'get_shipment') status = fields.Function(fields.Selection([ ('found', "Found"), ('error', "Error"), ], "Status"), 'get_status') @classmethod def __setup__(cls): super().__setup__() cls._order = [ ('id', 'DESC'), ] @classmethod def table_query(cls): AutomationDocument = Pool().get('automation.document') document = AutomationDocument.__table__() return document.select( Literal(None).as_('create_uid'), CurrentTimestamp().as_('create_date'), Literal(None).as_('write_uid'), Literal(None).as_('write_date'), document.id.as_('id'), document.id.as_('automation_document'), where=( (document.type == 'weight_report') & (document.metadata_json != Null))) @classmethod def _extract_bl_number(cls, metadata_json): if not metadata_json: return None try: metadata = json.loads(str(metadata_json)) except (TypeError, ValueError): return None def find_value(value): if isinstance(value, dict): for key in ( 'bl_no', 'bl_number', 'bl', 'BL_Number', 'BL number', 'B/L No'): bl_number = value.get(key) if bl_number: return str(bl_number).strip() for item in value.values(): bl_number = find_value(item) if bl_number: return bl_number elif isinstance(value, list): for item in value: bl_number = find_value(item) if bl_number: return bl_number return None return find_value(metadata) @classmethod def _get_report_data(cls, reports): ShipmentIn = Pool().get('stock.shipment.in') bl_numbers = {} for report in reports: document = report.automation_document bl_number = cls._extract_bl_number( document.metadata_json if document else None) bl_numbers[report.id] = bl_number shipments_by_bl = {} for bl_number in {b for b in bl_numbers.values() if b}: shipments = ShipmentIn.search([ ('bl_number', '=', bl_number), ], limit=1) if not shipments: shipments = ShipmentIn.search([ ('bl_number', 'ilike', bl_number), ], limit=1) shipments_by_bl[bl_number] = shipments[0] if shipments else None return bl_numbers, shipments_by_bl @classmethod def get_bl_number(cls, reports, name): bl_numbers, _ = cls._get_report_data(reports) return bl_numbers @classmethod def get_shipment(cls, reports, name): bl_numbers, shipments_by_bl = cls._get_report_data(reports) return { report.id: ( shipments_by_bl[bl_numbers[report.id]].id if bl_numbers[report.id] and shipments_by_bl.get(bl_numbers[report.id]) else None) for report in reports } @classmethod def get_status(cls, reports, name): bl_numbers, shipments_by_bl = cls._get_report_data(reports) return { report.id: ( 'found' if bl_numbers[report.id] and shipments_by_bl.get(bl_numbers[report.id]) else 'error') for report in reports }