import json from sql import Literal, Null from sql.functions import CurrentTimestamp from trytond.model import ModelSQL, ModelView, fields from trytond.pool import Pool from trytond.transaction import Transaction from trytond.wizard import Button, StateTransition, StateView, Wizard from trytond.exceptions import UserError 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 } class FreightBookingShipmentReportLinkStart(ModelView): "Link Freight Booking Shipment Report" __name__ = 'freight.booking.shipment.report.link.start' bl_number = fields.Char("BL Number", readonly=True) shipment = fields.Many2One( 'stock.shipment.in', "Shipment", required=True) class FreightBookingShipmentReportLink(Wizard): "Link Freight Booking Shipment Report" __name__ = 'freight.booking.shipment.report.link' start = StateView( 'freight.booking.shipment.report.link.start', 'automation.freight_booking_shipment_report_link_start_form', [ Button('Cancel', 'end', 'tryton-cancel'), Button('Link', 'link', 'tryton-ok', default=True), ]) link = StateTransition() def default_start(self, fields): Report = Pool().get('freight.booking.shipment.report') active_ids = Transaction().context.get('active_ids') or [] reports = Report.browse(active_ids) error_reports = [ report for report in reports if report.status == 'error' and report.bl_number] if len(error_reports) > 1: raise UserError( "Link one error line at a time to avoid overwriting the " "shipment BL number.") return { 'bl_number': error_reports[0].bl_number if error_reports else None, } def transition_link(self): Report = Pool().get('freight.booking.shipment.report') ShipmentIn = Pool().get('stock.shipment.in') active_ids = Transaction().context.get('active_ids') or [] reports = Report.browse(active_ids) error_reports = [ report for report in reports if report.status == 'error' and report.bl_number] if not error_reports: raise UserError( "Select at least one error line with a BL number.") if len(error_reports) > 1: raise UserError( "Link one error line at a time to avoid overwriting the " "shipment BL number.") shipment = self.start.shipment shipment.bl_number = error_reports[0].bl_number ShipmentIn.save([shipment]) return 'end' def end(self): return 'reload'