395 lines
14 KiB
Python
395 lines
14 KiB
Python
# This file is part of Tryton. The COPYRIGHT file at the top level
|
|
# of this repository contains the full copyright notices and license terms.
|
|
|
|
from trytond.model import Workflow, ModelView, ModelSQL, fields
|
|
from trytond.wizard import Wizard, StateView, Button, StateReport
|
|
from trytond.pyson import Eval
|
|
from trytond.transaction import Transaction
|
|
from trytond.modules.company import CompanyReport
|
|
from trytond.pool import Pool
|
|
from trytond.exceptions import UserError
|
|
from trytond.report import Report
|
|
|
|
STATES = {
|
|
'readonly': (Eval('state') != 'draft'),
|
|
}
|
|
|
|
|
|
class ChargeVehicle(ModelSQL, ModelView):
|
|
"Charge Vehicle"
|
|
__name__ = 'exportation.charge.vehicle'
|
|
_rec_name = 'plate'
|
|
plate = fields.Char('Plate')
|
|
|
|
|
|
class ExportationPort(ModelSQL, ModelView):
|
|
"Exportation Port"
|
|
__name__ = 'exportation.port'
|
|
name = fields.Char('Name')
|
|
city_info = fields.Char('City Info')
|
|
|
|
|
|
class ExportationCharge(Workflow, ModelSQL, ModelView):
|
|
"Exportation Charge"
|
|
__name__ = 'exportation.charge'
|
|
date = fields.Date('Date', required=True, states=STATES)
|
|
number = fields.Char('Number', readonly=True)
|
|
company = fields.Many2One('company.company', 'Company', required=True,
|
|
states=STATES)
|
|
carrier = fields.Many2One('party.party', 'Carrier', select=True,
|
|
states=STATES)
|
|
port = fields.Many2One('exportation.port', 'Port', select=True)
|
|
out_time = fields.Time('Out Time', states=STATES, required=False)
|
|
driver = fields.Many2One('party.party', 'Driver', required=True,
|
|
select=True)
|
|
driver2 = fields.Many2One('party.party', 'Driver 2',
|
|
select=True)
|
|
vehicle = fields.Many2One('exportation.charge.vehicle', 'Vehicle',
|
|
states=STATES, required=True)
|
|
vehicle2 = fields.Many2One('exportation.charge.vehicle', 'Vehicle 2',
|
|
states=STATES)
|
|
seal = fields.Char('Seal', states=STATES, required=False)
|
|
band = fields.Char('Band', states=STATES, required=False)
|
|
hts_list = fields.Function(fields.Char('HTS List'), 'get_hts_list')
|
|
description = fields.Char('Description', required=False)
|
|
lines = fields.One2Many('exportation.charge.line', 'charge', 'Lines',
|
|
states=STATES)
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('confirm', 'Confirm'),
|
|
('cancel', 'Canceled'),
|
|
], 'State', readonly=True, select=True)
|
|
sales = fields.Function(fields.One2Many('sale.sale', None, 'Sales'),
|
|
'get_sales')
|
|
total_amount = fields.Function(fields.Numeric('Total Charge',
|
|
digits=(16, 2)), 'get_total_amount')
|
|
pieces = fields.Function(fields.Float('Total Pieces',
|
|
digits=(6, 2)), 'get_value_uom')
|
|
units = fields.Function(fields.Float('Total Units',
|
|
digits=(6, 2)), 'get_value_uom')
|
|
fulles = fields.Function(fields.Float('Total Boxes',
|
|
digits=(6, 2)), 'get_value_uom')
|
|
dispatched_by = fields.Many2One('company.employee', 'Dispatched By',
|
|
states=STATES)
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(ExportationCharge, cls).__setup__()
|
|
cls._order.insert(0, ('date', 'DESC'))
|
|
cls._transitions |= set((
|
|
('draft', 'confirm'),
|
|
('draft', 'cancel'),
|
|
('confirm', 'draft'),
|
|
))
|
|
cls._buttons.update({
|
|
'cancel': {
|
|
'invisible': Eval('state') != 'draft'
|
|
},
|
|
'draft': {
|
|
'invisible': Eval('state') == 'draft',
|
|
},
|
|
'confirm': {
|
|
'invisible': Eval('state') != 'draft',
|
|
},
|
|
'select_sales': {
|
|
'invisible': Eval('state') != 'draft',
|
|
},
|
|
})
|
|
|
|
@staticmethod
|
|
def default_state():
|
|
return 'draft'
|
|
|
|
@staticmethod
|
|
def default_date():
|
|
Date = Pool().get('ir.date')
|
|
return Date.today()
|
|
|
|
@staticmethod
|
|
def default_company():
|
|
return Transaction().context.get('company') or False
|
|
|
|
def get_hts_list(self, name=None):
|
|
# Lista de subpartidas arancelarias (hts)
|
|
res = []
|
|
for line in self.lines:
|
|
if line.product.template.hts:
|
|
res.append(line.product.template.hts[0:4])
|
|
res = set(res)
|
|
return ' - '.join(list(res))
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
def select_sales(cls, records):
|
|
pool = Pool()
|
|
Sale = pool.get('sale.sale')
|
|
for charge in records:
|
|
if not charge.carrier:
|
|
continue
|
|
|
|
sales = Sale.search([
|
|
('carrier', '=', charge.carrier.id),
|
|
('state', '=', 'processing'),
|
|
('charge', '=', None),
|
|
('shipping_date', '=', charge.date),
|
|
])
|
|
|
|
for sale in sales:
|
|
freight_forwader = sale.freight_forwader
|
|
party = sale.invoice_party if sale.invoice_party else sale.party
|
|
if not freight_forwader:
|
|
raise UserError('Falta el agente aduanero en la venta! %s' % sale.number)
|
|
products = {}
|
|
for line in sale.lines:
|
|
key_ = (line.product, line.packing_uom)
|
|
if key_ not in products.keys():
|
|
products[key_] = {
|
|
'freight_forwader': freight_forwader.id,
|
|
'mawb': sale.mawb,
|
|
'hawb': sale.hawb,
|
|
'consignee': party.id,
|
|
'uom': line.packing_uom.id,
|
|
'pieces': line.packing_qty,
|
|
'fulles': round(line.boxes, 2),
|
|
'export_target_city': sale.export_target_city,
|
|
'export_route': sale.export_route,
|
|
'sale_lines': [('add', [line])],
|
|
}
|
|
else:
|
|
products[key_]['pieces'] += line.packing_qty
|
|
products[key_]['fulles'] += round(line.boxes, 2)
|
|
products[key_]['sale_lines'][0][1].append(line)
|
|
|
|
for pr in products.values():
|
|
pr['fulles'] = round(pr['fulles'], 2)
|
|
cls.write([charge], {'lines': [
|
|
('create', products.values())
|
|
]})
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('draft')
|
|
def draft(cls, records):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('cancel')
|
|
def cancel(cls, records):
|
|
pass
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('confirm')
|
|
def confirm(cls, records):
|
|
for rec in records:
|
|
rec.set_number()
|
|
|
|
def set_number(self):
|
|
"""
|
|
Fill the number field with the booking sequence
|
|
"""
|
|
pool = Pool()
|
|
Config = pool.get('farming.configuration')
|
|
config = Config.get_config()
|
|
|
|
if self.number or not config.charge_form_sequence:
|
|
return
|
|
number = config.charge_form_sequence.get()
|
|
self.write([self], {'number': number})
|
|
|
|
def get_value_uom(self, name=None):
|
|
res = []
|
|
for line in self.lines:
|
|
value = getattr(line, name)
|
|
if value:
|
|
res.append(value)
|
|
val = round(sum(res), 2)
|
|
return val
|
|
|
|
def get_sales(self, name=None):
|
|
pass
|
|
|
|
def get_total_amount(self, name=None):
|
|
pass
|
|
|
|
def subtotals(self):
|
|
subtotals = {}
|
|
|
|
class Sub():
|
|
pass
|
|
|
|
for line in self.lines:
|
|
lkey = (line.freight_forwader, line.consignee, line.hawb, line.mawb)
|
|
if lkey not in subtotals.keys():
|
|
sub = Sub()
|
|
sub.hawb = line.hawb
|
|
sub.mawb = line.mawb
|
|
sub.freight_forwader = line.freight_forwader
|
|
sub.consignee = line.consignee
|
|
sub.lines = [line]
|
|
sub.pieces = line.pieces
|
|
sub.fulles = line.fulles
|
|
subtotals[lkey] = sub
|
|
else:
|
|
subtotals[lkey].lines.append(line)
|
|
subtotals[lkey].pieces += line.pieces
|
|
subtotals[lkey].fulles += line.fulles
|
|
|
|
return subtotals.values()
|
|
|
|
@classmethod
|
|
def copy(cls, records, default=None):
|
|
default = {}
|
|
return super(ExportationCharge, cls).copy(ExportationCharge, default)
|
|
|
|
|
|
class ExportationChargeLine(ModelSQL, ModelView):
|
|
"Exportation Charge Line"
|
|
__name__ = 'exportation.charge.line'
|
|
charge = fields.Many2One('exportation.charge', 'Charge', select=True,
|
|
ondelete='CASCADE')
|
|
product = fields.Many2One('product.product', 'Product',
|
|
domain=[('salable', '=', 'True')])
|
|
uom = fields.Many2One('product.uom', 'UOM')
|
|
unit_digits = fields.Function(fields.Integer('Unit Digits'),
|
|
'get_unit_digits')
|
|
freight_forwader = fields.Many2One('party.party', 'Freight Forwader')
|
|
consignee = fields.Many2One('party.party', 'Consignee', required=True)
|
|
export_target_city = fields.Many2One('party.city_code', 'Target City',
|
|
required=True, select=True)
|
|
export_route = fields.Char('Route', select=True)
|
|
mawb = fields.Char('MAWB')
|
|
hawb = fields.Char('HAWB')
|
|
pieces = fields.Float('Pieces', digits=(6, 2))
|
|
units = fields.Float('Units', digits=(6, 2))
|
|
fulles = fields.Float('Fulles', digits=(6, 2))
|
|
sale_lines = fields.One2Many('sale.line', 'charge_line', 'Sale Lines',
|
|
states=STATES)
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(ExportationChargeLine, cls).__setup__()
|
|
cls._order.insert(0, ('consignee.name', 'ASC'))
|
|
|
|
@staticmethod
|
|
def default_unit_digits():
|
|
return 2
|
|
|
|
@fields.depends('product', 'uom', 'unit_digits')
|
|
def on_change_product(self):
|
|
if self.product:
|
|
self.uom = self.product.default_uom.id
|
|
self.unit_digits = self.product.default_uom.digits
|
|
self.unit_digits = 2
|
|
|
|
def get_uom(self, name):
|
|
return self.product.default_uom.id
|
|
|
|
def get_unit_digits(self, name):
|
|
return self.product.default_uom.digits
|
|
|
|
|
|
class ExportationChargeReport(CompanyReport):
|
|
__name__ = 'exportation.charge'
|
|
|
|
|
|
class LetterOfResponsability(CompanyReport):
|
|
__name__ = 'exportation.charge.letter_of_responsability'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, header, data):
|
|
context = super().get_context(records, header, data)
|
|
for rec in records:
|
|
_lines = {}
|
|
for line in rec.lines:
|
|
lkey = (line.hawb, line.consignee.id)
|
|
if lkey not in _lines.keys():
|
|
_lines[lkey] = {
|
|
'hawb': line.hawb,
|
|
'mawb': line.mawb,
|
|
'consignee': line.consignee.name,
|
|
'pieces': line.pieces,
|
|
'freight_forwader': line.freight_forwader.name,
|
|
'freight_forwader_id': line.freight_forwader.id_number,
|
|
'target_city': line.export_target_city.name,
|
|
'export_route': line.export_route,
|
|
}
|
|
else:
|
|
_lines[lkey]['pieces'] += line.pieces
|
|
rec._lines = _lines.values()
|
|
return context
|
|
|
|
|
|
class DispatchControlStart(ModelView):
|
|
'Dispatch Report Start'
|
|
__name__ = 'exportation.dispatch_control.start'
|
|
company = fields.Many2One('company.company', 'Company', required=True)
|
|
date_ = fields.Date("Date", required=True)
|
|
|
|
@staticmethod
|
|
def default_company():
|
|
return Transaction().context.get('company')
|
|
|
|
|
|
class DispatchControl(Wizard):
|
|
'Dispatch Control Report'
|
|
__name__ = 'exportation.dispatch_control'
|
|
start = StateView('exportation.dispatch_control.start',
|
|
'farming.dispatch_control_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Print', 'print_', 'tryton-ok', default=True),
|
|
])
|
|
print_ = StateReport('exportation.dispatch_control_report')
|
|
|
|
def do_print_(self, action):
|
|
data = {
|
|
'company': self.start.company.id,
|
|
'date': self.start.date_,
|
|
}
|
|
return action, data
|
|
|
|
def transition_print_(self):
|
|
return 'end'
|
|
|
|
|
|
class DispatchControlReport(Report):
|
|
__name__ = 'exportation.dispatch_control_report'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, header, data):
|
|
report_context = super().get_context(records, header, data)
|
|
pool = Pool()
|
|
Company = pool.get('company.company')
|
|
Charge = pool.get('exportation.charge')
|
|
charges = Charge.search([
|
|
('company', '=', data['company']),
|
|
('date', '=', data['date']),
|
|
], order=[('id', 'ASC')])
|
|
|
|
records = {}
|
|
for charge in charges:
|
|
for line in charge.lines:
|
|
lkey = (line.consignee.id, line.hawb, line.freight_forwader.id)
|
|
numbers = list(set([sl.sale.number for sl in line.sale_lines]))
|
|
if lkey not in records.keys():
|
|
records[lkey] = {
|
|
'customer': line.consignee.name,
|
|
'mawb': line.mawb,
|
|
'hawb': line.hawb,
|
|
'sales': numbers,
|
|
'freight_forwader': line.freight_forwader.name,
|
|
'pieces': [line.pieces],
|
|
'plate': charge.vehicle.plate,
|
|
'driver': charge.driver.name,
|
|
'driver_id': charge.driver.id_number,
|
|
'driver_phone': charge.driver.mobile,
|
|
}
|
|
else:
|
|
records[lkey]['pieces'].append(line.pieces)
|
|
records[lkey]['sales'] = list(set(records[lkey]['sales'] + numbers))
|
|
|
|
report_context['records'] = records.values()
|
|
report_context['company'] = Company(data['company'])
|
|
return report_context
|