Files
tradon/modules/automation/booking_report.py
2026-07-26 19:59:09 +02:00

194 lines
6.5 KiB
Python

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'