trytonpsk-farming/quality.py

630 lines
21 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 datetime import date
from sql import Table
from trytond.model import Workflow, ModelView, ModelSQL, fields
from trytond.report import Report
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.modules.company import CompanyReport
from trytond.transaction import Transaction
from trytond.wizard import Wizard, StateReport, StateView, Button
from decimal import Decimal
class QualityAnalysis(ModelSQL, ModelView):
"Quality Analysis"
__name__ = "farming.quality.analysis"
origin = fields.Reference('Origin', selection='get_origin', select=True,
depends=['state'])
test = fields.Many2One('farming.quality.test', 'Test', required=True)
issue_date = fields.Date('Issue Date', required=True)
returned_qty = fields.Float('Returned Qty', required=True)
farm = fields.Char('Farm')
action = fields.Char('Action')
@staticmethod
def default_issue_date():
return date.today()
@classmethod
def _get_origin(cls):
'Return list of Model names for origin Reference'
return ['purchase.line', 'stock.move', 'sale.line']
@classmethod
def get_origin(cls):
Model = Pool().get('ir.model')
get_name = Model.get_name
models = cls._get_origin()
return [(None, '')] + [(m, get_name(m)) for m in models]
class QualityTest(ModelSQL, ModelView):
"Quality Test"
__name__ = "farming.quality.test"
name = fields.Char('Name', required=True)
kind = fields.Selection([
('quality', 'Quality'),
('phytosanitary', 'Phytosanitary')
], 'Kind', required=True)
kind_string = kind.translated('kind')
@classmethod
def __setup__(cls):
super(QualityTest, cls).__setup__()
cls._order.insert(0, ('name', 'ASC'))
class ProductSpecies(ModelSQL, ModelView):
"Product Species"
__name__ = "farming.product.species"
name = fields.Char('Name', required=True)
area = fields.Float('Area')
certificate = fields.Many2One('farming.quality.ica', 'ICA', required=True)
class ICACertificate(ModelSQL, ModelView):
"ICA Certificate"
__name__ = "farming.quality.ica"
number = fields.Char('Number', required=True)
issue_date = fields.Date('Issue Date', required=True)
expiration_date = fields.Date('Expiration Date', required=True)
party = fields.Many2One('party.party', 'Party', required=True)
species = fields.One2Many('farming.product.species', 'certificate',
'Species')
farm = fields.Char('Farm', required=True)
location = fields.Char('Location', required=True)
product = fields.Many2One('product.template', 'Product', required=True,
domain=[('type', '=', 'goods')])
code = fields.Char('Code')
@classmethod
def __setup__(cls):
super(ICACertificate, cls).__setup__()
cls._order.insert(0, ('expiration_date', 'ASC'))
@classmethod
def search_rec_name(cls, name, clause):
if clause[1].startswith('!') or clause[1].startswith('not '):
bool_op = 'AND'
else:
bool_op = 'OR'
return [
bool_op,
('number',) + tuple(clause[1:]),
('farm',) + tuple(clause[1:]),
('party.name',) + tuple(clause[1:]),
]
def get_rec_name(self, name):
return '[' + self.number + '] ' + self.farm
class Phytosanitary(ModelSQL, ModelView):
"Phytosanitary"
__name__ = "farming.phyto"
_rec_name = 'number'
number = fields.Char('Number', required=True, select=True)
ica = fields.Many2One('farming.quality.ica', 'ICA Certificate',
required=True, select=True)
issue_date = fields.Date('Issue Date', required=True)
stock_lots = fields.One2Many('stock.lot', 'phyto', 'Stock Lots')
balance = fields.Function(fields.Integer('Balance'), 'get_balance')
state = fields.Selection([
('active', 'Active'),
('finished', 'Finished'),
], 'State', states={'readonly': True}, select=True)
@classmethod
def __setup__(cls):
super(Phytosanitary, cls).__setup__()
cls._buttons.update({
'refresh': {
'depends': ['state'],
}
})
@staticmethod
def default_issue_date():
return date.today()
@staticmethod
def default_state():
return 'active'
def get_balance(self, name=None):
res = [stock.balance for stock in self.stock_lots if stock.balance]
return Decimal(sum(res))
@classmethod
def search_rec_name(cls, name, clause):
if clause[1].startswith('!') or clause[1].startswith('not '):
bool_op = 'AND'
else:
bool_op = 'OR'
return [
bool_op,
('number',) + tuple(clause[1:]),
('ica.party.name',) + tuple(clause[1:]),
]
def get_rec_name(self, name):
return '[' + self.number + '] ' + self.ica.party.name
@classmethod
@ModelView.button
def refresh(cls, records):
for rec in records:
if rec.balance == 0:
rec.state = 'finished'
else:
rec.state = 'active'
rec.save()
class PhytoStock(ModelSQL, ModelView):
"Phytosanitary Stock"
__name__ = "farming.phyto.stock"
phyto = fields.Many2One('farming.phyto', 'Phyto', required=True,
ondelete='CASCADE')
lot = fields.Many2One('stock.lot', 'Lot')
product = fields.Many2One('product.product', 'Product', required=True,
domain=[('type', '=', 'goods')])
location = fields.Char('Location')
balance = fields.Integer('Balance', states={'readonly': True})
moves = fields.One2Many('farming.phyto.stock.move', 'stock', 'Moves')
state = fields.Selection([
('active', 'Active'),
('finished', 'Finished'),
], 'State', states={'readonly': True}, select=True)
@staticmethod
def default_balance():
return 0
@staticmethod
def default_state():
return 'active'
@fields.depends('balance', 'moves', 'state')
def on_change_moves(self, name=None):
res = []
for mo in self.moves:
res.append(mo.move_in - mo.move_out)
val = sum(res)
self.balance = val
if len(self.moves) > 1 and val == 0:
self.state = 'finished'
else:
self.state = 'active'
class PhytoStockMove(ModelSQL, ModelView):
"Phytosanitary Stock Move"
__name__ = "farming.phyto.stock.move"
stock = fields.Many2One('farming.phyto.stock', 'Stock Phyto',
required=True, ondelete='CASCADE')
date = fields.Date('Date', required=True)
move_in = fields.Integer('Move In', required=True)
move_out = fields.Integer('Move Out', required=True)
move = fields.Many2One('stock.move', 'Move', required=False)
origin = fields.Reference('Origin', selection='get_origin', select=True)
@staticmethod
def default_move_in():
return 0
@staticmethod
def default_move_out():
return 0
@classmethod
def delete(cls, records):
stocks = [rec.stock for rec in records]
super(PhytoStockMove, cls).delete(records)
for sto in stocks:
sto.on_change_moves()
sto.save()
@classmethod
def _get_origin(cls):
'Return list of Model names for origin Reference'
return ['stock.move', 'exportation.phyto.line']
@classmethod
def get_origin(cls):
Model = Pool().get('ir.model')
get_name = Model.get_name
models = cls._get_origin()
return [(None, '')] + [(m, get_name(m)) for m in models]
class ExportationPhyto(Workflow, ModelSQL, ModelView):
"Exportation Phytosanitary"
__name__ = 'exportation.phyto'
STATES = {
'readonly': (Eval('state') != 'draft'),
}
date = fields.Date('Date', required=True, states=STATES)
number = fields.Char('Number', readonly=True)
company = fields.Many2One('company.company', 'Company', required=True,
states=STATES)
customer = fields.Many2One('party.party', 'Customer', select=True,
states=STATES)
in_port = fields.Many2One('exportation.port', 'In Port', select=True)
out_port = fields.Many2One('exportation.port', 'Out Port', select=True)
freight_forwader = fields.Many2One('party.party', 'Freight Forwader',
required=True, select=True)
specie = fields.Many2One('product.template', 'Specie', required=True,
states=STATES, domain=[
('type', '=', 'goods'),
('farming', '=', True),
])
lines = fields.One2Many('exportation.phyto.line', 'phyto', 'Lines',
states=STATES)
phyto_moves = fields.One2Many("stock.lot.phyto.move", 'exportation_phyto',"Move Phyto")
dispatched_date = fields.Date('Dispatched Date', required=True,
states=STATES)
technical_prof = fields.Many2One('party.party',
'Technical Professional', states=STATES)
state = fields.Selection([
('draft', 'Draft'),
('confirm', 'Confirm'),
('cancel', 'Canceled'),
], 'State', readonly=True, select=True)
expire_start_date = fields.Date('Expire Start Date', required=True,
states=STATES)
expire_end_date = fields.Date('Expire End Date', required=True,
states=STATES)
total_quantity = fields.Function(fields.Numeric('Total Quantity',
digits=(16, 2)), 'get_total_quantity')
@classmethod
def __setup__(cls):
super(ExportationPhyto, cls).__setup__()
cls._order.insert(0, ('date', 'DESC'))
cls._transitions |= set((
('draft', 'confirm'),
('confirm', 'draft'),
('cancel', 'draft'),
('draft', 'cancel'),
))
cls._buttons.update({
'cancel': {
'invisible': Eval('state') != 'draft'
},
'draft': {
'invisible': Eval('state') == 'draft',
},
'confirm': {
'invisible': Eval('state') != 'draft',
},
'select_phytos': {
'invisible': (Eval('state') != 'draft') | Eval('lines', []),
},
})
@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
@classmethod
@ModelView.button
def select_phytos(cls, records):
cursor = Transaction().connection.cursor()
pool = Pool()
Production = pool.get('production')
ExLine = pool.get('exportation.phyto.line')
MovePhyto = pool.get('stock.lot.phyto.move')
move_phyto_table = Table('stock_lot_phyto_move')
# productions = []
id_export_pytho = None
Lot = pool.get('stock.lot')
for ex_phyto in records:
moves_phyto = []
moves = []
lots = set()
export_date = ex_phyto.dispatched_date
productions = Production.search([
('customer', '=', ex_phyto.customer.id),
('state', 'in', ('done', 'running', 'assigned')),
('delivery_date', '=', ex_phyto.dispatched_date),
])
id_export_pytho = ex_phyto.id
for pcc in productions:
for input in pcc.inputs:
if input.product.template.farming:
moves.append(input)
for move in moves:
query = move_phyto_table.select(
move_phyto_table.id,
where=move_phyto_table.origin == str(move))
cursor.execute(*query)
result = cursor.fetchall()
if result:
for move_phyto_id in result:
m_phyto = MovePhyto.search([
('id', '=', move_phyto_id[0])
])
moves_phyto.append(m_phyto[0])
for move_phyto in moves_phyto:
if move_phyto.move_in > 0 :
lots.add(move_phyto.lot)
ex_line = {
'phyto': id_export_pytho,
'ica_register': move_phyto.lot.phyto.ica.id,
}
line, = ExLine.create([ex_line])
new_phyto_move = {
'lot': move_phyto.lot,
'date_move': export_date,
'move_in': 0,
'move_out': move_phyto.move_in,
'origin': str(line)
}
new_id_phyto, = MovePhyto.create([new_phyto_move])
line.phyto_moves = new_id_phyto.id
line.quantity = int(move_phyto.move_in)
line.save()
if lots:
Lot.recompute_balance(lots)
@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.phyto_export_sequence:
return
number = config.phyto_export_sequence.get()
self.write([self], {'number': number})
def get_total_quantity(self, name=None):
return sum(line.quantity for line in self.lines)
class ExportationPhytoLine(ModelSQL, ModelView):
"Exportation Phyto Line"
__name__ = 'exportation.phyto.line'
ica_register = fields.Many2One('farming.quality.ica', 'ICA Register',
required=True)
phyto = fields.Many2One('exportation.phyto', 'Phyto')
quantity = fields.Integer('Quantity')
phyto_moves = fields.Many2One('stock.lot.phyto.move', 'origin', 'Phyto Move',
domain=[('lot.phyto.ica', '=', Eval('ica_register'))], ondelete="CASCADE")
manual = fields.Boolean('Manual')
@classmethod
def delete(cls, records):
StockMove = Pool().get('stock.lot.phyto.move')
for rec in records:
if rec.phyto_moves:
StockMove.delete([rec.phyto_moves])
super(ExportationPhytoLine, cls).delete(records)
class ExportationPhytosanitaryReport(CompanyReport):
__name__ = 'exportation.phyto'
class PhytoMovesStart(ModelView):
'Phyto Moves Start'
__name__ = 'farming.phyto_moves.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 PhytoMoves(Wizard):
'Phyto Moves'
__name__ = 'farming.phyto_moves'
start = StateView(
'farming.phyto_moves.start',
'farming.phyto_moves_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('farming.phyto_moves.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 PhytoMovesReport(Report):
'Phyto Moves Report'
__name__ = 'farming.phyto_moves.report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
company_id = Transaction().context.get('company')
Company = pool.get('company.company')
Phyto = pool.get('farming.phyto')
phytos = Phyto.search([
('issue_date', '=', data['date']),
], order=[('issue_date', 'ASC')]
)
records_ = []
_append = records_.append
for ph in phytos:
for st in ph.stock_lots:
balance = st.quantity_purchase
rec_in = {
'doc': ph.number,
'supplier': ph.ica.farm,
'date': ph.issue_date,
'reference': ph.ica.product.name,
'in': balance,
'out': 0,
'dev': '',
'balance': balance,
'customer': '',
}
_append(rec_in)
for mv in st.phyto_move:
if mv.move_out > 0:
balance -= mv.move_out
customer = ''
doc = ''
date_ = ''
if mv.origin and mv.origin.__name__ == 'exportation.phyto.line':
phyto = mv.origin.phyto
customer = phyto.customer.name
doc = phyto.number
date_ = phyto.dispatched_date
rec_out = {
'doc': doc,
'supplier': '',
'date': date_,
'reference': '',
'in': '',
'out': mv.move_out,
'dev': '',
'balance': balance,
'customer': customer,
}
_append(rec_out)
report_context['records'] = records_
report_context['company'] = Company(company_id)
return report_context
class PurchaseMonitoringStart(ModelView):
'Purchase Monitoring Start'
__name__ = 'farming.purchase_monitoring.start'
company = fields.Many2One('company.company', 'Company', required=True)
start_date = fields.Date("Start Date", required=True)
end_date = fields.Date("End Date", required=True)
returned_included = fields.Boolean("Return Included")
@staticmethod
def default_company():
return Transaction().context.get('company')
class PurchaseMonitoring(Wizard):
'Purchase Monitoring'
__name__ = 'farming.purchase_monitoring'
start = StateView(
'farming.purchase_monitoring.start',
'farming.purchase_monitoring_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('farming.purchase_monitoring.report')
def do_print_(self, action):
data = {
'company': self.start.company.id,
'start_date': self.start.start_date,
'end_date': self.start.end_date,
'returned_included': self.start.returned_included,
}
return action, data
def transition_print_(self):
return 'end'
class PurchaseMonitoringReport(Report):
'Purchase Monitoring Report'
__name__ = 'farming.purchase_monitoring.report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
company_id = Transaction().context.get('company')
Company = pool.get('company.company')
Purchase = pool.get('purchase.purchase')
purchases = Purchase.search([
('delivery_date', '>=', data['start_date']),
('delivery_date', '<=', data['end_date']),
('ica_certicate', '!=', None),
('state', 'in', ['confirmed', 'processing', 'done']),
], order=[('delivery_date', 'ASC')]
)
records_ = []
_append = records_.append
for purc in purchases:
phyto = ''
for ship in purc.shipments:
if ship.phyto:
phyto = ship.phyto.number
if not phyto and not data['returned_included']:
continue
for line in purc.lines:
rec_in = {
'date': purc.delivery_date,
'supplier': purc.ica_certicate.farm,
'doc': phyto,
'reference': line.product.template.name,
'quantity': line.original_qty,
'returned_qty': line.returned_qty,
'qty_checked': line.qty_checked,
'analysis': [],
'action': '',
}
_analysis = []
for qa in line.quality_analysis:
_analysis.append({
'returned_qty': qa.returned_qty,
'test_kind': qa.test.kind_string,
'test': qa.test.name,
'action': qa.action,
})
if _analysis:
rec_in['analysis'] = _analysis
else:
rec_in['action'] = 'NO PLAGA'
_append(rec_in)
report_context['records'] = records_
report_context['company'] = Company(company_id)
return report_context