From 0e3f5ffd20b4b3ddc10dd47b9a438e5b32fbf3d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 24 Sep 2012 22:17:38 +0200 Subject: [PATCH] Active Record --- __init__.py | 15 ++ account.py | 13 +- product.py | 197 +++++++++---------- stock.py | 174 ++++++++-------- tests/scenario_account_stock_continental.rst | 4 +- 5 files changed, 206 insertions(+), 197 deletions(-) diff --git a/__init__.py b/__init__.py index 2143f8868c..2d71d9861f 100644 --- a/__init__.py +++ b/__init__.py @@ -1,6 +1,21 @@ #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 trytond.pool import Pool from .stock import * from .product import * from .account import * + + +def register(): + Pool.register( + Move, + Category, + Template, + Configuration, + UpdateCostPriceAsk, + UpdateCostPriceShowMove, + module='account_stock_continental', type_='model') + Pool.register( + UpdateCostPrice, + module='account_stock_continental', type_='wizard') diff --git a/account.py b/account.py index 4d677fad21..4292981247 100644 --- a/account.py +++ b/account.py @@ -1,12 +1,15 @@ #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 trytond.model import Model, fields +from trytond.model import fields from trytond.pyson import Eval, Bool, Get +from trytond.pool import PoolMeta + +__all__ = ['Configuration'] +__metaclass__ = PoolMeta -class Configuration(Model): - _name = 'account.configuration' - +class Configuration: + __name__ = 'account.configuration' stock_journal = fields.Property(fields.Many2One( 'account.journal', 'Stock Journal', states={ @@ -16,5 +19,3 @@ class Configuration(Model): 'account.account', 'Cost Price Counterpart Account', domain=[ ('company', 'in', [Get(Eval('context', {}), 'company'), None]), ])) - -Configuration() diff --git a/product.py b/product.py index bd815ca38b..2b7939f51a 100644 --- a/product.py +++ b/product.py @@ -1,17 +1,19 @@ #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 decimal import Decimal -from trytond.model import ModelView, ModelSQL, fields +from trytond.model import ModelView, fields from trytond.wizard import Wizard, StateView, StateTransition, Button from trytond.pyson import Eval, Get from trytond.transaction import Transaction -from trytond.pool import Pool +from trytond.pool import Pool, PoolMeta + +__all__ = ['Category', 'Template', 'UpdateCostPriceAsk', + 'UpdateCostPriceShowMove', 'UpdateCostPrice'] +__metaclass__ = PoolMeta -class Category(ModelSQL, ModelView): - _name = 'product.category' - +class Category: + __name__ = 'product.category' account_stock = fields.Property(fields.Many2One('account.account', 'Account Stock', domain=[ ('kind', '=', 'stock'), @@ -61,12 +63,9 @@ class Category(ModelSQL, ModelView): account_stock_lost_found_used = fields.Function(fields.Many2One( 'account.account', 'Account Stock Lost and Found'), 'get_account') -Category() - - -class Template(ModelSQL, ModelView): - _name = 'product.template' +class Template: + __name__ = 'product.template' account_stock = fields.Property(fields.Many2One('account.account', 'Account Stock', domain=[ @@ -140,33 +139,26 @@ class Template(ModelSQL, ModelView): account_stock_lost_found_used = fields.Function(fields.Many2One( 'account.account', 'Account Stock Lost and Found'), 'get_account') - def __init__(self): - super(Template, self).__init__() - self.cost_price = copy.copy(self.cost_price) - self.cost_price.states = copy.copy(self.cost_price.states) - self.cost_price.states['readonly'] = ( - self.cost_price.states.get('readonly', False) + @classmethod + def __setup__(cls): + super(Template, cls).__setup__() + cls.cost_price.states['readonly'] = ( + cls.cost_price.states.get('readonly', False) | ((Eval('type', 'goods') == 'goods') & (Eval('id', -1) >= 0))) - self.cost_price.depends = copy.copy(self.cost_price.depends) - if 'type' not in self.cost_price.depends: - self.cost_price.depends.append('type') - self._reset_columns() - -Template() + if 'type' not in cls.cost_price.depends: + cls.cost_price.depends.append('type') class UpdateCostPriceAsk(ModelView): 'Update Cost Price Ask' - _name = 'product.update_cost_price.ask' + __name__ = 'product.update_cost_price.ask' product = fields.Many2One('product.product', 'Product', readonly=True) cost_price = fields.Numeric('Cost Price', required=True, digits=(16, 4)) -UpdateCostPriceAsk() - class UpdateCostPriceShowMove(ModelView): 'Update Cost Price Show Move' - _name = 'product.update_cost_price.show_move' + __name__ = 'product.update_cost_price.show_move' price_difference = fields.Numeric('Price Difference', readonly=True, digits=(16, 4)) amount = fields.Numeric('Amount', readonly=True, @@ -183,12 +175,10 @@ class UpdateCostPriceShowMove(ModelView): depends=['company', 'stock_account'], required=True) description = fields.Char('Description', required=True) -UpdateCostPriceShowMove() - class UpdateCostPrice(Wizard): 'Update Cost Price' - _name = 'product.update_cost_price' + __name__ = 'product.update_cost_price' start_state = 'ask_price' ask_price = StateView('product.update_cost_price.ask', 'account_stock_continental.update_cost_price_ask_form', [ @@ -204,116 +194,117 @@ class UpdateCostPrice(Wizard): create_move = StateTransition() update_price = StateTransition() - def __init__(self): - super(UpdateCostPrice, self).__init__() - self._error_messages.update({ + @classmethod + def __setup__(cls): + super(UpdateCostPrice, cls).__setup__() + cls._error_messages.update({ 'same_account': 'The stock account and the counterpart can ' 'not be the same account', }) - def default_ask_price(self, session, fields): + def default_ask_price(self, fields): if 'product' in fields: return { 'product': Transaction().context['active_id'], } - def get_quantity(self): + @staticmethod + def get_quantity(): pool = Pool() - date_obj = pool.get('ir.date') - product_obj = pool.get('product.product') - stock_obj = pool.get('stock.location') + Date = pool.get('ir.date') + Product = pool.get('product.product') + Stock = pool.get('stock.location') - locations = stock_obj.search([('type', '=', 'storage')]) - stock_date_end = date_obj.today() - with Transaction().set_context(locations=locations, + locations = Stock.search([('type', '=', 'storage')]) + stock_date_end = Date.today() + with Transaction().set_context(locations=[l.id for l in locations], stock_date_end=stock_date_end): - product = product_obj.browse(Transaction().context['active_id']) - if hasattr(product_obj, 'cost_price'): + product = Product(Transaction().context['active_id']) + if hasattr(Product, 'cost_price'): return product.quantity else: return product.template.quantity - def transition_should_show_move(self, session): + def transition_should_show_move(self): if self.get_quantity() != 0: return 'show_move' return 'update_price' - def default_show_move(self, session, fields): + def default_show_move(self, fields): pool = Pool() - currency_obj = pool.get('currency.currency') - user_obj = pool.get('res.user') - accountconf_obj = pool.get('account.configuration') - product_obj = pool.get('product.product') + User = pool.get('res.user') + AccountConfiguration = pool.get('account.configuration') + Product = pool.get('product.product') - product = product_obj.browse(Transaction().context['active_id']) - price_diff = (session.ask_price.cost_price + product = Product(Transaction().context['active_id']) + price_diff = (self.ask_price.cost_price - product.cost_price) - user = user_obj.browse(Transaction().user) - amount = currency_obj.round(user.company.currency, + user = User(Transaction().user) + amount = user.company.currency.round( Decimal(str(self.get_quantity())) * price_diff) - stock_account = product.account_stock_used.id - config = accountconf_obj.browse(1) - stock_journal = config.stock_journal.id - counterpart = (config.cost_price_counterpart_account.id if + stock_account_id = product.account_stock_used.id + config = AccountConfiguration(1) + stock_journal_id = config.stock_journal.id + counterpart_id = (config.cost_price_counterpart_account.id if config.cost_price_counterpart_account else None) return { - 'journal': stock_journal, + 'journal': stock_journal_id, 'amount': amount, 'price_difference': price_diff, - 'stock_account': stock_account, - 'counterpart': counterpart, + 'stock_account': stock_account_id, + 'counterpart': counterpart_id, 'currency_digits': user.company.currency.digits, } - def get_move_lines(self, session): - amount = session.show_move.amount - return [{ - 'name': session.ask_price.product.name, - 'debit': amount if amount > 0 else 0, - 'credit': -amount if amount < 0 else 0, - 'account': session.show_move.stock_account.id, - }, - { - 'name': session.ask_price.product.name, - 'debit': -amount if amount < 0 else 0, - 'credit': amount if amount > 0 else 0, - 'account': session.show_move.counterpart.id, - }, + def get_move_lines(self): + Line = Pool.get('account.move.line') + amount = self.show_move.amount + return [Line( + name=self.ask_price.product.name, + debit=amount if amount > 0 else 0, + credit=-amount if amount < 0 else 0, + account=self.show_move.stock_account, + ), + Line( + name=self.ask_price.product.name, + debit=-amount if amount < 0 else 0, + credit=amount if amount > 0 else 0, + account=self.show_move.counterpart, + ), ] - def get_move(self, session): + def get_move(self): pool = Pool() - date_obj = pool.get('ir.date') - period_obj = pool.get('account.period') - user_obj = pool.get('res.user') + Date = pool.get('ir.date') + Period = pool.get('account.period') + User = pool.get('res.user') + Move = pool.get('account.move') - user = user_obj.browse(Transaction().user) - period_id = period_obj.find(user.company.id) - return { - 'name': session.show_move.description, - 'period': period_id, - 'journal': session.show_move.journal.id, - 'date': date_obj.today(), - 'lines': [('create', l) - for l in self.get_move_lines(session)], - } - - def transition_create_move(self, session): - move_obj = Pool().get('account.move') - - if session.show_move.counterpart == session.show_move.stock_account: - self.raise_user_error('same_account') - move_data = self.get_move(session) + user = User(Transaction().user) + period_id = Period.find(user.company.id) with Transaction().set_user(0, set_context=True): - move_id = move_obj.create(move_data) - move_obj.post([move_id]) + return Move( + name=self.show_move.description, + period=period_id, + journal=self.show_move.journal, + date=Date.today(), + lines=self.get_move_lines(), + ) + + def transition_create_move(self): + Move = Pool().get('account.move') + + if self.show_move.counterpart == self.show_move.stock_account: + self.raise_user_error('same_account') + move = self.get_move() + move.save() + with Transaction().set_user(0, set_context=True): + Move.post([move]) return 'update_price' - def transition_update_price(self, session): - product_obj = Pool().get('product.product') - product_obj.write(session.ask_price.product.id, { - 'cost_price': session.ask_price.cost_price, + def transition_update_price(self): + Product = Pool().get('product.product') + Product.write([self.ask_price.product], { + 'cost_price': self.ask_price.cost_price, }) return 'end' - -UpdateCostPrice() diff --git a/stock.py b/stock.py index 8433ed732b..f76e886c8e 100644 --- a/stock.py +++ b/stock.py @@ -1,94 +1,102 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. from decimal import Decimal -from trytond.model import Model, fields -from trytond.pool import Pool + +from trytond.model import fields +from trytond.pool import Pool, PoolMeta from trytond.transaction import Transaction +__all__ = ['Move'] +__metaclass__ = PoolMeta -class Move(Model): - _name = 'stock.move' +class Move: + __name__ = 'stock.move' account_move = fields.Many2One('account.move', 'Account Move', readonly=True) - def _get_account_stock_move_lines(self, move, type_): + def _get_account_stock_move_lines(self, type_): ''' - Return move line values for stock move + Return move lines for stock move ''' - uom_obj = Pool().get('product.uom') - currency_obj = Pool().get('currency.currency') + pool = Pool() + Uom = pool.get('product.uom') + AccountMoveLine = pool.get('account.move.line') assert type_.startswith('in_') or type_.startswith('out_'), \ 'wrong type' - move_line = { - 'name': move.rec_name, - } + with Transaction().set_user(0, set_context=True): + move_line = AccountMoveLine(name=self.rec_name) if (type_.endswith('supplier') - and move.product.cost_price_method != 'fixed'): - unit_price = move.unit_price + and self.product.cost_price_method != 'fixed'): + unit_price = self.unit_price else: - unit_price = uom_obj.compute_price(move.product.default_uom, - move.cost_price, move.uom) - amount = currency_obj.round(move.company.currency, - Decimal(str(move.quantity)) * unit_price) + unit_price = Uom.compute_price(self.product.default_uom, + self.cost_price, self.uom) + amount = self.company.currency.round( + Decimal(str(self.quantity)) * unit_price) if type_.startswith('in_'): - move_line['debit'] = Decimal('0.0') - move_line['credit'] = amount + move_line.debit = Decimal('0.0') + move_line.credit = amount account_type = type_[3:] else: - move_line['debit'] = amount - move_line['credit'] = Decimal('0.0') + move_line.debit = amount + move_line.credit = Decimal('0.0') account_type = type_[4:] - move_line['account'] = getattr(move.product, - 'account_stock_%s_used' % account_type).id + move_line.account = getattr(self.product, + 'account_stock_%s_used' % account_type) return [move_line] - def _get_account_stock_move_line(self, move, amount): + def _get_account_stock_move_line(self, amount): ''' Return counterpart move line value for stock move ''' - move_line = { - 'name': move.rec_name, - 'account': move.product.account_stock_used.id, - } + pool = Pool() + AccountMoveLine = pool.get('account.move.line') + with Transaction().set_user(0, set_context=True): + move_line = AccountMoveLine( + name=self.rec_name, + account=self.product.account_stock_used, + ) if not amount: return if amount >= Decimal('0.0'): - move_line['debit'] = Decimal('0.0') - move_line['credit'] = amount + move_line.debit = Decimal('0.0') + move_line.credit = amount else: - move_line['debit'] = - amount - move_line['credit'] = Decimal('0.0') + move_line.debit = - amount + move_line.credit = Decimal('0.0') return move_line - def _get_account_stock_move(self, move, move_lines): + def _get_account_stock_move(self, move_lines): ''' Return account move value for stock move ''' - date_obj = Pool().get('ir.date') - period_obj = Pool().get('account.period') - account_configuration_obj = Pool().get('account.configuration') + pool = Pool() + Date = pool.get('ir.date') + Period = pool.get('account.period') + AccountConfiguration = pool.get('account.configuration') + AccountMove = pool.get('account.move') - date = move.effective_date or date_obj.today() - period_id = period_obj.find(move.company.id, date=date) - account_configuration = account_configuration_obj.browse(1) - journal_id = account_configuration.stock_journal.id - return { - 'journal': journal_id, - 'period': period_id, - 'date': date, - 'lines': [('create', line) for line in move_lines], - } + date = self.effective_date or Date.today() + period_id = Period.find(self.company.id, date=date) + account_configuration = AccountConfiguration(1) + with Transaction().set_user(0, set_context=True): + return AccountMove( + journal=account_configuration.stock_journal, + period=period_id, + date=date, + lines=move_lines, + ) - def _get_account_stock_move_type(self, move): + def _get_account_stock_move_type(self): ''' Get account move type ''' - type_ = (move.from_location.type, move.to_location.type) + type_ = (self.from_location.type, self.to_location.type) if type_ == ('supplier', 'storage'): return 'in_supplier' elif type_ == ('storage', 'supplier'): @@ -106,70 +114,64 @@ class Move(Model): elif type_ == ('customer', 'supplier'): return 'customer_supplier' - def _create_account_stock_move(self, move): + def _create_account_stock_move(self): ''' Create account move for stock move ''' - account_move_obj = Pool().get('account.move') - if move.product.type != 'goods': + AccountMove = Pool().get('account.move') + if self.product.type != 'goods': return - type_ = self._get_account_stock_move_type(move) + type_ = self._get_account_stock_move_type() if not type_: return - assert not move.account_move, 'account move field not empty' + assert not self.account_move, 'account move field not empty' if type_ == 'supplier_customer': - account_move_lines = self._get_account_stock_move_lines(move, + account_move_lines = self._get_account_stock_move_lines( 'in_supplier') - account_move_lines.extend(self._get_account_stock_move_lines(move, + account_move_lines.extend(self._get_account_stock_move_lines( 'out_customer')) elif type_ == 'customer_supplier': - account_move_lines = self._get_account_stock_move_lines(move, + account_move_lines = self._get_account_stock_move_lines( 'in_customer') - account_move_lines.extend(self._get_account_stock_move_lines(move, + account_move_lines.extend(self._get_account_stock_move_lines( 'out_supplier')) else: - account_move_lines = self._get_account_stock_move_lines(move, - type_) + account_move_lines = self._get_account_stock_move_lines(type_) amount = Decimal('0.0') for line in account_move_lines: - amount += line['debit'] - line['credit'] - move_line = self._get_account_stock_move_line(move, amount) + amount += line.debit - line.credit + move_line = self._get_account_stock_move_line(amount) if move_line: account_move_lines.append(move_line) + account_move = self._get_account_stock_move(account_move_lines) + account_move.save() with Transaction().set_user(0, set_context=True): - account_move_id = account_move_obj.create( - self._get_account_stock_move(move, account_move_lines)) - account_move_obj.post(account_move_id) - self.write(move.id, { - 'account_move': account_move_id, - }) - return account_move_id + AccountMove.post([account_move]) + self.write([self], { + 'account_move': account_move.id, + }) + return account_move - def copy(self, ids, default=None): + @classmethod + def copy(cls, moves, default=None): if default is None: default = {} default = default.copy() default.setdefault('account_move', None) - return super(Move, self).copy(ids, default=default) + return super(Move, cls).copy(moves, default=default) - def create(self, vals): - new_id = super(Move, self).create(vals) + @classmethod + def create(cls, vals): + move = super(Move, cls).create(vals) if vals.get('state') == 'done': - move = self.browse(new_id) - self._create_account_stock_move(move) - return new_id + move._create_account_stock_move() + return move - def write(self, ids, vals): - res = super(Move, self).write(ids, vals) + @classmethod + def write(cls, moves, vals): + super(Move, cls).write(moves, vals) if vals.get('state') == 'done': - if isinstance(ids, (int, long)): - move = self.browse(ids) - self._create_account_stock_move(move) - else: - for move in self.browse(ids): - self._create_account_stock_move(move) - return res - -Move() + for move in moves: + move._create_account_stock_move() diff --git a/tests/scenario_account_stock_continental.rst b/tests/scenario_account_stock_continental.rst index a3c2ed3ac4..25e2704f91 100644 --- a/tests/scenario_account_stock_continental.rst +++ b/tests/scenario_account_stock_continental.rst @@ -154,7 +154,7 @@ Create product:: >>> product.account_stock_customer = stock_customer >>> product.account_stock_lost_found = stock_lost_found >>> product.save() - >>> product_average = Product(Product.copy(product.id, config.context)) + >>> product_average = Product(Product.copy([product.id], config.context)[0]) >>> product_average.cost_price_method = 'average' >>> product_average.save() @@ -312,7 +312,7 @@ Create an Inventory:: >>> inventory = Inventory() >>> inventory.location = storage >>> inventory.save() - >>> Inventory.complete_lines(inventory.id, config.context) + >>> Inventory.complete_lines([inventory.id], config.context) >>> inventory_line = inventory.lines[0] >>> inventory_line.quantity = 1.0 >>> inventory_line = inventory.lines[1]