2021-11-02 18:25:11 +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.
|
|
|
|
from __future__ import with_statement
|
|
|
|
from decimal import Decimal
|
|
|
|
from datetime import date
|
|
|
|
|
|
|
|
from trytond.model import Workflow, ModelView, ModelSQL, fields
|
|
|
|
from trytond.pyson import Eval, If, In, Get
|
|
|
|
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
|
|
|
|
from trytond.wizard import (Wizard, StateReport, StateView,
|
|
|
|
Button, StateTransition)
|
2021-11-02 18:25:11 +01:00
|
|
|
|
|
|
|
STATES = {
|
|
|
|
'readonly': (Eval('state') != 'draft'),
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-16 06:50:49 +02:00
|
|
|
class FarmingVariety(ModelSQL, ModelView):
|
|
|
|
'Farming Variety'
|
|
|
|
__name__ = 'farming.variety'
|
|
|
|
product = fields.Many2One('product.product', 'Product', required=True)
|
|
|
|
standard_cycle = fields.Integer('Standard Cycle', required=True,
|
|
|
|
help='In weeks')
|
|
|
|
activities = fields.One2Many('farming.variety.activity', 'variety',
|
|
|
|
'Activities')
|
|
|
|
|
|
|
|
def get_rec_name(self, name=None):
|
|
|
|
return self.product.template.name
|
|
|
|
|
|
|
|
|
|
|
|
class FarmingVarietyActivity(ModelSQL, ModelView):
|
|
|
|
'Farming Variety Activity'
|
|
|
|
__name__ = 'farming.variety.activity'
|
|
|
|
variety = fields.Many2One('farming.variety', 'Variety', required=True)
|
|
|
|
sequence = fields.Integer('Sequence', required=True)
|
|
|
|
kind = fields.Many2One('farming.activity.kind', 'Kind', required=True)
|
|
|
|
time_of_realization = fields.Integer('Time Of Realization', required=True,
|
|
|
|
help='In weeks')
|
|
|
|
|
|
|
|
|
2021-11-02 18:25:11 +01:00
|
|
|
class Kind(ModelSQL, ModelView):
|
|
|
|
"Kind"
|
|
|
|
__name__ = "farming.activity.kind"
|
|
|
|
name = fields.Char('Name', required=True)
|
|
|
|
activity_time = fields.Float('Act. Time', required=True)
|
|
|
|
|
|
|
|
|
2022-08-16 06:50:49 +02:00
|
|
|
class FarmingCrop(Workflow, ModelSQL, ModelView):
|
|
|
|
'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'])
|
|
|
|
field_size = fields.Float('Field Size', states=STATES, select=True, help='In hectares')
|
|
|
|
quantity_produced = fields.Float('Quantity Produced', states=STATES)
|
|
|
|
quantity_produced_uom = fields.Many2One('product.uom', 'Quantity UoM', states=STATES)
|
2022-08-16 06:50:49 +02:00
|
|
|
lots = fields.One2Many('farming.crop.lot', 'crop', 'Lots',
|
2022-06-30 04:08:43 +02:00
|
|
|
states=STATES)
|
2022-08-16 06:50:49 +02:00
|
|
|
seed = fields.Char('Seed', states=STATES)
|
2021-11-02 18:25:11 +01:00
|
|
|
description = fields.Char('Description', states=STATES)
|
|
|
|
notes = fields.Text('Notes', states=STATES)
|
|
|
|
state = fields.Selection([
|
|
|
|
('draft', 'Draft'),
|
|
|
|
('production', 'Production'),
|
|
|
|
('finished', 'Finished'),
|
|
|
|
('cancelled', 'Cancelled'),
|
|
|
|
], 'State', readonly=True, required=True)
|
|
|
|
# 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):
|
2022-08-16 06:50:49 +02:00
|
|
|
super(FarmingCrop, 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',
|
|
|
|
},
|
|
|
|
'production': {
|
|
|
|
'invisible': Eval('state').in_(['cancelled', 'finished']),
|
|
|
|
},
|
|
|
|
'finished': {
|
|
|
|
'invisible': Eval('state') != 'production',
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
@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()
|
|
|
|
|
|
|
|
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_production_time(self, name):
|
|
|
|
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
|
|
|
|
|
|
|
|
|
2022-08-16 06:50:49 +02:00
|
|
|
class FarmingCropLot(ModelSQL, ModelView):
|
|
|
|
'Farming Crop Lot'
|
|
|
|
__name__ = 'farming.crop.lot'
|
2022-06-30 04:08:43 +02:00
|
|
|
_rec_name = 'lot'
|
2022-08-16 06:50:49 +02:00
|
|
|
crop = fields.Many2One('farming.crop', 'Crop', required=True)
|
|
|
|
number = fields.Char('Number', required=True)
|
|
|
|
cycle = fields.Float('Cycle', digits=(16, 2), help="In weeks")
|
|
|
|
production_rate = fields.Float('Production Rate', digits=(16, 2),
|
|
|
|
help="In units")
|
|
|
|
beds = fields.One2Many('farming.crop.lot.bed', 'lot', 'Beds')
|
|
|
|
activities = fields.One2Many('farming.activity', 'lot', 'Activities')
|
|
|
|
total_plants = fields.Function(fields.Integer('Total Plants'),
|
|
|
|
'get_total_plants')
|
|
|
|
|
|
|
|
def get_total_plants(self, name):
|
|
|
|
res = []
|
|
|
|
for bed in self.beds:
|
|
|
|
res.append(bed.quantity_plants)
|
|
|
|
return sum(res)
|
|
|
|
|
|
|
|
|
|
|
|
class FarmingCropLotBed(ModelSQL, ModelView):
|
|
|
|
'Crop Lot Bed'
|
|
|
|
__name__ = 'farming.crop.lot.bed'
|
|
|
|
_rec_name = 'code'
|
|
|
|
lot = fields.Many2One('farming.crop.lot', 'Lot', required=True)
|
|
|
|
code = fields.Char('Code', required=True)
|
2022-06-30 04:08:43 +02:00
|
|
|
quantity_plants = fields.Integer('Quantity Plants')
|
2022-08-16 06:50:49 +02:00
|
|
|
unit = fields.Many2One('product.uom', 'Unit')
|
2022-07-15 09:01:45 +02:00
|
|
|
production_rate = fields.Float('Production Rate', digits=(16, 2),
|
2022-08-16 06:50:49 +02:00
|
|
|
help="In the unit of measure")
|
2022-06-30 04:08:43 +02: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)
|
2022-08-16 06:50:49 +02:00
|
|
|
# works = fields.One2Many('farming.crop.work', 'activity', 'Works',
|
|
|
|
# states=STATES)
|
2021-11-02 18:25:11 +01:00
|
|
|
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)
|
|
|
|
date_ = fields.Date("Date", required=True)
|
|
|
|
# storage = fields.Many2One('stock.location', 'Location', required=True,
|
|
|
|
# domain=[('type', '=', 'storage')])
|
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'
|
|
|
|
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,
|
|
|
|
'date': self.start.date_,
|
|
|
|
'storage': self.start.storage.id,
|
|
|
|
}
|
|
|
|
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()
|
|
|
|
Company = pool.get('company.company')
|
|
|
|
Production = pool.get('production')
|
|
|
|
Purchase = pool.get('purchase.purchase')
|
|
|
|
Product = pool.get('product.product')
|
|
|
|
|
|
|
|
default_dates = {}
|
|
|
|
for i in range(6):
|
|
|
|
d = data['date'] + timedelta(days=i)
|
|
|
|
default_dates[str(d)] = {
|
|
|
|
'stock': 0,
|
|
|
|
'in': 0,
|
|
|
|
'pcc': 0,
|
|
|
|
'balance': 0,
|
|
|
|
}
|
|
|
|
report_context['date' + str(1+i)] = str(d)
|
|
|
|
|
|
|
|
last_date = data['date'] + timedelta(days=5)
|
|
|
|
productions = Production.search([
|
|
|
|
('company', '=', data['company']),
|
|
|
|
('planned_date', '>=', data['date']),
|
|
|
|
('planned_date', '<=', last_date),
|
|
|
|
], order=[('planned_date', 'ASC')])
|
|
|
|
|
|
|
|
products = {}
|
|
|
|
for pcc in productions:
|
|
|
|
for inp in pcc.inputs:
|
|
|
|
if not inp.product.template.purchasable:
|
|
|
|
continue
|
|
|
|
if inp.product not in products.keys():
|
|
|
|
products[inp.product] = copy.deepcopy(default_dates)
|
|
|
|
products[inp.product]['name'] = inp.product.rec_name
|
|
|
|
products[inp.product]['uom'] = inp.product.template.default_uom.symbol
|
|
|
|
products[inp.product][str(pcc.planned_date)]['pcc'] += inp.quantity
|
|
|
|
|
|
|
|
purchases = Purchase.search([
|
|
|
|
('company', '=', data['company']),
|
|
|
|
('delivery_date', '>=', data['date']),
|
|
|
|
('delivery_date', '<=', last_date),
|
|
|
|
('state', 'in', ['quotation', 'processing', 'done', 'confirmed']),
|
|
|
|
], order=[('delivery_date', 'ASC')])
|
|
|
|
|
|
|
|
for pch in purchases:
|
|
|
|
for line in pch.lines:
|
|
|
|
if line.product not in products.keys():
|
|
|
|
products[line.product] = copy.deepcopy(default_dates)
|
|
|
|
products[line.product]['name'] = line.product.rec_name
|
|
|
|
products[line.product]['uom'] = line.product.template.default_uom.symbol
|
|
|
|
|
|
|
|
products[line.product][str(pch.delivery_date)]['in'] += line.quantity
|
|
|
|
|
|
|
|
date_ = data['date'] - timedelta(days=1)
|
|
|
|
ctx = {
|
|
|
|
'locations': [data['storage']],
|
|
|
|
'stock_date_end': date_,
|
|
|
|
}
|
|
|
|
|
|
|
|
def get_balance(qty, product_date):
|
|
|
|
res = qty + product_date['in'] - product_date['pcc']
|
|
|
|
return res
|
|
|
|
|
|
|
|
# Search stock of products
|
|
|
|
product_ids = [pd.id for pd in products.keys()]
|
|
|
|
with Transaction().set_context(ctx):
|
|
|
|
_products = Product.search([
|
|
|
|
('id', 'in', product_ids),
|
|
|
|
('template.purchasable', '=', True),
|
|
|
|
], order=[('template.name', 'ASC')]
|
|
|
|
)
|
|
|
|
|
|
|
|
for pt in _products:
|
|
|
|
stock = pt.quantity
|
|
|
|
for dt in default_dates.keys():
|
|
|
|
product_date = products[pt][str(dt)]
|
|
|
|
product_date['stock'] = stock
|
|
|
|
value = get_balance(stock, product_date)
|
|
|
|
product_date['balance'] = value
|
|
|
|
stock = value
|
|
|
|
|
|
|
|
report_context['records'] = products.values()
|
|
|
|
report_context['date'] = data['date']
|
|
|
|
report_context['company'] = Company(data['company'])
|
|
|
|
return report_context
|
|
|
|
|
|
|
|
|
|
|
|
# class FarmingWork(ModelSQL, ModelView):
|
|
|
|
# 'Farming Work'
|
|
|
|
# __name__ = 'farming.crop.work'
|
|
|
|
# sequence = fields.Integer('Sequence', select=True)
|
|
|
|
# activity = fields.Many2One('farming.activity', 'Activity', select=True,
|
|
|
|
# ondelete='CASCADE', required=True)
|
|
|
|
# method = fields.Selection([
|
|
|
|
# ('by_day', 'By Day'),
|
|
|
|
# ('by_productivity', 'By Productivity'),
|
|
|
|
# ], 'Method', required=True)
|
|
|
|
# quantity = fields.Float('Quantity', required=True)
|
|
|
|
# unit_price = fields.Numeric('Unit Price', digits=(16, 2), required=True)
|
|
|
|
# amount = fields.Function(fields.Numeric('Amount', digits=(16, 2)), 'get_amount')
|
|
|
|
# employee = fields.Many2One('company.employee', 'Employee')
|
|
|
|
#
|
|
|
|
# @classmethod
|
|
|
|
# def __setup__(cls):
|
|
|
|
# super(FarmingWork, cls).__setup__()
|
|
|
|
#
|
|
|
|
# def get_amount(self, name):
|
|
|
|
# res = 0
|
|
|
|
# if self.quantity and self.unit_price:
|
|
|
|
# res = Decimal(self.quantity) * self.unit_price
|
|
|
|
# return res
|
|
|
|
#
|
|
|
|
# @staticmethod
|
|
|
|
# def default_method():
|
|
|
|
# return 'by_day'
|
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)
|