440 lines
15 KiB
Python
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
|