From: Ludovic CHEVALIER Date: Fri, 27 Jan 2017 07:46:06 +0000 (+0100) Subject: [PYTHON] +point of sale move lines analytic account based on shop default analytic... X-Git-Url: http://git.cyclocoop.org/?p=burette%2Fanalytic_point_of_sale.git;a=commitdiff_plain;h=815096298d0b1925488e3a7fbf5794bfa2ceac33 [PYTHON] +point of sale move lines analytic account based on shop default analytic account --- diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..68ac9a6 --- /dev/null +++ b/__init__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# analytic_point_of_sale module for OpenERP, Analytic Point of sale +# Copyright (C) 2017 L'Heureux Cyclage () +# L'Heureux Cyclage +# +# This file is a part of analytic_point_of_sale +# +# analytic_point_of_sale is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or (at +# your option) any later version. +# +# analytic_point_of_sale is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +############################################################################## + +import point_of_sale + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 0000000..55c9ac0 --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# analytic_point_of_sale module for OpenERP, Analytic Point of sale +# Copyright (C) 2017 L'Heureux Cyclage () +# L'Heureux Cyclage +# +# This file is a part of analytic_point_of_sale +# +# analytic_point_of_sale is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or (at +# your option) any later version. +# +# analytic_point_of_sale is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Analytic Point of sale', + 'version': '0.1', + 'category': 'Accounting & Finance', + 'complexity': "normal", + 'description': """ +This module is add analycics features in point of sale +====================================================== + +Functionnalities : + * Use shops analytic account as default for move lines generate from the + point of sale; + * … + """, + 'author': 'L\'Heureux Cyclage', + 'website': 'http://www.heureux-cyclage.org/', + 'depends': [ + 'sale', + 'point_of_sale', + ], + 'init_xml': [ + ], + 'update_xml': [ + ], + 'installable': True, + 'auto_install': False, + 'images': [], +} + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/point_of_sale.py b/point_of_sale.py new file mode 100644 index 0000000..350277c --- /dev/null +++ b/point_of_sale.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# analytic_point_of_sale module for OpenERP, Analytic Point of sale +# Copyright (C) 2017 L'Heureux Cyclage () +# L'Heureux Cyclage +# +# This file is a part of analytic_point_of_sale +# +# analytic_point_of_sale is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, either version 3 of the License, or (at +# your option) any later version. +# +# analytic_point_of_sale is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +############################################################################## + +from openerp.osv import osv +from openerp.osv import orm +from openerp.osv import fields +from openerp.tools.translate import _ + + +class pos_order(orm.Model): + _inherit = 'pos.order' + + def _create_account_move_line(self, cr, uid, ids, session=None, move_id=None, context=None): + # Tricky, via the workflow, we only have one id in the ids variable + """Create a account move line of order grouped by products or not.""" + account_move_obj = self.pool.get('account.move') + account_period_obj = self.pool.get('account.period') + account_tax_obj = self.pool.get('account.tax') + property_obj = self.pool.get('ir.property') + cur_obj = self.pool.get('res.currency') + shop_obj = self.pool.get('sale.shop') + + #session_ids = set(order.session_id for order in self.browse(cr, uid, ids, context=context)) + + if session and not all(session.id == order.session_id.id for order in self.browse(cr, uid, ids, context=context)): + raise osv.except_osv(_('Error!'), _('Selected orders do not have the same session!')) + + grouped_data = {} + have_to_group_by = session and session.config_id.group_by or False + + def compute_tax(amount, tax, line): + if amount > 0: + tax_code_id = tax['base_code_id'] + tax_amount = line.price_subtotal * tax['base_sign'] + else: + tax_code_id = tax['ref_base_code_id'] + tax_amount = line.price_subtotal * tax['ref_base_sign'] + + return (tax_code_id, tax_amount,) + + for order in self.browse(cr, uid, ids, context=context): + if order.account_move: + continue + if order.state != 'paid': + continue + + current_company = order.sale_journal.company_id + analytic_account_id = order.shop_id.project_id.id + print("#DEBUG: analytic_account_id = %s" % analytic_account_id) + + group_tax = {} + account_def = property_obj.get(cr, uid, 'property_account_receivable', 'res.partner', context=context) + + order_account = order.partner_id and \ + order.partner_id.property_account_receivable and \ + order.partner_id.property_account_receivable.id or \ + account_def and account_def.id or current_company.account_receivable.id + + if move_id is None: + # Create an entry for the sale + move_id = account_move_obj.create(cr, uid, { + 'ref' : order.name, + 'journal_id': order.sale_journal.id, + }, context=context) + + def insert_data(data_type, values): + # if have_to_group_by: + + sale_journal_id = order.sale_journal.id + period = account_period_obj.find(cr, uid, context=dict(context or {}, company_id=current_company.id, account_period_prefer_normal=True))[0] + + # 'quantity': line.qty, + # 'product_id': line.product_id.id, + values.update({ + 'date': order.date_order[:10], + 'ref': order.name, + 'journal_id' : sale_journal_id, + 'period_id' : period, + 'move_id' : move_id, + 'company_id': current_company.id, + }) + + if data_type == 'product': + key = ('product', values['partner_id'], values['product_id'], values['debit'] > 0) + elif data_type == 'tax': + key = ('tax', values['partner_id'], values['tax_code_id'], values['debit'] > 0) + elif data_type == 'counter_part': + key = ('counter_part', values['partner_id'], values['account_id'], values['debit'] > 0) + else: + return + + grouped_data.setdefault(key, []) + + # if not have_to_group_by or (not grouped_data[key]): + # grouped_data[key].append(values) + # else: + # pass + + if have_to_group_by: + if not grouped_data[key]: + grouped_data[key].append(values) + else: + current_value = grouped_data[key][0] + current_value['quantity'] = current_value.get('quantity', 0.0) + values.get('quantity', 0.0) + current_value['credit'] = current_value.get('credit', 0.0) + values.get('credit', 0.0) + current_value['debit'] = current_value.get('debit', 0.0) + values.get('debit', 0.0) + current_value['tax_amount'] = current_value.get('tax_amount', 0.0) + values.get('tax_amount', 0.0) + else: + grouped_data[key].append(values) + + #because of the weird way the pos order is written, we need to make sure there is at least one line, + #because just after the 'for' loop there are references to 'line' and 'income_account' variables (that + #are set inside the for loop) + #TOFIX: a deep refactoring of this method (and class!) is needed in order to get rid of this stupid hack + assert order.lines, _('The POS order must have lines when calling this method') + # Create an move for each order line + + cur = order.pricelist_id.currency_id + for line in order.lines: + tax_amount = 0 + taxes = [] + for t in line.product_id.taxes_id: + if t.company_id.id == current_company.id: + taxes.append(t) + computed_taxes = account_tax_obj.compute_all(cr, uid, taxes, line.price_unit * (100.0-line.discount) / 100.0, line.qty)['taxes'] + + for tax in computed_taxes: + tax_amount += cur_obj.round(cr, uid, cur, tax['amount']) + group_key = (tax['tax_code_id'], tax['base_code_id'], tax['account_collected_id'], tax['id']) + + group_tax.setdefault(group_key, 0) + group_tax[group_key] += cur_obj.round(cr, uid, cur, tax['amount']) + + amount = line.price_subtotal + + # Search for the income account + if line.product_id.property_account_income.id: + income_account = line.product_id.property_account_income.id + elif line.product_id.categ_id.property_account_income_categ.id: + income_account = line.product_id.categ_id.property_account_income_categ.id + else: + raise osv.except_osv(_('Error!'), _('Please define income '\ + 'account for this product: "%s" (id:%d).') \ + % (line.product_id.name, line.product_id.id, )) + + # Empty the tax list as long as there is no tax code: + tax_code_id = False + tax_amount = 0 + while computed_taxes: + tax = computed_taxes.pop(0) + tax_code_id, tax_amount = compute_tax(amount, tax, line) + + # If there is one we stop + if tax_code_id: + break + + # Create a move for the line + insert_data('product', { + 'name': line.product_id.name, + 'quantity': line.qty, + 'product_id': line.product_id.id, + 'account_id': income_account, + 'analytic_account_id': analytic_account_id, + 'credit': ((amount>0) and amount) or 0.0, + 'debit': ((amount<0) and -amount) or 0.0, + 'tax_code_id': tax_code_id, + 'tax_amount': tax_amount, + 'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False + }) + + # For each remaining tax with a code, whe create a move line + for tax in computed_taxes: + tax_code_id, tax_amount = compute_tax(amount, tax, line) + if not tax_code_id: + continue + + insert_data('tax', { + 'name': _('Tax'), + 'product_id':line.product_id.id, + 'quantity': line.qty, + 'account_id': income_account, + 'credit': 0.0, + 'debit': 0.0, + 'tax_code_id': tax_code_id, + 'tax_amount': tax_amount, + 'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False + }) + + # Create a move for each tax group + (tax_code_pos, base_code_pos, account_pos, tax_id)= (0, 1, 2, 3) + + for key, tax_amount in group_tax.items(): + tax = self.pool.get('account.tax').browse(cr, uid, key[tax_id], context=context) + insert_data('tax', { + 'name': _('Tax') + ' ' + tax.name, + 'quantity': line.qty, + 'product_id': line.product_id.id, + 'account_id': key[account_pos] or income_account, + 'credit': ((tax_amount>0) and tax_amount) or 0.0, + 'debit': ((tax_amount<0) and -tax_amount) or 0.0, + 'tax_code_id': key[tax_code_pos], + 'tax_amount': tax_amount, + 'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False + }) + + # counterpart + insert_data('counter_part', { + 'name': _("Trade Receivables"), #order.name, + 'account_id': order_account, + 'credit': ((order.amount_total < 0) and -order.amount_total) or 0.0, + 'debit': ((order.amount_total > 0) and order.amount_total) or 0.0, + 'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False + }) + + order.write({'state':'done', 'account_move': move_id}) + + all_lines = [] + for group_key, group_data in grouped_data.iteritems(): + print("#DEBUG: group_key = %s - group_data = %s" % (group_key, group_data)) + for value in group_data: + all_lines.append((0, 0, value),) + if move_id: #In case no order was changed + self.pool.get("account.move").write(cr, uid, [move_id], {'line_id':all_lines}, context=context) + + return True + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: