trytonpsk-farming/exportation.py

440 lines
15 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.
import copy
from datetime import date
from decimal import Decimal
from trytond.model import Workflow, ModelView, ModelSQL, fields
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.wizard import Wizard, StateView, StateTransition, Button
from trytond.modules.company import CompanyReport
STATES = {
'readonly': (Eval('state') != 'draft'),
}
class DEX(Workflow, ModelSQL, ModelView):
"DEX"
__name__ = "exportation.dex"
_rec_name = 'number'
number = fields.Char('Number', states=STATES)
issue_date = fields.Date('Issue Date', required=True, states=STATES)
company = fields.Many2One('company.company', 'Company', required=True,
states=STATES)
customer = fields.Many2One('party.party', 'Customer', select=True,
states=STATES, required=True)
invoice = fields.Many2One('account.invoice', 'invoice', states=STATES, domain=[
('party', '=', Eval('customer')),
('company', '=', Eval('company')),
])
invoice_exchange_rate = fields.Float('Invoice Exchange Rate', digits=(16, 2))
currency = fields.Many2One('currency.currency', 'Currency', states=STATES,
required=True)
invoiced_amount = fields.Numeric('Invoiced Amount', digits=(16, 2),
states=STATES)
description = fields.Char('Description', states=STATES)
lines = fields.One2Many('exportation.dex.line', 'dex', 'Lines')
state = fields.Selection([
('draft', 'Draft'),
('processing', 'Processing'),
('finished', 'Finished'),
], 'State', readonly=True, select=True)
amount_receivable = fields.Function(fields.Numeric('Amount Receivable',
digits=(16, 2), readonly=True), 'get_amounts')
amount_for_exchange = fields.Function(fields.Numeric('Amount For Exchange',
digits=(16, 2), readonly=True), 'get_amounts')
conversion_invoiced_amount = fields.Function(fields.Numeric(
'Conversion Invoiced Amount', digits=(16, 2), readonly=True),
'get_conversion_of_invoiced_amount')
@classmethod
def __setup__(cls):
super(DEX, cls).__setup__()
cls._order.insert(0, ('issue_date', 'DESC'))
cls._transitions |= set((
('draft', 'processing'),
('processing', 'finished'),
('processing', 'draft'),
))
cls._buttons.update({
'draft': {
'invisible': Eval('state') != 'processing',
},
'process': {
'invisible': Eval('state') != 'draft',
},
'finish': {
'invisible': True,
},
'refresh': {
'invisible': Eval('state') == 'draft',
'icon': 'tryton-refresh',
'depends': ['state'],
},
})
@staticmethod
def default_issue_date():
return date.today()
@staticmethod
def default_state():
return 'draft'
@staticmethod
def default_company():
return Transaction().context.get('company') or False
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('processing')
def process(cls, records):
pass
@classmethod
@ModelView.button
@Workflow.transition('finished')
def finish(cls, records):
pass
@classmethod
@ModelView.button
def refresh(cls, records):
process = []
finished = []
for record in records:
if record.amount_receivable <= 0:
amount = record.amount_for_exchange
if record.state == 'finished' and not record.currency.is_zero(amount):
process.append(record)
elif record.state == 'processing' and record.currency.is_zero(amount):
finished.append(record)
cls.write(process, {'state': 'processing'})
cls.finish(finished)
@fields.depends('invoice')
def on_change_invoice(self):
if self.invoice:
self.currency = self.invoice.currency
self.invoiced_amount = self.invoice.total_amount
self.on_change_currency()
@fields.depends('currency', 'invoice')
def on_change_currency(self):
if self.currency:
Currency = Pool().get('currency.currency')
ctx = {'date': self.invoice.invoice_date}
with Transaction().set_context(ctx):
rates = Currency.get_rate([self.currency], name=None)
try:
rate = round((1/rates[self.currency.id]), 2)
except:
rate = 0.0
self.invoice_exchange_rate = float(rate)
def get_amounts(self, name):
amount_for_exchange = []
amount_receivable = []
if self.lines:
for line in self.lines:
if line.amount_paid:
amount_receivable.append(line.amount_paid)
elif line.amount_exchanged:
amount_for_exchange.append(line.amount_exchanged)
if name == 'amount_receivable':
return (round(self.invoiced_amount - sum(amount_receivable), 2) if self.invoiced_amount else 0)
else:
return round(sum(amount_receivable) - sum(amount_for_exchange), 2)
def get_conversion_of_invoiced_amount(self, name=None):
res = 0
if self.invoiced_amount and self.invoice_exchange_rate:
res = self.invoiced_amount * Decimal(self.invoice_exchange_rate)
return round(res, 2)
class DEXLine(ModelSQL, ModelView):
"DEX Line"
__name__ = "exportation.dex.line"
dex = fields.Many2One('exportation.dex', 'DEX', required=True)
number = fields.Char('Number')
date = fields.Date('Date')
exchange_rate = fields.Float('Exchange Rate', digits=(16, 2))
amount_paid = fields.Numeric('Amount Paid', digits=(16, 2))
amount_exchanged = fields.Numeric('Amount Exchanged', digits=(16, 2))
balance = fields.Function(fields.Numeric('Balance', digits=(16, 2)),
'get_balance')
trading_line = fields.Many2One('foreign.exchange.trading_line', 'line',
'Trading Line')
amount_local_currency = fields.Function(fields.Numeric(
'Amount Local Currency', digits=(16, 2)), 'get_local_amount')
profit = fields.Function(fields.Numeric('Profit', digits=(16, 2)),
'get_profit')
currency = fields.Function(fields.Many2One('currency.currency', 'Currency'),
'get_currency')
currency_company = fields.Function(fields.Many2One('currency.currency',
'Company'), 'get_currency')
@fields.depends('amount_exchanged', 'amount_paid', 'date')
def on_change_exchage_rate(self):
if (self.amount_exchaged or self.amount_paid) and self.exchange:
Currency = Pool().get('currency.currency')
ctx = {'date': self.date}
with Transaction().set_context(ctx):
rates = Currency.get_rate([self.currency])
try:
rate = round((1/rates[self.currency.id]), 2)
except:
rate = 0.0
self.exchange_rate = rate
def get_balance(self, name=None):
res = []
return sum(res)
def get_local_amount(self, name=None):
res = 0
if self.amount_exchanged and self.exchange_rate:
res = self.amount_exchanged * Decimal(self.exchange_rate)
return res
def get_profit(self, name=None):
res = 0
if self.amount_exchanged and self.amount_local_currency and \
self.dex.invoice_exchange_rate:
res = self.amount_local_currency - (
Decimal(self.dex.invoice_exchange_rate) * self.amount_exchanged
)
return res
def get_currency(self, name=None):
currency = None
if name == 'currency' and self.dex.currency:
currency = self.dex.currency.id
elif name == 'currency_company' and self.dex.company:
currency = self.dex.company.currency.id
return currency
class ForeignExhangeTrading(Workflow, ModelSQL, ModelView):
"Foreign Exhange Trading"
__name__ = "foreign.exchange.trading"
_rec_name = 'number'
bank = fields.Many2One('bank', 'Bank', required=True)
company = fields.Many2One('company.company', 'Company', required=True,
states=STATES)
number = fields.Char('Number', states=STATES)
exchange_date = fields.Date('Exchange Date')
exchange_rate = fields.Float('Exchange Rate')
total_exchange = fields.Function(fields.Numeric('Total Exchange',
digits=(16,2)), 'get_total_exchange')
lines = fields.One2Many('foreign.exchange.trading_line', 'exchange', 'Lines')
state = fields.Selection([
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('cancelled', 'Cancelled'),
], 'State', readonly=True, select=True)
@classmethod
def __setup__(cls):
super(ForeignExhangeTrading, cls).__setup__()
cls._order.insert(0, ('exchange_date', 'DESC'))
cls._buttons.update({
'draft': {
'invisible': Eval('state') != 'confirmed',
},
'confirm': {
'invisible': Eval('state') != 'draft',
},
'cancel': {
'invisible': Eval('state') != 'draft',
},
'select_lines': {
'invisible': Eval('state') != 'draft',
},
})
cls._transitions |= set((
('draft', 'confirmed'),
('draft', 'cancelled'),
('cancelled', 'draft'),
('confirmed', 'draft'),
))
@staticmethod
def default_state():
return 'draft'
@staticmethod
def default_company():
return Transaction().context.get('company') or False
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, records):
cls.remove_trading_dex(records)
@classmethod
@ModelView.button
@Workflow.transition('confirmed')
def confirm(cls, records):
cls.set_tranding_dex(records)
@classmethod
@ModelView.button
@Workflow.transition('cancelled')
def cancel(cls, records):
pass
@classmethod
@ModelView.button_action('farming.wizard_select_lines')
def select_lines(cls, records):
pass
@classmethod
def set_tranding_dex(cls, records):
pool = Pool()
DEXLine = pool.get('exportation.dex.line')
DEX = pool.get('exportation.dex')
dex_to_refresh = []
for record in records:
value = {
'number': record.number,
'date': record.exchange_date,
'exchange_rate': record.exchange_rate,
}
for line in record.lines:
value_ = copy.deepcopy(value)
value_.update({
'amount_exchanged': line.trading_amount,
'dex': line.dex.id,
'trading_line': line.id,
})
dex_to_refresh.append(line.dex)
DEXLine.create([value_])
DEX.refresh(dex_to_refresh)
@classmethod
def remove_trading_dex(cls, records):
pool = Pool()
DEXLine = pool.get('exportation.dex.line')
DEX = pool.get('exportation.dex')
lines_trading = []
for record in records:
for line in record.lines:
lines_trading += [line.id]
lines_to_remove = DEXLine.search([
('trading_line', 'in', lines_trading)
])
dexs = [line.dex for line in lines_to_remove]
DEXLine.delete(lines_to_remove)
DEX.refresh(dexs)
def get_total_exchange(self, name=None):
res = [line.trading_amount for line in self.lines]
return sum(res)
@classmethod
def copy(cls, records, default=None):
if default is None:
default = {}
else:
default = default.copy()
default.setdefault('number', None)
default.setdefault('lines', None)
default.setdefault('exchange_date', None)
return super(ForeignExhangeTrading, cls).copy(records, default=default)
class ForeignExhangeTradingLine(ModelSQL, ModelView):
"Foreign Exhange Trading Line"
__name__ = "foreign.exchange.trading_line"
exchange = fields.Many2One('foreign.exchange.trading', 'Exchange',
required=True)
dex = fields.Many2One('exportation.dex', 'DEX', required=True)
trading_amount = fields.Numeric('Trading Amount', digits=(16, 2))
party = fields.Function(fields.Many2One('party.party', 'Party'),
'get_values_dex')
date_dex = fields.Function(fields.Date('Date Dex'), 'get_values_dex')
@classmethod
def get_values_dex(cls, lines, names):
parties = {}
date_dex = {}
for line in lines:
party = None
date = None
if line.dex:
party = line.dex.customer.id
date = line.dex.issue_date
parties[line.id] = party
date_dex[line.id] = date
result = {
'party': parties,
'date_dex': date_dex,
}
for key in list(result.keys()):
if key not in names:
del result[key]
return result
class SelectLinesAsk(ModelView):
'Select Lines Assistant'
__name__ = 'farming.select_lines.ask'
lines = fields.Many2Many('exportation.dex', None, None, 'Dexs', domain=[
('state', '=', 'processing')
])
class SelectLines(Wizard):
'Select Lines'
__name__ = 'farming.select_lines'
start = StateView(
'farming.select_lines.ask',
'farming.view_search_lines_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Add', 'add_lines', 'tryton-ok', default=True),
])
add_lines = StateTransition()
def transition_add_lines(self):
pool = Pool()
TradingLine = pool.get('foreign.exchange.trading_line')
exchange_id = Transaction().context.get('active_id')
lines_to_create = []
for line in self.start.lines:
value = {
'exchange': exchange_id,
'dex': line.id,
'trading_amount': line.amount_for_exchange
}
lines_to_create.append(value)
TradingLine.create(lines_to_create)
return 'end'
class ForeignExhangeTradingReport(CompanyReport):
'Foreign Exhange Trading Report'
__name__ = 'foreign.exchange.trading_report'
@classmethod
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
report_context['company'] = Transaction().context.get('company')
return report_context