trytonpsk-farming/crop.py

1590 lines
57 KiB
Python
Raw Normal View History

2022-11-15 17:54:02 +01:00
# This file is part of Tryton. The COPYRIGHT file at the top level
# of this repository contains the full copyright notices and license terms.
2021-11-02 18:25:11 +01:00
from decimal import Decimal
2022-08-22 23:18:51 +02:00
from datetime import date, datetime, timedelta
import copy
2022-11-15 17:54:02 +01:00
2021-11-02 18:25:11 +01:00
from trytond.model import Workflow, ModelView, ModelSQL, fields
from trytond.pyson import Eval, If, Bool, In, Get
2021-11-02 18:25:11 +01:00
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.exceptions import UserError
2022-08-16 06:50:49 +02:00
from trytond.report import Report
2022-11-15 17:54:02 +01:00
from trytond.wizard import Wizard, StateReport, StateView, Button
2022-12-15 19:49:07 +01:00
from trytond.modules.company import CompanyReport
2021-11-02 18:25:11 +01:00
STATES = {
'readonly': (Eval('state') != 'draft'),
}
2022-12-01 17:42:58 +01:00
MAX_WEEKS = 18
2021-11-02 18:25:11 +01:00
2022-11-15 17:54:02 +01:00
2022-08-19 05:05:25 +02:00
class FarmingStage(ModelSQL, ModelView):
"Farming Stage"
__name__ = "farming.stage"
name = fields.Char('Name', required=True)
2023-01-17 06:17:25 +01:00
class Variety(ModelSQL, ModelView):
'Variety'
2022-08-16 06:50:49 +02:00
__name__ = 'farming.variety'
product = fields.Many2One('product.product', 'Product', required=True)
2022-08-22 23:18:51 +02:00
production_rate = fields.Float('Production Rate', digits=(16, 2))
2022-11-15 17:54:02 +01:00
production_uom = fields.Many2One('product.uom', 'UoM')
2022-08-19 05:05:25 +02:00
standard_cycle = fields.Integer('Standard Cycle', help='In weeks')
2023-01-17 06:17:25 +01:00
stages = fields.One2Many('farming.variety.stage', 'variety', 'Stages')
2022-11-01 21:16:44 +01:00
location = fields.Many2One('farming.location', 'Location')
2023-01-17 06:17:25 +01:00
# cycles = fields.One2Many('farming.variety.cycle', 'variety', 'Cycles')
2022-08-16 06:50:49 +02:00
def get_rec_name(self, name=None):
2022-11-02 14:45:26 +01:00
if self.location:
2022-11-15 17:54:02 +01:00
return self.product.template.name + '\u2014' + self.location.name
2022-11-02 14:45:26 +01:00
else:
return self.product.template.name
2022-08-16 06:50:49 +02:00
2023-01-17 06:17:25 +01:00
class VarietyStage(ModelSQL, ModelView):
"Variety Stage"
__name__ = "farming.variety.stage"
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))
activities = fields.One2Many('farming.variety.stage.activity', 'stage',
'Activities')
class VarietyStageActivity(ModelSQL, ModelView):
'Variety Stage Activity'
# 'farming.variety.activity' => 'farming.variety.stage.activity'
__name__ = 'farming.variety.stage.activity'
# variety = fields.Many2One('farming.variety', 'Variety')
stage = fields.Many2One('farming.variety.stage', 'Stage')
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')
2023-08-03 16:48:29 +02:00
uom_time = fields.Many2One('product.uom', 'Unit of Time',
states={'required': True})
2023-01-17 06:17:25 +01:00
supplies = fields.One2Many('farming.variety.stage.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 VarietyStageActivitySupply(ModelSQL, ModelView):
'Variety Stage Activity Supply'
__name__ = 'farming.variety.stage.activity.supply'
# farming.variety.activity.supply => farming.variety.stage.activity.supply
activity = fields.Many2One('farming.variety.stage.activity', 'Activity')
2022-11-15 23:54:25 +01:00
product = fields.Many2One("product.product", "Product", required=True)
quantity = fields.Float('Quantity', required=True)
2023-01-17 06:17:25 +01:00
unit = fields.Many2One('product.uom', 'Unit', domain=[
2022-10-22 19:05:32 +02:00
If(Bool(Eval('product_uom_category')),
('category', '=', Eval('product_uom_category')),
('category', '!=', -1)),
2023-01-17 06:17:25 +01:00
], depends=['product_uom_category'])
product_uom_category = fields.Function(fields.Many2One(
'product.uom.category', 'Product Uom Category'),
2022-10-22 19:05:32 +02:00
'on_change_with_product_uom_category')
2023-01-17 06:17:25 +01:00
unit_price = fields.Numeric('Unit Price', digits=(16, 2))
@fields.depends('product')
2022-10-22 19:05:32 +02:00
def on_change_with_unit(self, name=None):
if self.product:
unit = self.product.default_uom.id
return unit
2022-10-22 19:05:32 +02:00
@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
2022-11-15 17:54:02 +01:00
2023-01-17 21:11:54 +01:00
class CropStage(Workflow, ModelSQL, ModelView):
2022-08-19 16:59:05 +02:00
"Crop Stage"
2022-11-15 17:54:02 +01:00
__name__ = "farming.crop.stage"
2023-01-17 21:11:54 +01:00
sequence = fields.Integer('Sequence', required=True)
2022-08-19 16:59:05 +02:00
crop = fields.Many2One('farming.crop', 'Crop', required=True)
stage = fields.Many2One('farming.stage', 'Stage', required=True)
2022-10-22 19:05:32 +02:00
week = fields.Float('Week', required=True)
effective_date = fields.Date('Effective Date')
2023-01-17 06:17:25 +01:00
activities = fields.One2Many('farming.crop.stage.activity', 'stage',
'Activity')
2022-08-22 23:18:51 +02:00
production_rate = fields.Float('Production Rate', digits=(3, 2))
2022-11-16 18:45:36 +01:00
quantity_planned = fields.Function(fields.Float('Qty. Planned',
digits=(6, 2)), 'get_quantity_planned')
2022-11-16 05:04:53 +01:00
quantity_produced = fields.Float('Qty. Produced', digits=(6, 2))
2023-01-17 21:11:54 +01:00
total_cost = fields.Function(fields.Numeric(
'Total Cost', digits=(16, 2)), 'get_total_cost')
losses = fields.One2Many('farming.crop.stage.loss', 'stage', 'Losses')
total_losses = fields.Function(fields.Numeric(
'Total Losses', digits=(16, 2)), 'get_total_losses')
state = fields.Selection([
('pending', 'Pending'),
('running', 'Running'),
('finished', 'Finished'),
('cancelled', 'Cancelled'),
], 'State', readonly=True, required=True)
@classmethod
def __setup__(cls):
super(CropStage, cls).__setup__()
cls._order.insert(0, ('sequence', 'ASC'))
cls._transitions |= set((
('pending', 'running'),
('running', 'finished'),
('running', 'pending'),
))
cls._buttons.update({
'pending': {
'invisible': Eval('state').in_(['pending', 'finished'])
},
'running': {
'invisible': Eval('state').in_(['finished', 'running']),
},
'finished': {
'invisible': Eval('state') != 'running',
}})
@classmethod
@ModelView.button
@Workflow.transition('pending')
def pending(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('running')
def running(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('finished')
def finished(cls, records):
pass
@staticmethod
def default_state():
return 'pending'
def get_total_losses(self, name=None):
return sum(lo.quantity for lo in self.losses)
def get_total_cost(self, name=None):
return sum(act.total_cost or 0 for act in self.activities)
2022-08-19 16:59:05 +02:00
2022-11-16 18:45:36 +01:00
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)
2022-08-19 16:59:05 +02:00
2023-01-17 21:11:54 +01:00
class CropStageLoss(Workflow, ModelSQL, ModelView):
'Crop Stage Loss'
__name__ = 'farming.crop.stage.loss'
stage = fields.Many2One('farming.crop.stage', 'Stage', ondelete='CASCADE')
date = fields.Date('Date', required=True)
quantity = fields.Integer('Quantity', required=True)
reason = fields.Text('Reason', required=True)
2021-11-02 18:25:11 +01:00
class Kind(ModelSQL, ModelView):
2022-11-16 18:45:36 +01:00
"Activity Kind"
2021-11-02 18:25:11 +01:00
__name__ = "farming.activity.kind"
name = fields.Char('Name', required=True)
activity_time = fields.Float('Act. Time', required=True)
2022-11-15 23:54:25 +01:00
uom = fields.Many2One('product.uom', 'Unit', required=True,
help="Unit of time, according to activity time")
2021-11-02 18:25:11 +01:00
2023-01-17 06:17:25 +01:00
class Crop(Workflow, ModelSQL, ModelView):
2022-08-16 06:50:49 +02:00
'Farming Crop'
__name__ = 'farming.crop'
2021-11-02 18:25:11 +01:00
_rec_name = 'number'
number = fields.Char('Number', readonly=True)
2022-08-16 06:50:49 +02:00
variety = fields.Many2One('farming.variety', 'Variety', required=True,
2021-11-02 18:25:11 +01:00
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'])
2022-11-15 17:54:02 +01:00
field_size = fields.Float('Field Size', states=STATES, select=True,
help='In hectares')
total_plants = fields.Integer('Total Plants')
2022-11-16 18:45:36 +01:00
quantity_planned = fields.Function(fields.Float('Quantity Planned'),
'get_quantity_planned')
2023-01-17 21:11:54 +01:00
quantity_planned_net = fields.Function(fields.Float('Net. Quantity Planned'),
'get_quantity_planned_net')
2021-11-02 18:25:11 +01:00
quantity_produced = fields.Float('Quantity Produced', states=STATES)
2022-08-22 23:18:51 +02:00
production_uom = fields.Many2One('product.uom', 'Production UoM', states=STATES)
2022-08-19 16:59:05 +02:00
lots = fields.One2Many('farming.crop.lot', 'crop', 'Lots', states=STATES)
stages = fields.One2Many('farming.crop.stage', 'crop', 'Stages',
states=STATES)
2022-08-19 05:05:25 +02:00
seed = fields.Many2One('farming.seed', 'Seed', required=True)
2022-10-22 19:05:32 +02:00
analytic_account = fields.Many2One('analytic_account.account', 'Analytic Account')
2022-08-22 23:18:51 +02:00
production_rate = fields.Float('Production Rate')
2021-11-02 18:25:11 +01:00
notes = fields.Text('Notes', states=STATES)
state = fields.Selection([
('draft', 'Draft'),
('production', 'Production'),
('finished', 'Finished'),
('cancelled', 'Cancelled'),
], 'State', readonly=True, required=True)
2022-11-15 23:54:25 +01:00
# moves for supplies
2023-01-17 06:17:25 +01:00
# supplies = fields.One2Many('farming.crop.stage.activity.supply',
# 'crop', 'Supply')
2021-11-02 18:25:11 +01:00
# Financial indicators
production_time = fields.Function(fields.Integer('Production Time'),
'get_production_time')
production_cost = fields.Function(fields.Numeric('Production Cost'),
'get_production_cost')
2023-01-17 21:11:54 +01:00
performance = fields.Function(fields.Float('Performance', digits=(4, 2)),
2021-11-02 18:25:11 +01:00
'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')
2023-01-17 06:17:25 +01:00
# Deprecation warning: activities was moved to farming.crop.stage
# activities = fields.One2Many('farming.crop.activity', 'crop', 'Activities')
2023-01-17 21:11:54 +01:00
total_cost = fields.Function(fields.Numeric(
'Total Cost', digits=(16, 2)), 'get_total_cost')
total_losses = fields.Function(fields.Numeric(
'Total Losses', digits=(16, 2)), 'get_total_losses')
2021-11-02 18:25:11 +01:00
@classmethod
def __setup__(cls):
2023-01-17 06:17:25 +01:00
super(Crop, cls).__setup__()
2021-11-02 18:25:11 +01:00
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',
},
2023-01-17 06:17:25 +01:00
'load_stages': {
2022-11-15 17:54:02 +01:00
'invisible': Eval('lines', []) | (Eval('state') != 'draft'),
'depends': ['state'],
},
2022-11-15 23:54:25 +01:00
'load_lots': {
2022-11-15 17:54:02 +01:00
'invisible': Eval('lines', []) | (Eval('state') != 'draft'),
'depends': ['state'],
},
2021-11-02 18:25:11 +01:00
'production': {
'invisible': Eval('state').in_(['cancelled', 'finished']),
},
'finished': {
'invisible': Eval('state') != 'production',
},
})
@staticmethod
def default_start_date():
return date.today()
2021-11-02 18:25:11 +01:00
@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()
2023-01-17 21:11:54 +01:00
def get_total_cost(self, name=None):
return sum(act.total_cost or 0 for act in self.stages)
def get_total_losses(self, name=None):
return sum(st.total_losses for st in self.stages)
@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(
2022-11-15 17:54:02 +01:00
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')
2022-11-15 17:54:02 +01:00
Lot = Pool().get('farming.location.lot')
lines = []
cls.clear_bed_lines(crops)
cls.clear_lots_lines(crops)
for crop in crops:
2022-11-02 22:13:39 +01:00
lots = Lot.search([('location', '=', crop.location)])
for lot in lots:
line = Line(
2022-11-15 17:54:02 +01:00
crop=crop.id,
lot=lot.id,
cycle=0,
beds=[],
activities=[],
total_plants=lot.quantity
)
lines.append(line)
2022-11-15 17:54:02 +01:00
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)
2023-01-17 06:17:25 +01:00
def _create_supplies(self, activity, crop_activity):
Supply = Pool().get('farming.crop.stage.activity.supply')
res = []
for sup in activity.supplies:
unit_price = sup.unit_price
if not unit_price:
unit_price = sup.product.cost_price
qty = sup.quantity * self.total_plants
amount = round(qty * float(unit_price), 2)
supply = Supply(
activity=crop_activity.id,
product=sup.product.id,
unit=sup.unit.id,
unit_price=unit_price,
quantity=qty,
cost_planned=amount,
cost_real=amount
)
res.append(supply)
return res
def _create_activities(self, stage, crop_stage):
Activity = Pool().get('farming.crop.stage.activity')
res = []
crop = crop_stage.crop
for act in stage.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)
activity = Activity(
stage=crop_stage.id,
sequence=act.sequence,
kind=act.kind.id,
work_time=work_time,
uom_time=act.uom_time.id,
week=act.time_of_realization,
planned_date=_date,
effective_date=_date,
)
supplies = self._create_supplies(act, activity)
activity.supplies = supplies
res.append(activity)
return res
def create_stages(self):
CropStage = Pool().get('farming.crop.stage')
to_save = []
for stage in self.variety.stages:
effec_date = self.start_date + timedelta(weeks=stage.start_time)
_stage = CropStage(
2023-01-17 21:11:54 +01:00
sequence=stage.sequence,
2023-01-17 06:17:25 +01:00
crop=self.id,
stage=stage.stage.id,
production_rate=stage.production_rate,
week=stage.start_time,
effective_date=effec_date,
)
_stage.activities = self._create_activities(stage, _stage)
to_save.append(_stage)
CropStage.save(to_save)
2022-11-15 17:54:02 +01:00
@classmethod
def clear_stages_lines(cls, crops):
Line = Pool().get('farming.crop.stage')
2023-01-17 06:17:25 +01:00
to_delete = []
for crop in crops:
for stage in crop.stages:
2023-01-17 06:17:25 +01:00
to_delete.append(stage)
Line.delete(to_delete)
# @classmethod
# def clear_activities_lines(cls, crops):
# Line = Pool().get('farming.crop.stage.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)
2022-11-15 17:54:02 +01:00
@classmethod
def create_activity_supplies_lines(cls, crops):
2023-01-17 06:17:25 +01:00
Line = Pool().get('farming.crop.stage.activity.supply')
lines = []
for crop in crops:
2023-01-17 06:17:25 +01:00
for act_crop, act_variety in zip(
crop.activities, crop.variety.activities):
2022-11-15 23:54:25 +01:00
for supply in act_variety.supplies:
line = Line(
2022-11-15 23:54:25 +01:00
activity=act_crop.id,
2022-11-15 17:54:02 +01:00
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)
2022-11-15 17:54:02 +01:00
2022-11-01 21:16:44 +01:00
@classmethod
def clear_supplies_lines(cls, crops):
Line = Pool().get('farming.crop.lot.bed')
lines_to_delete = []
for crop in crops:
2023-01-17 06:17:25 +01:00
for stage in crop.stages:
for activity in stage.activities:
for supply in activity.supplies:
lines_to_delete.append(supply)
2022-11-01 21:16:44 +01:00
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
2023-01-17 06:17:25 +01:00
def load_stages(cls, crops):
cls.clear_stages_lines(crops)
2023-01-17 06:17:25 +01:00
for crop in crops:
crop.create_stages()
# cls.clear_stages_lines(crops)
# cls.clear_supplies_lines(crops)
# cls.clear_activities_lines(crops)
# cls.create_activities_lines(crops)
2022-11-01 21:16:44 +01:00
@classmethod
@ModelView.button
2022-11-15 23:54:25 +01:00
def load_lots(cls, crops):
cls.create_lots_lines(crops)
cls.get_total_plants(crops)
2022-09-27 22:42:52 +02:00
@classmethod
2022-11-16 18:45:36 +01:00
def supplies_shipment(cls, records):
2022-09-27 22:42:52 +02:00
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(
2022-11-15 17:54:02 +01:00
crop=record.id,
product=supply.product,
unit=supply.unit,
quantity=supply.quantity,
2022-09-27 22:42:52 +02:00
)
supplyMoves.append(supplyMove)
SupplyMoves.save(supplyMoves)
2021-11-02 18:25:11 +01:00
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})
2023-01-17 21:11:54 +01:00
def get_quantity_planned_net(self, name=None):
return self.quantity_planned - (
self.total_losses * self.production_rate)
2022-11-16 18:45:36 +01:00
def get_quantity_planned(self, name=None):
2022-08-19 05:05:25 +02:00
res = []
_append = res.append
for lot in self.lots:
2022-08-22 23:18:51 +02:00
if self.production_rate and lot.total_plants:
_append(self.production_rate * lot.total_plants)
2022-11-25 05:49:43 +01:00
return round(sum(res), 2)
2022-08-19 05:05:25 +02:00
def get_production_time(self, name=None):
2021-11-02 18:25:11 +01:00
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):
2023-07-12 17:32:31 +02:00
res = 0
if self.quantity_produced and self.quantity_planned_net:
res = (self.quantity_produced / self.quantity_planned_net) * 100
2021-11-02 18:25:11 +01:00
return res
def get_gross_profit_rate(self, name):
res = 0
return res
def get_gross_profit(self, name):
res = 0
return res
2022-11-16 18:45:36 +01:00
@fields.depends('stages')
def on_change_stages(self):
if self.stages:
res = []
for stage in self.stages:
2023-01-17 06:17:25 +01:00
res.append(stage.quantity_produced or 0)
2022-11-16 18:45:36 +01:00
self.quantity_produced = sum(res)
2022-08-22 23:18:51 +02:00
@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:
2022-11-15 17:54:02 +01:00
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:
2022-11-15 23:54:25 +01:00
self.end_date = self.start_date + timedelta(
weeks=self.variety.standard_cycle)
2022-11-15 17:54:02 +01:00
@fields.depends('lots')
2022-11-15 17:54:02 +01:00
def on_change_with_total_plants(self, name=None):
quantity = 0
for lot in self.lots:
2022-11-01 22:40:23 +01:00
if lot.total_plants:
quantity += lot.total_plants
return quantity
2021-11-02 18:25:11 +01:00
2022-09-27 22:42:52 +02:00
class SupplyMoves(ModelSQL, ModelView):
'Supply moves'
__name__ = 'crop.supply.moves'
2022-11-15 17:54:02 +01:00
crop = fields.Many2One('farming.crop', 'Crop', ondelete='CASCADE',
required=True)
2022-09-27 22:42:52 +02:00
product = fields.Many2One("product.product", "Product", required=True,
2022-11-15 17:54:02 +01:00
select=True)
2022-09-27 22:42:52 +02:00
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]
2022-08-16 06:50:49 +02:00
class FarmingCropLot(ModelSQL, ModelView):
'Farming Crop Lot'
__name__ = 'farming.crop.lot'
2022-11-15 17:54:02 +01:00
crop = fields.Many2One('farming.crop', 'Crop', ondelete='CASCADE',
required=True)
2022-11-02 22:13:39 +01:00
lot = fields.Many2One('farming.location.lot', 'Lot', required=True)
2022-08-16 06:50:49 +02:00
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')
2022-11-16 18:45:36 +01:00
analytic_account = fields.Many2One('analytic_account.account',
'Analytic Account')
2022-11-15 17:54:02 +01:00
total_plants = fields.Integer('Total Plants', states={
'readonly': Bool(Eval('beds'))}, depends=['beds'])
@fields.depends('beds')
2022-11-15 17:54:02 +01:00
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
2022-08-16 06:50:49 +02:00
2022-11-15 17:54:02 +01:00
2022-08-16 06:50:49 +02:00
class FarmingCropLotBed(ModelSQL, ModelView):
'Crop Lot Bed'
__name__ = 'farming.crop.lot.bed'
2022-11-15 23:54:25 +01:00
lot = fields.Many2One('farming.crop.lot', 'Lot', ondelete='CASCADE',
required=True)
2022-11-15 17:54:02 +01:00
bed = fields.Many2One('farming.location.lot.bed', 'Bed',
ondelete='CASCADE', required=True)
quantity_plants = fields.Integer('Quantity Plants')
2022-11-15 17:54:02 +01:00
2023-01-17 06:17:25 +01:00
class CropStageActivity(Workflow, ModelSQL, ModelView):
'Crop Stage Activity'
# farming.crop.activity => farming.crop.stage.activity
__name__ = 'farming.crop.stage.activity'
# crop = fields.Many2One('farming.crop', 'Crop', ondelete='CASCADE')
stage = fields.Many2One('farming.crop.stage', 'Stage', ondelete='CASCADE')
sequence = fields.Char('Sequence', states=STATES)
kind = fields.Many2One('farming.activity.kind', 'Kind')
2022-11-05 18:05:01 +01:00
work_time = fields.Numeric('Work Time')
2023-01-17 06:17:25 +01:00
# uom = fields.Many2One('product.uom', 'Unit')
2022-11-15 23:54:25 +01:00
uom_time = fields.Many2One('product.uom', 'UoM of Time')
2023-01-17 06:17:25 +01:00
supplies = fields.One2Many('farming.crop.stage.activity.supply',
'activity', 'Supply')
2022-11-01 21:16:44 +01:00
week = fields.Integer('Week')
2022-11-15 23:54:25 +01:00
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)
2023-01-17 06:17:25 +01:00
workforce_cost = fields.Function(fields.Numeric(
'Workforce Cost', digits=(16, 2)), 'get_cost')
materials_cost = fields.Function(fields.Numeric(
'Materials Cost', digits=(16, 2)), 'get_cost')
indirect_cost = fields.Function(fields.Numeric(
'Indirect Cost', digits=(16, 2)), 'get_cost')
total_cost = fields.Function(fields.Numeric(
'Total Cost', digits=(16, 2)), 'get_cost')
2022-09-27 22:42:52 +02:00
@classmethod
2023-01-14 16:15:34 +01:00
def generate_shipment(cls, records):
2022-09-27 22:42:52 +02:00
pool = Pool()
2022-10-22 19:05:32 +02:00
company = Transaction().context.get('company')
2023-01-17 06:17:25 +01:00
Supply = pool.get('farming.crop.stage.activity.supply')
2022-10-22 19:05:32 +02:00
supplies = []
2022-09-27 22:42:52 +02:00
for record in records:
2022-10-22 19:05:32 +02:00
location = record.crop.location.warehouse
for supplyRec in record.supplies:
supply = Supply(
2022-11-15 17:54:02 +01:00
crop=record.crop,
product=supplyRec.product,
unit=supplyRec.unit,
quantity=supplyRec.quantity,
2022-09-27 22:42:52 +02:00
)
2023-01-17 06:17:25 +01:00
# move = record._move(
# record,
# location.output_location,
# location.production_location,
# company,
# supplyRec.product,
# supplyRec.unit,
# supplyRec.quantity)
2022-10-22 19:05:32 +02:00
supplies.append(supply)
Supply.save(supplies)
2022-11-15 17:54:02 +01:00
2022-09-27 22:42:52 +02:00
@classmethod
@ModelView.button
2023-01-14 16:15:34 +01:00
def generate_shipment_button(cls, records):
2022-11-01 21:16:44 +01:00
pass
2022-09-27 22:42:52 +02:00
2022-11-15 17:54:02 +01:00
def _move(self, activity, from_location, to_location, company, product,
uom, quantity):
2022-10-22 19:05:32 +02:00
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
2022-11-15 17:54:02 +01:00
2023-01-17 06:17:25 +01:00
def get_cost(self, name=None):
res = []
for supply in self.supplies:
_name, _name_cost = name.split('_')
print(name, _name, _name_cost)
if supply.product.template.cost_type == _name or _name == 'total':
res.append(supply.cost_planned)
return sum(res)
@classmethod
2022-10-22 19:05:32 +02:00
def create_supply_moves(cls, location):
2022-11-15 17:54:02 +01:00
# pool = Pool()
# Move = pool.get('stock.move')
# Location = pool.get('stock.location')
# Date = pool.get('ir.date')
# Shipment = pool.get('stock.shipment.internal')
# moves = []
2022-10-22 19:05:32 +02:00
# 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,
2022-11-15 17:54:02 +01:00
# from_location =
# to_location =
2022-10-22 19:05:32 +02:00
# state = 'draft',
# company = Transaction().context.get('company'),
# unit_price = supply.product.cost_price,
# planned_date = Date.today(),
2022-11-15 17:54:02 +01:00
# #origin = ['production']
2022-10-22 19:05:32 +02:00
# )
# moves.append(move)
# shipment.moves = moves
2022-11-15 17:54:02 +01:00
# Shipment.save([shipment])
2022-10-22 19:05:32 +02:00
# #Move.save(moves)
# #BUG(ERROR: SHIPMENT INTERRUPTS STATUS, NOT GENERATED)
2022-11-15 17:54:02 +01:00
pass
@classmethod
def __setup__(cls):
2023-01-17 06:17:25 +01:00
super(CropStageActivity, cls).__setup__()
2022-10-22 19:05:32 +02:00
cls._order.insert(0, ('planned_date', 'ASC'))
cls._transitions |= set((
('draft', 'production'),
('production', 'draft'),
('production', 'finished'),
('production', 'cancelled'),
2022-11-15 17:54:02 +01:00
('finished', 'production'), # only developings
))
cls._buttons.update({
'draft': {
'invisible': Eval('state').in_(['draft', 'finished'])
},
'cancel': {
'invisible': Eval('state') == 'finished',
},
'load_configuration': {
2022-11-16 18:45:36 +01:00
'invisible': Eval('lines', []) | (Eval('state') != 'draft'),
'depends': ['state'],
},
2022-11-15 17:54:02 +01:00
'production': {},
'finished': {
'invisible': Eval('state') != 'production',
},
})
@staticmethod
def default_state():
return 'draft'
@classmethod
@ModelView.button
@Workflow.transition('finished')
def finished(cls, records):
2022-10-22 19:05:32 +02:00
pool = Pool()
AccountMove = pool.get('account.move')
Journal = pool.get('account.journal')
journal = Journal.search([('type', '=', 'expense')])
for record in records:
account_move = {
2022-11-15 17:54:02 +01:00
'company': Transaction().context.get('company'),
'journal': journal[0].id,
'date': date.today(),
'origin': str(record.crop),
'lines': None,
2022-10-22 19:05:32 +02:00
}
account_move_lines = []
for supply in record.supplies:
product = supply.product
account = product.account_category
product_name = product.name
lineC = {
2022-11-15 17:54:02 +01:00
'debit': Decimal(0),
'credit': product.cost_price,
'account': account.account_stock.id,
'description': product_name,
2022-10-22 19:05:32 +02:00
}
lineD = {
2022-11-15 17:54:02 +01:00
'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()
2022-10-22 19:05:32 +02:00
}])]
}
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:
2022-10-22 19:05:32 +02:00
#cls.create_shipment(record)
2023-01-14 16:15:34 +01:00
cls.generate_shipment([record])
record.set_number()
def set_number(self):
2022-11-01 21:16:44 +01:00
#Config = Pool().get('farming.configuration')
#config = Config.get_config()
#if not config.farming_production_sequence:
# raise UserError('missing_sequence_farming_production')
pass
2022-11-15 17:54:02 +01:00
2023-01-17 06:17:25 +01:00
class CropStageActivitySupply(ModelSQL, ModelView):
'Crop Stage Activity Supply'
# farming.crop.activity.supply => farming.crop.stage.activity.supply
__name__ = 'farming.crop.stage.activity.supply'
# crop = fields.Many2One('farming.crop', 'Crop')
activity = fields.Many2One('farming.crop.stage.activity', 'Activity',
2022-11-15 17:54:02 +01:00
states={"invisible": False})
2022-10-22 19:05:32 +02:00
product = fields.Many2One("product.product", "Product", required=True,
select=True)
unit = fields.Many2One('product.uom', 'Unit')
quantity = fields.Float('Quantity', required=True)
2023-01-17 06:17:25 +01:00
unit_price = fields.Numeric('Unit Price')
2022-11-15 17:54:02 +01:00
analytic_account = fields.Many2One('analytic_account.account',
'Analytic Account')
2022-10-22 19:05:32 +02:00
cost_planned = fields.Numeric('Cost Planned', readonly=True)
cost_real = fields.Numeric('Cost Real')
2023-01-17 06:17:25 +01:00
# def get_unit(self, name=None):
# unit = self.product.default_uom.name
# return unit
2022-11-15 17:54:02 +01:00
2021-11-02 18:25:11 +01:00
class Activity(Workflow, ModelSQL, ModelView):
'Activity'
__name__ = 'farming.activity'
_rec_name = 'kind'
sequence = fields.Char('Sequence', states=STATES)
2022-08-16 06:50:49 +02:00
lot = fields.Many2One('farming.crop.lot', 'Lot', required=True,
states=STATES)
employee = fields.Many2One('party.party', 'Employee', states=STATES)
2021-11-02 18:25:11 +01:00
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)
2022-08-16 06:50:49 +02:00
amount = fields.Function(fields.Numeric('Amount', digits=(16, 2)),
'get_amount')
2021-11-02 18:25:11 +01:00
@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
2022-08-16 06:50:49 +02:00
class CropForecastStart(ModelView):
'Crop Forecast Start'
__name__ = 'farming.crop_forecast.start'
company = fields.Many2One('company.company', 'Company', required=True)
2022-08-17 00:46:41 +02:00
start_date = fields.Date("Start Date", required=True)
2022-08-22 23:18:51 +02:00
end_date = fields.Date("End Date", required=True)
2021-11-02 18:25:11 +01:00
2022-08-16 06:50:49 +02:00
@staticmethod
def default_company():
return Transaction().context.get('company')
2021-11-02 18:25:11 +01:00
2022-08-16 06:50:49 +02:00
class CropForecast(Wizard):
'Crop Forecast'
__name__ = 'farming.crop_forecast'
2022-11-15 17:54:02 +01:00
start = StateView(
'farming.crop_forecast.start',
2022-08-16 06:50:49 +02:00
'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,
2022-08-17 00:46:41 +02:00
'start_date': self.start.start_date,
2022-08-16 06:50:49 +02:00
}
return action, data
def transition_print_(self):
return 'end'
class CropForecastReport(Report):
'Crop Forecast Report'
__name__ = 'farming.crop_forecast.report'
2022-08-22 23:18:51 +02:00
2022-08-16 06:50:49 +02:00
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
2022-08-17 00:46:41 +02:00
Crop = pool.get('farming.crop')
2022-11-16 18:45:36 +01:00
Company = pool.get('company.company')
2022-08-22 23:18:51 +02:00
2022-11-25 05:49:43 +01:00
recs = {}
2022-08-22 23:18:51 +02:00
week_range = []
for nd in range(MAX_WEEKS):
week_n = 'day' + str((nd + 1))
first_date = data['start_date'] + timedelta(nd * 7)
2022-11-16 18:45:36 +01:00
last_date = first_date + timedelta(6)
2022-11-16 05:04:53 +01:00
data[week_n] = first_date.strftime("%d %b")
2022-08-22 23:18:51 +02:00
data['total_' + week_n] = 0
2022-11-16 18:45:36 +01:00
data['week' + str(nd + 1)] = first_date.strftime("%W")
2022-11-25 05:49:43 +01:00
recs[week_n] = ''
recs[first_date] = {
2022-11-16 18:45:36 +01:00
'week': week_n
}
2022-11-15 17:54:02 +01:00
week_range.append((first_date, last_date))
2022-08-17 00:46:41 +02:00
crops = Crop.search([
2022-12-12 18:56:37 +01:00
('start_date', '<=', data['start_date']),
('end_date', '>=', data['start_date']),
2022-08-17 00:46:41 +02:00
])
2022-08-22 23:18:51 +02:00
records = {}
for crop in crops:
records[crop] = []
start_date = crop.start_date
2022-11-25 05:49:43 +01:00
recs_days = copy.deepcopy(recs)
2022-11-16 18:45:36 +01:00
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:
2022-11-25 05:49:43 +01:00
week_n = recs_days[start_day]['week']
2022-12-12 17:28:06 +01:00
recs_days[week_n] = int(stage.quantity_planned)
data['total_' + week_n] += int(stage.quantity_planned)
2022-11-16 18:45:36 +01:00
2022-11-25 05:49:43 +01:00
records[crop] = recs_days
2022-08-22 23:18:51 +02:00
2022-11-15 17:54:02 +01:00
report_context['records'] = records.items()
2022-11-16 18:45:36 +01:00
company_id = Transaction().context.get('company')
report_context['company'] = Company(company_id)
2022-08-16 06:50:49 +02:00
return report_context
2021-11-02 18:25:11 +01:00
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)
2022-11-15 17:54:02 +01:00
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():
2022-10-22 19:05:32 +02:00
return 'draft'
2022-11-15 17:54:02 +01:00
2022-10-22 19:05:32 +02:00
class CropActivitiesStart(ModelView):
'Crop Activities Start'
__name__ = 'farming.crop_activities.start'
2022-11-01 21:16:44 +01:00
company = fields.Many2One('company.company', 'Company', required=True)
crop = fields.Many2One('farming.crop', 'Crop')
start_date = fields.Date("Start Date")
2022-11-10 16:24:59 +01:00
location = fields.Many2One('farming.location', 'Location')
2022-11-15 17:54:02 +01:00
2022-10-22 19:05:32 +02:00
@staticmethod
def default_company():
return Transaction().context.get('company')
2022-11-01 21:16:44 +01:00
def default_start_date():
return datetime.today()
2022-10-22 19:05:32 +02:00
class CropActivitiesWizard(Wizard):
'Crop Activities'
__name__ = 'farming.crop_activities'
2022-11-15 17:54:02 +01:00
start = StateView(
'farming.crop_activities.start',
2022-10-22 19:05:32 +02:00
'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):
2022-12-10 17:29:44 +01:00
location_id = None
if self.start.location:
location_id = self.start.location.id
2022-10-22 19:05:32 +02:00
data = {
'company': self.start.company.id,
'start_date': self.start.start_date,
2022-12-10 17:29:44 +01:00
'location': location_id,
2022-10-22 19:05:32 +02:00
}
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)
2022-12-10 17:29:44 +01:00
RANGE_WEEKS = 17
2022-10-22 19:05:32 +02:00
pool = Pool()
Company = pool.get('company.company')
2022-12-10 17:29:44 +01:00
Crop = pool.get('farming.crop')
date_start = data['start_date']
2022-12-18 05:08:11 +01:00
date_end = data['start_date'] + timedelta(days=(7*17))
2022-12-10 17:29:44 +01:00
dom = [
2022-12-18 05:08:11 +01:00
('activities.planned_date', '>=', date_start),
('activities.planned_date', '<=', date_end),
2022-12-10 17:29:44 +01:00
]
2022-11-01 21:16:44 +01:00
2022-11-10 16:24:59 +01:00
if data['location']:
2022-12-10 17:29:44 +01:00
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] = ''
2022-12-15 05:50:01 +01:00
days['activity' + sday] = ''
2022-12-10 17:29:44 +01:00
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)
2022-12-15 05:50:01 +01:00
matrix_dates.append((last_date, _date, sday))
2022-12-10 17:29:44 +01:00
records = []
crops = Crop.search(dom)
for crop in crops:
2022-12-19 15:23:39 +01:00
lots_beds = []
2022-12-17 18:06:18 +01:00
for lot in crop.lots:
beds = '-'.join(bed.bed.number for bed in lot.beds)
2022-12-19 15:23:39 +01:00
lots_beds.append((lot.lot.number, beds))
# lots_beds = f'{lot.lot.number} ({beds})'
2022-12-10 17:29:44 +01:00
record = {
'number': crop.number,
'variety': crop.variety.product.name,
'location': crop.location.name,
'plants': crop.total_plants,
2022-12-17 18:06:18 +01:00
'lots': lots_beds,
2022-12-10 17:29:44 +01:00
'activities': [],
}
2022-12-15 19:49:07 +01:00
variety_acts = {
2022-12-19 15:23:39 +01:00
act.kind.id: act.time_of_realization
for act in crop.variety.activities
2022-12-15 19:49:07 +01:00
}
2022-12-15 05:50:01 +01:00
add_line = True
_days = copy.deepcopy(days)
2022-12-10 17:29:44 +01:00
for activity in crop.activities:
2022-12-15 05:50:01 +01:00
for start, end, sday in matrix_dates:
2022-12-10 17:29:44 +01:00
pdate = activity.planned_date
if pdate >= start and pdate <= end:
2022-12-15 05:50:01 +01:00
nday = 'day' + sday
if _days[nday] != '':
_days = copy.deepcopy(days)
record['activities'].append(_days)
elif add_line:
record['activities'].append(_days)
add_line = False
2022-12-15 19:49:07 +01:00
tr = variety_acts[activity.kind.id]
_days['activity' + sday] = f'{activity.kind.name}({tr})'
2022-12-10 17:29:44 +01:00
_days[nday] = activity.work_time
data['total_' + nday] += activity.work_time
records.append(record)
report_context['records'] = records
2022-11-01 21:16:44 +01:00
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)
2022-11-15 17:54:02 +01:00
2022-11-01 21:16:44 +01:00
@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'
2022-11-15 17:54:02 +01:00
start = StateView(
'farming.crop_activities.start',
2022-11-01 21:16:44 +01:00
'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)
2022-11-10 16:24:59 +01:00
calculate_month = data['start_date'] + timedelta(days=30)
date_end = date(data['start_date'].year, calculate_month.month, 28)
2022-11-01 21:16:44 +01:00
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)
2022-11-15 17:54:02 +01:00
2022-11-01 21:16:44 +01:00
Location = pool.get('farming.location')
locations = Location.search([('id', '=', data['location'])])
2022-11-15 17:54:02 +01:00
# location_list = []
2022-11-01 21:16:44 +01:00
for location in locations:
Crop = pool.get('farming.crop')
crops = Crop.search([
('location', '=', location.id),
('end_date', '>=', date_end),
('activities', '!=', None)
])
2022-11-15 17:54:02 +01:00
2022-11-10 16:24:59 +01:00
supplies_list = []
2022-11-15 17:54:02 +01:00
2022-11-01 21:16:44 +01:00
for crop in crops:
2022-11-10 16:24:59 +01:00
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)
2022-11-15 17:54:02 +01:00
supplies_list = [{'supply': spl} for spl in supplies_list]
2022-11-10 16:24:59 +01:00
crop_list = []
for crop in crops:
2022-11-15 17:54:02 +01:00
2022-11-10 16:24:59 +01:00
for activity in crop.activities:
2022-11-01 21:16:44 +01:00
activity_week = activity.planned_date.isocalendar()[1]
2022-11-10 16:24:59 +01:00
supply_info_list = []
2022-11-01 21:16:44 +01:00
for week in week_list:
2022-11-10 16:24:59 +01:00
spl = {
'quantity': '',
'unit': '',
2022-11-01 21:16:44 +01:00
}
2022-11-10 16:24:59 +01:00
supply_info_list.append(spl)
2022-12-18 05:08:11 +01:00
2022-11-10 16:24:59 +01:00
for supply in activity.supplies:
for index, week in enumerate(week_list):
week_number = week['number']
2022-11-15 17:54:02 +01:00
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
2022-11-10 16:24:59 +01:00
suply_crop = {
'crop': crop.number,
'info': supply_info_list
}
crop_list.append(suply_crop)
2022-11-15 17:54:02 +01:00
report_context['weeks'] = week_list
report_context['supplies'] = supplies_list
2022-11-01 21:16:44 +01:00
report_context['company'] = Company(data['company'])
report_context['date_start'] = date_start
report_context['date_end'] = date_end
return report_context
2022-11-15 17:54:02 +01:00
2022-11-01 21:16:44 +01:00
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")
2022-11-10 16:24:59 +01:00
location = fields.Many2One('farming.location', 'Location')
2022-11-15 17:54:02 +01:00
2022-11-01 21:16:44 +01:00
@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'
2022-11-15 17:54:02 +01:00
start = StateView(
'farming.crop_forecast_week.start',
2022-11-01 21:16:44 +01:00
'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 = {
2022-11-15 17:54:02 +01:00
# 'crop': self.start.crop.id,
2022-11-01 21:16:44 +01:00
'company': self.start.company.id,
'start_date': self.start.start_date,
2022-11-10 16:24:59 +01:00
'location': self.start.location.id if self.start.location else self.start.location,
2022-11-15 17:54:02 +01:00
# 'end_date': self.start.end_date,
2022-11-01 21:16:44 +01:00
}
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)
2022-11-05 18:02:35 +01:00
calculate_month = data['start_date'] + timedelta(days=30)
date_end = date(data['start_date'].year, calculate_month.month, 28)
2022-11-01 21:16:44 +01:00
week_list = []
2022-11-10 16:24:59 +01:00
date_calculate_week = date_start
2022-11-01 21:16:44 +01:00
for w in range(13):
week_data = {
'number': date_calculate_week.isocalendar()[1],
'range': date_calculate_week
}
week_list.append(week_data)
2022-11-15 17:54:02 +01:00
date_calculate_week = date_calculate_week + timedelta(days=7)
2022-11-01 21:16:44 +01:00
Location = pool.get('farming.location')
2022-11-10 16:24:59 +01:00
if data['location']:
locations = Location.search([('id', '=', data['location'])])
2022-11-15 17:54:02 +01:00
else:
2022-11-10 16:24:59 +01:00
locations = Location.search([('name', '!=', '')])
2022-11-01 21:16:44 +01:00
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 = []
2022-11-15 17:54:02 +01:00
total_list = [0 for x in range(13)]
2022-11-01 21:16:44 +01:00
for crop in crops:
crop_report = {
'lots': []
}
2022-11-15 17:54:02 +01:00
lots_report = []
2022-11-01 21:16:44 +01:00
for lot in crop.lots:
beds = ''
for bed in lot.beds:
2022-11-15 17:54:02 +01:00
beds = beds + '-' + bed.bed.number
2022-11-01 21:16:44 +01:00
lot_dict = {
2022-11-05 18:02:35 +01:00
'number': crop.number,
2022-11-01 21:16:44 +01:00
'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])
2022-11-15 17:54:02 +01:00
for lot_report in lots_report:
2022-11-01 21:16:44 +01:00
stage_list = []
for stage in stages:
stage_week = stage.effective_date.isocalendar()[1]
for week in week_list:
act = {
2022-11-15 23:54:25 +01:00
'name': '',
'production_rate': 0
}
2022-11-01 21:16:44 +01:00
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,
2022-11-15 17:54:02 +01:00
'production_rate': int(
stage.production_rate * lot_report['total_plants'] * crop.production_rate * .01)
2022-11-01 21:16:44 +01:00
}
2022-11-10 21:55:00 +01:00
total_list[index-1] = int(total_list[index-1] + (stage.production_rate * lot_report['total_plants'] * crop.production_rate * .01))
2022-11-01 21:16:44 +01:00
break
lot_report['stages'] = stage_list
2022-11-15 17:54:02 +01:00
2022-11-01 21:16:44 +01:00
crop_list.append(crop_report)
2022-11-15 17:54:02 +01:00
location_list.append({'name': location.name, 'crops': crop_list, 'total_time': total_list})
2022-11-01 21:16:44 +01:00
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
2022-11-04 17:16:51 +01:00
return report_context
2022-12-15 19:49:07 +01:00
class FarmingCropReport(CompanyReport):
__name__ = 'farming.crop'