trytonpsk-farming/crop.py

1427 lines
50 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 decimal import Decimal
from datetime import date, datetime, timedelta
import copy
from trytond.model import Workflow, ModelView, ModelSQL, fields
from trytond.pyson import Eval, If, Bool, In, Get
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.exceptions import UserError
from trytond.report import Report
from trytond.wizard import Wizard, StateReport, StateView, Button
from trytond.modules.company import CompanyReport
STATES = {
'readonly': (Eval('state') != 'draft'),
}
MAX_WEEKS = 18
class FarmingStage(ModelSQL, ModelView):
"Farming Stage"
__name__ = "farming.stage"
name = fields.Char('Name', required=True)
class FarmingVarietyCycle(ModelSQL, ModelView):
"Farming Variety Cycle"
__name__ = "farming.variety.cycle"
variety = fields.Many2One('farming.variety', 'Variety')
lot = fields.Many2One('farming.location.lot', 'Lot')
sequence = fields.Integer('Sequence', required=True)
stage = fields.Many2One('farming.stage', 'Stage', required=True)
start_time = fields.Integer('Start Time', help='Harvest week',
required=True)
production_rate = fields.Float('Production Rate', digits=(3, 2))
class FarmingVariety(ModelSQL, ModelView):
'Farming Variety'
__name__ = 'farming.variety'
product = fields.Many2One('product.product', 'Product', required=True)
production_rate = fields.Float('Production Rate', digits=(16, 2))
production_uom = fields.Many2One('product.uom', 'UoM')
standard_cycle = fields.Integer('Standard Cycle', help='In weeks')
activities = fields.One2Many('farming.variety.activity', 'variety',
'Activities')
location = fields.Many2One('farming.location', 'Location')
cycles = fields.One2Many('farming.variety.cycle', 'variety', 'Cycles')
def get_rec_name(self, name=None):
if self.location:
return self.product.template.name + '\u2014' + self.location.name
else:
return self.product.template.name
class FarmingActivitySupply(ModelSQL, ModelView):
'Farming Activity Supply'
__name__ = 'farming.variety.activity.supply'
activity = fields.Many2One('farming.variety.activity', 'Activity')
product = fields.Many2One("product.product", "Product", required=True)
quantity = fields.Float('Quantity', required=True)
unit = fields.Many2One('product.uom', 'Unit',
domain=[
If(Bool(Eval('product_uom_category')),
('category', '=', Eval('product_uom_category')),
('category', '!=', -1)),
],
depends=['product_uom_category'])
product_uom_category = fields.Function(
fields.Many2One('product.uom.category', 'Product Uom Category'),
'on_change_with_product_uom_category')
@fields.depends('product')
def on_change_with_unit(self, name=None):
if self.product:
unit = self.product.default_uom.id
return unit
@fields.depends('product')
def on_change_with_product_uom_category(self, name=None):
if self.product:
return self.product.default_uom_category.id
else:
return None
class FarmingVarietyActivity(ModelSQL, ModelView):
'Farming Variety Activity'
__name__ = 'farming.variety.activity'
variety = fields.Many2One('farming.variety', 'Variety')
sequence = fields.Integer('Sequence', required=True)
kind = fields.Many2One('farming.activity.kind', 'Kind', required=True)
work_time = fields.Numeric('Work Time', required=True)
cost_per_hour = fields.Numeric('Cost Per Hour', digits=(16, 2))
uom = fields.Many2One('product.uom', 'Unit')
uom_time = fields.Many2One('product.uom', 'Unit of Time')
supplies = fields.One2Many('farming.variety.activity.supply', 'activity',
'Supply')
time_of_realization = fields.Integer('Time of Realization', required=True,
help='In weeks')
@fields.depends('kind', 'uom')
def on_change_kind(self):
if self.kind:
self.uom = self.kind.uom.id
class CropStage(ModelSQL, ModelView):
"Crop Stage"
__name__ = "farming.crop.stage"
crop = fields.Many2One('farming.crop', 'Crop', required=True)
activity = fields.Many2One('farming.crop.activity', 'Activity',
states={"invisible": False})
stage = fields.Many2One('farming.stage', 'Stage', required=True)
week = fields.Float('Week', required=True)
effective_date = fields.Date('Effective Date')
analytic_account = fields.Many2One('analytic_account.account',
'Analytic Account')
activities = fields.One2Many('farming.crop.activity', 'stage', 'Activity')
production_rate = fields.Float('Production Rate', digits=(3, 2))
quantity_planned = fields.Function(fields.Float('Qty. Planned',
digits=(6, 2)), 'get_quantity_planned')
quantity_produced = fields.Float('Qty. Produced', digits=(6, 2))
def get_quantity_planned(self, name=None):
res = 0
if self.production_rate and self.crop.quantity_planned:
res = (self.production_rate / 100) * self.crop.quantity_planned
return round(res, 2)
class Kind(ModelSQL, ModelView):
"Activity Kind"
__name__ = "farming.activity.kind"
name = fields.Char('Name', required=True)
activity_time = fields.Float('Act. Time', required=True)
uom = fields.Many2One('product.uom', 'Unit', required=True,
help="Unit of time, according to activity time")
class FarmingCrop(Workflow, ModelSQL, ModelView):
'Farming Crop'
__name__ = 'farming.crop'
_rec_name = 'number'
number = fields.Char('Number', readonly=True)
variety = fields.Many2One('farming.variety', 'Variety', required=True,
states=STATES)
location = fields.Many2One('farming.location', 'Location', required=True,
states=STATES)
company = fields.Many2One('company.company', 'Company', required=True,
states=STATES, domain=[('id', If(In('company',
Eval('context', {})), '=', '!='), Get(Eval('context', {}),
'company', 0)), ])
start_date = fields.Date('Start Date', states=STATES, depends=['state'])
end_date = fields.Date('End Date', states=STATES, depends=['state'])
field_size = fields.Float('Field Size', states=STATES, select=True,
help='In hectares')
total_plants = fields.Integer('Total Plants')
quantity_planned = fields.Function(fields.Float('Quantity Planned'),
'get_quantity_planned')
quantity_produced = fields.Float('Quantity Produced', states=STATES)
production_uom = fields.Many2One('product.uom', 'Production UoM', states=STATES)
lots = fields.One2Many('farming.crop.lot', 'crop', 'Lots', states=STATES)
stages = fields.One2Many('farming.crop.stage', 'crop', 'Stages',
states=STATES)
activities = fields.One2Many('farming.crop.activity', 'crop', 'Activities')
seed = fields.Many2One('farming.seed', 'Seed', required=True)
analytic_account = fields.Many2One('analytic_account.account', 'Analytic Account')
production_rate = fields.Float('Production Rate')
notes = fields.Text('Notes', states=STATES)
state = fields.Selection([
('draft', 'Draft'),
('production', 'Production'),
('finished', 'Finished'),
('cancelled', 'Cancelled'),
], 'State', readonly=True, required=True)
# moves for supplies
supplies = fields.One2Many('farming.crop.activity.supply', 'crop', 'Supply')
# Financial indicators
production_time = fields.Function(fields.Integer('Production Time'),
'get_production_time')
production_cost = fields.Function(fields.Numeric('Production Cost'),
'get_production_cost')
performance = fields.Function(fields.Numeric('Performance'),
'get_performance')
gross_profit_rate = fields.Function(fields.Float('Gross Profit Rate'),
'get_gross_profit_rate')
gross_profit = fields.Function(fields.Numeric('Gross Profit'),
'get_gross_profit')
@classmethod
def __setup__(cls):
super(FarmingCrop, cls).__setup__()
cls._order.insert(0, ('create_date', 'DESC'))
cls._order.insert(1, ('id', 'DESC'))
cls._transitions |= set((
('draft', 'production'),
('production', 'draft'),
('production', 'finished'),
('production', 'cancelled'),
))
cls._buttons.update({
'draft': {
'invisible': Eval('state').in_(['draft', 'finished'])
},
'cancel': {
'invisible': Eval('state') == 'finished',
},
'load_activities': {
'invisible': Eval('lines', []) | (Eval('state') != 'draft'),
'depends': ['state'],
},
'load_lots': {
'invisible': Eval('lines', []) | (Eval('state') != 'draft'),
'depends': ['state'],
},
'production': {
'invisible': Eval('state').in_(['cancelled', 'finished']),
},
'finished': {
'invisible': Eval('state') != 'production',
},
})
@staticmethod
def default_start_date():
return date.today()
@staticmethod
def default_company():
return Transaction().context.get('company') or False
@staticmethod
def default_state():
return 'draft'
@classmethod
@ModelView.button
@Workflow.transition('finished')
def finished(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('cancelled')
def cancel(cls, services):
pass
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('production')
def production(cls, records):
for record in records:
record.set_number()
@classmethod
def clear_bed_lines(cls, crops):
Line = Pool().get('farming.crop.lot.bed')
lines_to_delete = []
for crop in crops:
for lot in crop.lots:
for bed in lot.beds:
lines_to_delete.append(bed)
Line.delete(lines_to_delete)
@classmethod
def create_beds_lines(cls, crops):
Line = Pool().get('farming.crop.lot.bed')
lines = []
for crop in crops:
for lot in crop.lots:
for bed in lot.lot.beds:
line = Line(
lot=lot.id,
bed=bed.id,
quantity_plants=bed.quantity,
)
lines.append(line)
Line.save(lines)
@classmethod
def create_lots_lines(cls, crops):
Line = Pool().get('farming.crop.lot')
Lot = Pool().get('farming.location.lot')
lines = []
cls.clear_bed_lines(crops)
cls.clear_lots_lines(crops)
for crop in crops:
lots = Lot.search([('location', '=', crop.location)])
for lot in lots:
line = Line(
crop=crop.id,
lot=lot.id,
cycle=0,
beds=[],
activities=[],
total_plants=lot.quantity
)
lines.append(line)
Line.save(lines)
cls.create_beds_lines(crops)
@classmethod
def clear_lots_lines(cls, crops):
Line = Pool().get('farming.crop.lot')
lines_to_delete = []
for crop in crops:
for lot in crop.lots:
lines_to_delete.append(lot)
Line.delete(lines_to_delete)
@classmethod
def create_stages_lines(cls, crops):
Line = Pool().get('farming.crop.stage')
lines = []
cls.clear_stages_lines(crops)
for crop in crops:
for stage in crop.variety.cycles:
effective_date = crop.start_date + timedelta(weeks=stage.start_time)
line = Line(
crop=crop.id,
stage=stage.stage.id,
production_rate=stage.production_rate,
week=stage.start_time,
effective_date=effective_date,
)
lines.append(line)
Line.save(lines)
@classmethod
def clear_stages_lines(cls, crops):
Line = Pool().get('farming.crop.stage')
lines_to_delete = []
for crop in crops:
for stage in crop.stages:
lines_to_delete.append(stage)
Line.delete(lines_to_delete)
@classmethod
def clear_activities_lines(cls, crops):
Line = Pool().get('farming.crop.activity')
lines_to_delete = []
for crop in crops:
for activity in crop.activities:
lines_to_delete.append(activity)
Line.delete(lines_to_delete)
@classmethod
def create_activities_lines(cls, crops):
Line = Pool().get('farming.crop.activity')
lines = []
for crop in crops:
for act in crop.variety.activities:
_date = crop.start_date + timedelta(
weeks=act.time_of_realization)
work_time = act.work_time * crop.total_plants
if act.uom_time.symbol == 'min':
_cost = round((act.cost_per_hour / 60) * work_time, 2)
elif act.uom_time.symbol == 'h':
_cost = round(act.cost_per_hour * work_time, 2)
elif act.uom_time.symbol == 's':
_cost = round((act.cost_per_hour / 3600) * work_time, 2)
else:
_cost = round(act.cost_per_hour * work_time, 2)
line = Line(
crop=crop.id,
sequence=act.sequence,
kind=act.kind.id,
work_time=work_time,
workforce_cost=_cost,
uom=act.uom.id,
uom_time=act.uom_time.id,
week=act.time_of_realization,
planned_date=_date,
effective_date=_date,
)
lines.append(line)
Line.save(lines)
cls.create_activity_supplies_lines(crops)
@classmethod
def create_activity_supplies_lines(cls, crops):
Line = Pool().get('farming.crop.activity.supply')
lines = []
for crop in crops:
for act_crop, act_variety in zip(crop.activities, crop.variety.activities):
for supply in act_variety.supplies:
line = Line(
activity=act_crop.id,
product=supply.product.id,
unit=supply.unit,
quantity=supply.quantity * crop.total_plants,
cost_planned=supply.product.cost_price,
cost_real=supply.product.cost_price
)
lines.append(line)
Line.save(lines)
@classmethod
def clear_supplies_lines(cls, crops):
Line = Pool().get('farming.crop.lot.bed')
lines_to_delete = []
for crop in crops:
for activity in crop.activities:
for supply in activity.supplies:
lines_to_delete.append(supply)
Line.delete(lines_to_delete)
@classmethod
def get_total_plants(cls, crops):
Line = Pool().get('farming.crop')
lines = []
for crop in crops:
quantity = 0
for lot in crop.lots:
quantity += lot.total_plants
crop.total_plants = quantity
lines.append(crop)
Line.save(lines)
@classmethod
@ModelView.button
def load_activities(cls, crops):
cls.clear_stages_lines(crops)
cls.create_stages_lines(crops)
cls.clear_supplies_lines(crops)
cls.clear_activities_lines(crops)
cls.create_activities_lines(crops)
@classmethod
@ModelView.button
def load_lots(cls, crops):
cls.create_lots_lines(crops)
cls.get_total_plants(crops)
@classmethod
def supplies_shipment(cls, records):
pool = Pool()
SupplyMoves = pool.get('crop.supply.moves')
supplyMoves = []
for record in records:
for activity in record.activities:
for supply in activity.supplies:
supplyMove = SupplyMoves(
crop=record.id,
product=supply.product,
unit=supply.unit,
quantity=supply.quantity,
)
supplyMoves.append(supplyMove)
SupplyMoves.save(supplyMoves)
def set_number(self):
Config = Pool().get('farming.configuration')
config = Config.get_config()
if not config.farming_production_sequence:
raise UserError('missing_sequence_farming_production')
number = config.farming_production_sequence.get()
self.write([self], {'number': number})
def get_quantity_planned(self, name=None):
res = []
_append = res.append
for lot in self.lots:
if self.production_rate and lot.total_plants:
_append(self.production_rate * lot.total_plants)
return round(sum(res), 2)
def get_production_time(self, name=None):
res = None
if self.start_date:
today = date.today()
if self.end_date:
res = (self.end_date - self.start_date).days
else:
res = (today - self.start_date).days
return res
def get_production_cost(self, name):
res = 0
return res
def get_performance(self, name):
res = 0
return res
def get_gross_profit_rate(self, name):
res = 0
return res
def get_gross_profit(self, name):
res = 0
return res
@fields.depends('stages')
def on_change_stages(self):
if self.stages:
res = []
for stage in self.stages:
res.append(stage.quantity_produced)
self.quantity_produced = sum(res)
@fields.depends('variety', 'production_uom', 'production_rate')
def on_change_variety(self):
if self.variety and self.variety.production_uom:
self.production_uom = self.variety.production_uom.id
self.production_rate = self.variety.production_rate
if self.variety.standard_cycle:
self.end_date = date.today() + timedelta(
weeks=self.variety.standard_cycle)
@fields.depends('start_date', 'variety')
def on_change_start_date(self):
if self.variety:
self.end_date = self.start_date + timedelta(
weeks=self.variety.standard_cycle)
@fields.depends('lots')
def on_change_with_total_plants(self, name=None):
quantity = 0
for lot in self.lots:
if lot.total_plants:
quantity += lot.total_plants
return quantity
class SupplyMoves(ModelSQL, ModelView):
'Supply moves'
__name__ = 'crop.supply.moves'
crop = fields.Many2One('farming.crop', 'Crop', ondelete='CASCADE',
required=True)
product = fields.Many2One("product.product", "Product", required=True,
select=True)
unit = fields.Many2One('product.uom', 'Unit')
quantity = fields.Float('Quantity', required=True)
origin = fields.Reference('Origin', selection='get_origin', select=True)
@classmethod
def _get_origin(cls):
'Return list of Model names for origin Reference'
return ['stock.move']
@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 FarmingCropLot(ModelSQL, ModelView):
'Farming Crop Lot'
__name__ = 'farming.crop.lot'
crop = fields.Many2One('farming.crop', 'Crop', ondelete='CASCADE',
required=True)
lot = fields.Many2One('farming.location.lot', 'Lot', required=True)
cycle = fields.Float('Cycle', digits=(16, 2), help="In weeks")
beds = fields.One2Many('farming.crop.lot.bed', 'lot', 'Beds')
activities = fields.One2Many('farming.activity', 'lot', 'Activities')
analytic_account = fields.Many2One('analytic_account.account',
'Analytic Account')
total_plants = fields.Integer('Total Plants', states={
'readonly': Bool(Eval('beds'))}, depends=['beds'])
@fields.depends('beds')
def on_change_with_total_plants(self, name=None):
quantity = 0
if self.beds:
for bed in self.beds:
if bed.quantity_plants:
quantity += bed.quantity_plants
return quantity
class FarmingCropLotBed(ModelSQL, ModelView):
'Crop Lot Bed'
__name__ = 'farming.crop.lot.bed'
lot = fields.Many2One('farming.crop.lot', 'Lot', ondelete='CASCADE',
required=True)
bed = fields.Many2One('farming.location.lot.bed', 'Bed',
ondelete='CASCADE', required=True)
quantity_plants = fields.Integer('Quantity Plants')
class CropActivity(Workflow, ModelSQL, ModelView):
'Crop Activity'
__name__ = 'farming.crop.activity'
crop = fields.Many2One('farming.crop', 'Crop', ondelete='CASCADE',
required=True)
stage = fields.Many2One('farming.crop.stage', 'Stage')
sequence = fields.Char('Sequence', states=STATES)
kind = fields.Many2One('farming.activity.kind', 'Kind')
work_time = fields.Numeric('Work Time')
workforce_cost = fields.Numeric('Workforce Cost', digits=(16, 2))
uom = fields.Many2One('product.uom', 'Unit')
uom_time = fields.Many2One('product.uom', 'UoM of Time')
supplies = fields.One2Many('farming.crop.activity.supply', 'activity',
'Supply')
week = fields.Integer('Week')
planned_date = fields.Date('Planned Date')
effective_date = fields.Date('Effective Date')
state = fields.Selection([
('draft', 'Draft'),
('production', 'Production'),
('finished', 'Finished'),
('cancelled', 'Cancelled'),
], 'State', readonly=True, required=True)
@classmethod
def generate_shipment(cls, records):
pool = Pool()
company = Transaction().context.get('company')
Supply = pool.get('farming.crop.activity.supply')
supplies = []
for record in records:
location = record.crop.location.warehouse
for supplyRec in record.supplies:
supply = Supply(
crop=record.crop,
product=supplyRec.product,
unit=supplyRec.unit,
quantity=supplyRec.quantity,
)
move = record._move(
record,
location.output_location,
location.production_location,
company,
supplyRec.product,
supplyRec.unit,
supplyRec.quantity)
supplies.append(supply)
Supply.save(supplies)
@classmethod
@ModelView.button
def generate_shipment_button(cls, records):
pass
def _move(self, activity, from_location, to_location, company, product,
uom, quantity):
Move = Pool().get('stock.move')
move = Move(
product=product,
uom=uom,
quantity=quantity,
from_location=from_location,
to_location=to_location,
company=company,
origin=str(activity),
state='draft'
)
Move.save([move])
return move
@classmethod
def create_supply_moves(cls, location):
# pool = Pool()
# Move = pool.get('stock.move')
# Location = pool.get('stock.location')
# Date = pool.get('ir.date')
# Shipment = pool.get('stock.shipment.internal')
# moves = []
# for supply in activity.supplies:
# print(supply.unit, 'esto no es correcto')
# move = Move(
# quantity = supply.quantity,
# uom = supply.unit.id,
# product = supply.product.id,
# from_location =
# to_location =
# state = 'draft',
# company = Transaction().context.get('company'),
# unit_price = supply.product.cost_price,
# planned_date = Date.today(),
# #origin = ['production']
# )
# moves.append(move)
# shipment.moves = moves
# Shipment.save([shipment])
# #Move.save(moves)
# #BUG(ERROR: SHIPMENT INTERRUPTS STATUS, NOT GENERATED)
pass
@classmethod
def __setup__(cls):
super(CropActivity, cls).__setup__()
cls._order.insert(0, ('planned_date', 'ASC'))
cls._transitions |= set((
('draft', 'production'),
('production', 'draft'),
('production', 'finished'),
('production', 'cancelled'),
('finished', 'production'), # only developings
))
cls._buttons.update({
'draft': {
'invisible': Eval('state').in_(['draft', 'finished'])
},
'cancel': {
'invisible': Eval('state') == 'finished',
},
'load_configuration': {
'invisible': Eval('lines', []) | (Eval('state') != 'draft'),
'depends': ['state'],
},
'production': {},
'finished': {
'invisible': Eval('state') != 'production',
},
})
@staticmethod
def default_state():
return 'draft'
@classmethod
@ModelView.button
@Workflow.transition('finished')
def finished(cls, records):
pool = Pool()
AccountMove = pool.get('account.move')
Journal = pool.get('account.journal')
journal = Journal.search([('type', '=', 'expense')])
for record in records:
account_move = {
'company': Transaction().context.get('company'),
'journal': journal[0].id,
'date': date.today(),
'origin': str(record.crop),
'lines': None,
}
account_move_lines = []
for supply in record.supplies:
product = supply.product
account = product.account_category
product_name = product.name
lineC = {
'debit': Decimal(0),
'credit': product.cost_price,
'account': account.account_stock.id,
'description': product_name,
}
lineD = {
'debit': product.cost_price,
'credit': Decimal(0),
'account': account.account_expense.id,
'description': product_name,
'analytic_lines': [('create', [{
'debit': product.cost_price,
'credit': Decimal(0),
'account': supply.analytic_account,
'date': date.today()
}])]
}
account_move_lines.append(lineC)
account_move_lines.append(lineD)
account_move['lines'] = [('create', account_move_lines)]
AccountMove.create([account_move])
@classmethod
@ModelView.button
@Workflow.transition('cancelled')
def cancel(cls, services):
pass
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('production')
def production(cls, records):
for record in records:
#cls.create_shipment(record)
cls.generate_shipment([record])
record.set_number()
def set_number(self):
#Config = Pool().get('farming.configuration')
#config = Config.get_config()
#if not config.farming_production_sequence:
# raise UserError('missing_sequence_farming_production')
pass
class CropActivitySupply(ModelSQL, ModelView):
'Crop Activity Supply'
__name__ = 'farming.crop.activity.supply'
crop = fields.Many2One('farming.crop', 'Crop')
activity = fields.Many2One('farming.crop.activity', 'Activity',
states={"invisible": False})
product = fields.Many2One("product.product", "Product", required=True,
select=True)
unit = fields.Many2One('product.uom', 'Unit')
quantity = fields.Float('Quantity', required=True)
analytic_account = fields.Many2One('analytic_account.account',
'Analytic Account')
cost_planned = fields.Numeric('Cost Planned', readonly=True)
cost_real = fields.Numeric('Cost Real')
def get_unity(self, name=None):
unity = self.product.default_uom.name
return unity
class Activity(Workflow, ModelSQL, ModelView):
'Activity'
__name__ = 'farming.activity'
_rec_name = 'kind'
sequence = fields.Char('Sequence', states=STATES)
lot = fields.Many2One('farming.crop.lot', 'Lot', required=True,
states=STATES)
employee = fields.Many2One('party.party', 'Employee', states=STATES)
planned_date = fields.Date('Planned Date', states=STATES, required=True)
start_date = fields.Date('Start Date', states=STATES, required=True)
end_date = fields.Date('End Date', states=STATES)
kind = fields.Many2One('farming.activity.kind', 'Kind', states=STATES)
notes = fields.Text('Notes', states=STATES)
days = fields.Function(fields.Integer('Days', states=STATES), 'get_days')
shipments = fields.Many2Many('farming.activity-shipment.internal',
'activity', 'shipment', 'Shipments', states=STATES)
state = fields.Selection([
('draft', 'Draft'),
('execution', 'Execution'),
('done', 'Done'),
('cancelled', 'Cancelled'),
], 'State', readonly=True, required=True)
amount = fields.Function(fields.Numeric('Amount', digits=(16, 2)),
'get_amount')
@classmethod
def __setup__(cls):
super(Activity, cls).__setup__()
cls._order.insert(0, ('planned_date', 'DESC'))
cls._order.insert(1, ('id', 'DESC'))
cls._transitions |= set((
('draft', 'execution'),
('execution', 'done'),
('execution', 'draft'),
('execution', 'cancelled'),
('cancelled', 'draft'),
))
cls._buttons.update({
'draft': {
'invisible': Eval('state').in_(['draft', 'done']),
},
'cancel': {
'invisible': Eval('state').in_(['cancelled', 'done']),
},
'execution': {
'invisible': Eval('state') != 'draft',
},
'done': {
'invisible': Eval('state') != 'execution',
},
})
@staticmethod
def default_company():
return Transaction().context.get('company') or False
@staticmethod
def default_state():
return 'draft'
@classmethod
@ModelView.button
@Workflow.transition('done')
def done(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('execution')
def execution(cls, records):
for record in records:
record.set_number()
@classmethod
@ModelView.button
@Workflow.transition('cancelled')
def cancel(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, records):
pass
def get_amount(self, name):
res = 0
for shipment in self.shipments:
for m in shipment.outgoing_moves:
res += m.product.template.cost_price * Decimal(m.quantity)
for w in self.works:
res += w.unit_price * Decimal(w.quantity)
return res
def get_days(self, name):
res = 0
if self.start_date and self.end_date:
res = (self.end_date - self.start_date).days
return res
class CropForecastStart(ModelView):
'Crop Forecast Start'
__name__ = 'farming.crop_forecast.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)
@staticmethod
def default_company():
return Transaction().context.get('company')
class CropForecast(Wizard):
'Crop Forecast'
__name__ = 'farming.crop_forecast'
start = StateView(
'farming.crop_forecast.start',
'farming.farming_crop_forecast_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('farming.crop_forecast.report')
def do_print_(self, action):
data = {
'company': self.start.company.id,
'start_date': self.start.start_date,
}
return action, data
def transition_print_(self):
return 'end'
class CropForecastReport(Report):
'Crop Forecast Report'
__name__ = 'farming.crop_forecast.report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Crop = pool.get('farming.crop')
Company = pool.get('company.company')
recs = {}
week_range = []
for nd in range(MAX_WEEKS):
week_n = 'day' + str((nd + 1))
first_date = data['start_date'] + timedelta(nd * 7)
last_date = first_date + timedelta(6)
data[week_n] = first_date.strftime("%d %b")
data['total_' + week_n] = 0
data['week' + str(nd + 1)] = first_date.strftime("%W")
recs[week_n] = ''
recs[first_date] = {
'week': week_n
}
week_range.append((first_date, last_date))
crops = Crop.search([
('start_date', '<=', data['start_date']),
('end_date', '>=', data['start_date']),
])
records = {}
for crop in crops:
records[crop] = []
start_date = crop.start_date
recs_days = copy.deepcopy(recs)
for stage in crop.stages:
prod_date = start_date + timedelta(days=stage.week * 7)
for (start_day, end_day) in week_range:
if prod_date >= start_day and prod_date < end_day:
week_n = recs_days[start_day]['week']
recs_days[week_n] = int(stage.quantity_planned)
data['total_' + week_n] += int(stage.quantity_planned)
records[crop] = recs_days
report_context['records'] = records.items()
company_id = Transaction().context.get('company')
report_context['company'] = Company(company_id)
return report_context
class FarmingActivityShipmentInternal(ModelSQL):
'Farming Activity - Shipment Internal'
__name__ = 'farming.activity-shipment.internal'
_table = 'farming_activity_shipment_internal_rel'
activity = fields.Many2One('farming.activity', 'Activity',
ondelete='CASCADE', select=True, required=True)
shipment = fields.Many2One('stock.shipment.internal', 'Tax',
ondelete='RESTRICT', required=True)
class ActivityTask(ModelView, ModelSQL):
"Execute Activity"
__name__ = 'crop.activity.task'
STATES = {
'readonly': Eval('state') == 'finished',
}
effective_date = fields.Date('Effective Date ', states=STATES,
required=True)
employee = fields.Many2One('company.employee', 'Employee', states=STATES)
quantity = fields.Float('Quantity', states=STATES, required=True)
start_time = fields.DateTime('Start Time')
production = fields.Many2One('production', 'Production', states=STATES)
state = fields.Selection([
('draft', 'Draft'),
('processing', 'Processing'),
('finished', 'Finished'),
], 'State', select=True)
@staticmethod
def default_state():
return 'draft'
class CropActivitiesStart(ModelView):
'Crop Activities Start'
__name__ = 'farming.crop_activities.start'
company = fields.Many2One('company.company', 'Company', required=True)
crop = fields.Many2One('farming.crop', 'Crop')
start_date = fields.Date("Start Date")
location = fields.Many2One('farming.location', 'Location')
@staticmethod
def default_company():
return Transaction().context.get('company')
def default_start_date():
return datetime.today()
class CropActivitiesWizard(Wizard):
'Crop Activities'
__name__ = 'farming.crop_activities'
start = StateView(
'farming.crop_activities.start',
'farming.farming_crop_activities_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('farming.crop_activities.report')
def do_print_(self, action):
location_id = None
if self.start.location:
location_id = self.start.location.id
data = {
'company': self.start.company.id,
'start_date': self.start.start_date,
'location': location_id,
}
return action, data
def transition_print_(self):
return 'end'
class CropActivitiesReport(Report):
'Crop Activities Report'
__name__ = 'farming.crop_activities.report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
RANGE_WEEKS = 17
pool = Pool()
Company = pool.get('company.company')
Crop = pool.get('farming.crop')
date_start = data['start_date']
date_end = data['start_date'] + timedelta(days=(7*17))
dom = [
('activities.planned_date', '>=', date_start),
('activities.planned_date', '<=', date_end),
]
if data['location']:
dom.append(('location', '=', data['location']))
days = {}
matrix_dates = []
_date = date_start
for nd in range(1, RANGE_WEEKS):
sday = str(nd)
nday = 'day' + sday
days[nday] = ''
days['activity' + sday] = ''
data['week' + sday] = _date.strftime("%W")
data[nday] = _date.strftime("%d %b")
data['total_' + nday] = 0
last_date = _date + timedelta(days=1)
_date = _date + timedelta(days=7)
matrix_dates.append((last_date, _date, sday))
records = []
crops = Crop.search(dom)
for crop in crops:
lots_beds = []
for lot in crop.lots:
beds = '-'.join(bed.bed.number for bed in lot.beds)
lots_beds.append((lot.lot.number, beds))
# lots_beds = f'{lot.lot.number} ({beds})'
record = {
'number': crop.number,
'variety': crop.variety.product.name,
'location': crop.location.name,
'plants': crop.total_plants,
'lots': lots_beds,
'activities': [],
}
variety_acts = {
act.kind.id: act.time_of_realization
for act in crop.variety.activities
}
add_line = True
_days = copy.deepcopy(days)
for activity in crop.activities:
for start, end, sday in matrix_dates:
pdate = activity.planned_date
if pdate >= start and pdate <= end:
nday = 'day' + sday
if _days[nday] != '':
_days = copy.deepcopy(days)
record['activities'].append(_days)
elif add_line:
record['activities'].append(_days)
add_line = False
tr = variety_acts[activity.kind.id]
_days['activity' + sday] = f'{activity.kind.name}({tr})'
_days[nday] = activity.work_time
data['total_' + nday] += activity.work_time
records.append(record)
report_context['records'] = records
report_context['company'] = Company(data['company'])
return report_context
class CropSuppliesStart(ModelView):
'Crop Activities Start'
__name__ = 'farming.crop_supplies.start'
company = fields.Many2One('company.company', 'Company', required=True)
crop = fields.Many2One('farming.crop', 'Crop')
start_date = fields.Date("Start Date")
location = fields.Many2One('farming.location', 'Location', required=True)
@staticmethod
def default_company():
return Transaction().context.get('company')
def default_start_date():
return datetime.today()
class CropSuppliesWizard(Wizard):
'Crop Activities'
__name__ = 'farming.crop_supplies'
start = StateView(
'farming.crop_activities.start',
'farming.farming_crop_activities_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('farming.crop_supplies.report')
def do_print_(self, action):
data = {
#'crop': self.start.crop.id,
'company': self.start.company.id,
'start_date': self.start.start_date,
'location': self.start.location.id,
#'end_date': self.start.end_date,
}
return action, data
def transition_print_(self):
return 'end'
class CropSuppliesReport(Report):
'Crop Activities Report'
__name__ = 'farming.crop_supplies.report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Company = pool.get('company.company')
date_start = date(data['start_date'].year, data['start_date'].month, 1)
calculate_month = data['start_date'] + timedelta(days=30)
date_end = date(data['start_date'].year, calculate_month.month, 28)
week_list = []
date_calculate_week = date_start + timedelta(7)
for w in range(13):
week_data = {
'number': date_calculate_week.isocalendar()[1],
'range': date_calculate_week
}
week_list.append(week_data)
date_calculate_week = date_calculate_week + timedelta(days=7)
Location = pool.get('farming.location')
locations = Location.search([('id', '=', data['location'])])
# location_list = []
for location in locations:
Crop = pool.get('farming.crop')
crops = Crop.search([
('location', '=', location.id),
('end_date', '>=', date_end),
('activities', '!=', None)
])
supplies_list = []
for crop in crops:
for activity in crop.activities:
for supply in activity.supplies:
supply_record = supply.product.name
supplies_list.append(supply_record)
supplies_list = set(supplies_list)
supplies_list = [{'supply': spl} for spl in supplies_list]
crop_list = []
for crop in crops:
for activity in crop.activities:
activity_week = activity.planned_date.isocalendar()[1]
supply_info_list = []
for week in week_list:
spl = {
'quantity': '',
'unit': '',
}
supply_info_list.append(spl)
for supply in activity.supplies:
for index, week in enumerate(week_list):
week_number = week['number']
if activity_week == week_number:
for spl in supplies_list:
if spl['supply'] == supply.product.name:
supply_info_list[index-1] = {
'name': supply.product.name,
'quantity': supply.quantity,
'unit': supply.unit
}
#spl['info'] = supply_info_list
break
suply_crop = {
'crop': crop.number,
'info': supply_info_list
}
crop_list.append(suply_crop)
report_context['weeks'] = week_list
report_context['supplies'] = supplies_list
report_context['company'] = Company(data['company'])
report_context['date_start'] = date_start
report_context['date_end'] = date_end
return report_context
class CropForecastWeekStart(ModelView):
'Crop Activities Start'
__name__ = 'farming.crop_forecast_week.start'
company = fields.Many2One('company.company', 'Company', required=True)
crop = fields.Many2One('farming.crop', 'Crop')
start_date = fields.Date("Start Date")
location = fields.Many2One('farming.location', 'Location')
@staticmethod
def default_company():
return Transaction().context.get('company')
def default_start_date():
return datetime.today()
class CropForecastWeekWizard(Wizard):
'Crop Activities'
__name__ = 'farming.crop_forecast_week'
start = StateView(
'farming.crop_forecast_week.start',
'farming.farming_crop_forecast_week_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-ok', default=True),
])
print_ = StateReport('farming.crop_forecast_week.report')
def do_print_(self, action):
data = {
# 'crop': self.start.crop.id,
'company': self.start.company.id,
'start_date': self.start.start_date,
'location': self.start.location.id if self.start.location else self.start.location,
# 'end_date': self.start.end_date,
}
return action, data
def transition_print_(self):
return 'end'
class CropForecastWeekReport(Report):
'Crop Activities Report'
__name__ = 'farming.crop_forecast_week.report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Company = pool.get('company.company')
date_start = date(data['start_date'].year, data['start_date'].month, 1)
calculate_month = data['start_date'] + timedelta(days=30)
date_end = date(data['start_date'].year, calculate_month.month, 28)
week_list = []
date_calculate_week = date_start
for w in range(13):
week_data = {
'number': date_calculate_week.isocalendar()[1],
'range': date_calculate_week
}
week_list.append(week_data)
date_calculate_week = date_calculate_week + timedelta(days=7)
Location = pool.get('farming.location')
if data['location']:
locations = Location.search([('id', '=', data['location'])])
else:
locations = Location.search([('name', '!=', '')])
location_list = []
for location in locations:
Crop = pool.get('farming.crop')
crops = Crop.search([
('location', '=', location.id),
('end_date', '>=', date_end),
('activities', '!=', None)
])
crop_list = []
total_list = [0 for x in range(13)]
for crop in crops:
crop_report = {
'lots': []
}
lots_report = []
for lot in crop.lots:
beds = ''
for bed in lot.beds:
beds = beds + '-' + bed.bed.number
lot_dict = {
'number': crop.number,
'variety': crop.variety.product.name,
'lot': 'Lote: ' + lot.lot.number,
'total_plants': lot.total_plants,
'beds': 'Camas: ' + beds,
'stages': []
}
lots_report.append(lot_dict)
crop_report['lots'] = lots_report
Stage = pool.get('farming.crop.stage')
stages = Stage.search(['crop', "=", crop.id])
for lot_report in lots_report:
stage_list = []
for stage in stages:
stage_week = stage.effective_date.isocalendar()[1]
for week in week_list:
act = {
'name': '',
'production_rate': 0
}
stage_list.append(act)
for index, week in enumerate(week_list):
week_number = week['number']
if stage_week == week_number:
stage_list[index-1] = {
'name': stage.stage.name,
'production_rate': int(
stage.production_rate * lot_report['total_plants'] * crop.production_rate * .01)
}
total_list[index-1] = int(total_list[index-1] + (stage.production_rate * lot_report['total_plants'] * crop.production_rate * .01))
break
lot_report['stages'] = stage_list
crop_list.append(crop_report)
location_list.append({'name': location.name, 'crops': crop_list, 'total_time': total_list})
report_context['weeks'] = week_list
report_context['records'] = location_list
report_context['company'] = Company(data['company'])
report_context['date_start'] = date_start
report_context['date_end'] = date_end
return report_context
class FarmingCropReport(CompanyReport):
__name__ = 'farming.crop'