Browse Source

[ADD] module from 8.0 serie

pull/370/head
Sylvain LE GAL 5 years ago
parent
commit
2e854b88b4
  1. 93
      pos_invoicing/README.rst
  2. 1
      pos_invoicing/__init__.py
  3. 27
      pos_invoicing/__openerp__.py
  4. 13
      pos_invoicing/demo/res_groups.xml
  5. 50
      pos_invoicing/i18n/fr.po
  6. 5
      pos_invoicing/models/__init__.py
  7. 39
      pos_invoicing/models/account_invoice.py
  8. 28
      pos_invoicing/models/account_voucher.py
  9. 18
      pos_invoicing/models/pos_order.py
  10. 70
      pos_invoicing/models/pos_session.py
  11. 2
      pos_invoicing/readme/CONTRIBUTORS.rst
  12. 25
      pos_invoicing/readme/DESCRIPTION.rst
  13. 4
      pos_invoicing/readme/ROADMAP.rst
  14. BIN
      pos_invoicing/static/description/account_invoice_form.png
  15. BIN
      pos_invoicing/static/description/icon.png
  16. 2
      pos_invoicing/tests/__init__.py
  17. 92
      pos_invoicing/tests/test_module.py
  18. 21
      pos_invoicing/views/view_account_invoice.xml

93
pos_invoicing/README.rst

@ -0,0 +1,93 @@
=========================
Point Of Sale - Invoicing
=========================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-grap%2Fgrap--odoo--incubator-lightgray.png?logo=github
:target: https://github.com/grap/grap-odoo-incubator/tree/8.0/pos_invoicing
:alt: grap/grap-odoo-incubator
|badge1| |badge2| |badge3|
When you pay a pos_order, and then create an invoice :
* you mustn't register a payment against the invoice as the payment
already exists in POS
* The POS payment will be reconciled with the invoice when the session
is closed
* You mustn't modify the invoice because the amount could become
different from the one registered in POS. Thus we have to
automatically validate the created invoice
Functionality
-------------
About the invoices created from POS after payment:
* automatically validate them and don't allow modifications
* Disable the Pay Button
* Don't display them in the Customer Payment tool
Technically
-----------
add a ``pos_pending_payment`` field on the ``account.invoice`` to mark the
items that shouldn't be paid.
.. figure:: https://raw.githubusercontent.com/grap/grap-odoo-incubator/8.0/pos_invoicing/static/description/account_invoice_form.png
**Table of contents**
.. contents::
:local:
Known issues / Roadmap
======================
* This module reconcile invoiced orders only if a customer has one invoice per
session.
* It should be great to use the OCA module ``pos_autoreconcile``.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/grap/grap-odoo-incubator/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/grap/grap-odoo-incubator/issues/new?body=module:%20pos_invoicing%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* GRAP
Contributors
~~~~~~~~~~~~
* Sylvain LE GAL <https://twitter.com/legalsylvain>
* Julien WESTE
Maintainers
~~~~~~~~~~~
This module is part of the `grap/grap-odoo-incubator <https://github.com/grap/grap-odoo-incubator/tree/8.0/pos_invoicing>`_ project on GitHub.
You are welcome to contribute.

1
pos_invoicing/__init__.py

@ -0,0 +1 @@
from . import models

27
pos_invoicing/__openerp__.py

@ -0,0 +1,27 @@
# coding: utf-8
# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop)
# @author: Julien WESTE
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Point Of Sale - Invoicing',
'summary': 'Handle invoicing from Point Of Sale',
'version': '8.0.3.0.0',
'category': 'Point of Sale',
'author': 'GRAP',
'website': 'http://www.grap.coop',
'license': 'AGPL-3',
'depends': [
'point_of_sale',
],
'data': [
'views/view_account_invoice.xml',
],
'demo': [
'demo/res_groups.xml',
],
'images': [
'static/description/account_invoice_form.png',
],
'installable': False,
}

13
pos_invoicing/demo/res_groups.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2015 - Today: GRAP (http://www.grap.coop)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp><data>
<record id="account.group_account_manager" model="res.groups">
<field name="users" eval="[(4, ref('base.user_root'))]"/>
</record>
</data></openerp>

50
pos_invoicing/i18n/fr.po

@ -0,0 +1,50 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_invoicing
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-02 13:14+0000\n"
"PO-Revision-Date: 2018-08-02 13:14+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pos_invoicing
#: model:ir.model,name:pos_invoicing.model_account_voucher
msgid "Accounting Voucher"
msgstr "Justificatif comptable"
#. module: pos_invoicing
#: help:account.invoice,pos_pending_payment:0
msgid "Indicates an invoice for which there are pending payments in the Point of Sale. \n"
"The invoice will be marked as paid when the session will be closed."
msgstr "La case est cochée si il y a des paiements en cours dans le point de vente. \n"
"La facture sera marquée comme payée quand la session sera fermée."
#. module: pos_invoicing
#: model:ir.model,name:pos_invoicing.model_account_invoice
msgid "Invoice"
msgstr "Facture"
#. module: pos_invoicing
#: view:account.invoice:pos_invoicing.view_account_invoice_form
#: field:account.invoice,pos_pending_payment:0
msgid "PoS - Pending Payment"
msgstr "PdV - Paiement en cours"
#. module: pos_invoicing
#: model:ir.model,name:pos_invoicing.model_pos_order
msgid "Point of Sale"
msgstr "Point de Vente"
#. module: pos_invoicing
#: code:addons/pos_invoicing/models/account_invoice.py:37
#, python-format
msgid "You can not realize this action on the invoice(s) %s because there are pending payments in the Point of Sale."
msgstr "Vous ne pouvez pas réaliser cette action sur la / les facture(s) %s car il y a des paiements en cours dans le point de vente."

5
pos_invoicing/models/__init__.py

@ -0,0 +1,5 @@
# coding: utf-8
from . import pos_order
from . import pos_session
from . import account_invoice
from . import account_voucher

39
pos_invoicing/models/account_invoice.py

@ -0,0 +1,39 @@
# coding: utf-8
# Copyright (C) 2018 - Today: GRAP (http://www.grap.coop)
# @author: Julien WESTE
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import _, api, fields, models
from openerp.exceptions import Warning as UserError
class AccountInvoice(models.Model):
_inherit = 'account.invoice'
pos_pending_payment = fields.Boolean(
string='PoS - Pending Payment', readonly=True,
oldname='forbid_payment',
help="Indicates an invoice for which there are pending payments in the"
" Point of Sale. \nThe invoice will be marked as paid when the session"
" will be closed.")
# Overload Section
@api.multi
def action_cancel(self):
self._check_pos_pending_payment()
return super(AccountInvoice, self).action_cancel()
@api.multi
def invoice_pay_customer(self):
self._check_pos_pending_payment()
return super(AccountInvoice, self).invoice_pay_customer()
@api.multi
def _check_pos_pending_payment(self):
invoices = self.filtered(lambda x: x.pos_pending_payment)
if invoices:
raise UserError(_(
"You can not realize this action on the invoice(s) %s because"
" there are pending payments in the Point of Sale.") % (
', '.join(invoices.mapped('name'))))

28
pos_invoicing/models/account_voucher.py

@ -0,0 +1,28 @@
# coding: utf-8
# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop)
# @author: Julien WESTE
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import api, models
class AccountVoucher(models.Model):
_inherit = 'account.voucher'
# Override section
@api.multi
def recompute_voucher_lines(
self, partner_id, journal_id, price, currency_id, ttype, date):
move_line_obj = self.env['account.move.line']
res = super(AccountVoucher, self).recompute_voucher_lines(
partner_id, journal_id, price, currency_id, ttype, date)
for voucher_type in ['line_dr_ids', 'line_cr_ids']:
for voucher_line in res['value'][voucher_type]:
move_line = move_line_obj.browse(voucher_line['move_line_id'])
if move_line.invoice.pos_pending_payment:
res['value'][voucher_type].remove(voucher_line)
return res

18
pos_invoicing/models/pos_order.py

@ -0,0 +1,18 @@
# coding: utf-8
# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop)
# @author: Julien WESTE
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import api, models
class PosOrder(models.Model):
_inherit = 'pos.order'
@api.multi
def action_invoice(self):
res = super(PosOrder, self).action_invoice()
self.mapped('invoice_id').write({'pos_pending_payment': True})
self.mapped('invoice_id').signal_workflow('invoice_open')
return res

70
pos_invoicing/models/pos_session.py

@ -0,0 +1,70 @@
# coding: utf-8
# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop)
# @author: Julien WESTE
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from openerp import api, models
_logger = logging.getLogger(__name__)
class PosSession(models.Model):
_inherit = 'pos.session'
@api.multi
def wkf_action_close(self):
move_line_obj = self.env['account.move.line']
res = super(PosSession, self).wkf_action_close()
# Get All Pos Order invoiced during the current Sessions
orders = self.order_ids.filtered(lambda x: x.invoice_id)
for order in orders:
# Get accounting partner
partner = order.partner_id.parent_id or order.partner_id
# Search all Sale Move Lines to reconcile in Sale Journal
sale_move_lines = []
sale_total = 0
for move_line in order.invoice_id.move_id.line_id:
if (move_line.partner_id.id == partner.id and
move_line.account_id.type == 'receivable'):
sale_move_lines.append(move_line)
sale_total += move_line.debit - move_line.credit
# Search all move Line to reconcile in Payment Journals
payment_move_lines = []
payment_total = 0
statement_ids = order.mapped('statement_ids.statement_id').ids
move_lines = move_line_obj.search([
('statement_id', 'in', statement_ids),
('partner_id', '=', partner.id),
('reconcile_id', '=', False)])
for move_line in move_lines:
if (move_line.account_id.type == 'receivable'):
payment_move_lines.append(move_line)
payment_total += move_line.debit - move_line.credit
# Try to reconcile
if payment_total != - sale_total:
# Unable to reconcile
_logger.warning(
"Unable to reconcile the payment of %s #%d."
"(partner : %s)" % (
order.name, order.id, partner.name))
else:
# Reconcile move lines
move_lines = move_line_obj.browse(
[x.id for x in sale_move_lines] +
[x.id for x in payment_move_lines])
move_lines.reconcile('manual', False, False, False)
# Unflag the invoice as 'PoS Pending Payment'
order.invoice_id.pos_pending_payment = False
return res

2
pos_invoicing/readme/CONTRIBUTORS.rst

@ -0,0 +1,2 @@
* Sylvain LE GAL <https://twitter.com/legalsylvain>
* Julien WESTE

25
pos_invoicing/readme/DESCRIPTION.rst

@ -0,0 +1,25 @@
When you pay a pos_order, and then create an invoice :
* you mustn't register a payment against the invoice as the payment
already exists in POS
* The POS payment will be reconciled with the invoice when the session
is closed
* You mustn't modify the invoice because the amount could become
different from the one registered in POS. Thus we have to
automatically validate the created invoice
Functionality
-------------
About the invoices created from POS after payment:
* automatically validate them and don't allow modifications
* Disable the Pay Button
* Don't display them in the Customer Payment tool
Technically
-----------
add a ``pos_pending_payment`` field on the ``account.invoice`` to mark the
items that shouldn't be paid.
.. figure:: ../static/description/account_invoice_form.png

4
pos_invoicing/readme/ROADMAP.rst

@ -0,0 +1,4 @@
* This module reconcile invoiced orders only if a customer has one invoice per
session.
* It should be great to use the OCA module ``pos_autoreconcile``.

BIN
pos_invoicing/static/description/account_invoice_form.png

After

Width: 852  |  Height: 430  |  Size: 32 KiB

BIN
pos_invoicing/static/description/icon.png

After

Width: 64  |  Height: 64  |  Size: 4.0 KiB

2
pos_invoicing/tests/__init__.py

@ -0,0 +1,2 @@
# coding: utf-8
from . import test_module

92
pos_invoicing/tests/test_module.py

@ -0,0 +1,92 @@
# coding: utf-8
# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop)
# @author: Julien WESTE
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import time
from openerp.tests.common import TransactionCase
class TestPosInvoicing(TransactionCase):
"""Tests for POS Invoicing Module"""
def setUp(self):
super(TestPosInvoicing, self).setUp()
# Get Registry
self.session_obj = self.env['pos.session']
self.order_obj = self.env['pos.order']
# Get Object
self.config = self.env.ref('point_of_sale.pos_config_main')
self.partner_A = self.env.ref('base.res_partner_2')
self.partner_B = self.env.ref('base.res_partner_12')
self.product = self.env.ref('product.product_product_48')
self.payment_journal = self.env.ref('account.cash_journal')
# Test Section
def test_01_invoice_with_payment(self):
"""Test the workflow: Draft Order -> Payment -> Invoice"""
# Opening Session
session = self.session_obj.create({'config_id': self.config.id})
# TODO FIXME, for the time being, reconciliation is not
# set if a customer make many invoices in the same pos session.
# self._create_order(session, self.partner_A, 100, True)
# self._create_order(session, self.partner_A, 200, True)
self._create_order(session, self.partner_B, 400, True)
# The Invoice must be unpayable but in 'open' state
# Invoice created by order should be in open state
invoiced_orders = session.mapped('order_ids').filtered(
lambda x: x.state == 'invoiced')
invoices = invoiced_orders.mapped('invoice_id')
self.assertEquals(
[x for x in invoices.mapped('state') if x != 'open'], [],
"All invoices generated from PoS should be in the 'open' state"
" when session is opened")
self.assertEquals(
[x for x in invoices.mapped('pos_pending_payment') if not x],
[],
"All invoices generated from PoS should be marked as PoS Pending"
" Payment when session is opened")
# Close Session
session.signal_workflow('close')
self.assertEquals(
[x for x in invoices.mapped('state') if x != 'paid'], [],
"All invoices generated from PoS should be in the 'paid' state"
" when session is closed")
self.assertEquals(
[x for x in invoices.mapped('pos_pending_payment') if x], [],
"Invoices generated from PoS should not be marked as PoS Pending"
" Payment when session is closed")
# Private Section
def _create_order(self, session, partner, amount, with_invoice):
# create Pos Order
order = self.order_obj.create({
'session_id': session.id,
'partner_id': partner.id,
'lines': [[0, False, {
'product_id': self.product.id,
'qty': 1,
'price_unit': amount,
}]],
})
# Finish Payment
self.order_obj.add_payment(order.id, {
'journal': self.payment_journal.id,
'payment_date': time.strftime('%Y-%m-%d'),
'amount': amount,
})
# Mark as Paid
order.signal_workflow('paid')
if with_invoice:
order.action_invoice()
return order

21
pos_invoicing/views/view_account_invoice.xml

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2013 - Today: GRAP (http://www.grap.coop)
@author Julien WESTE
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp><data>
<record id="view_account_invoice_form" model="ir.ui.view">
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='payment_ids']" position="before">
<label for="pos_pending_payment">PoS - Pending Payment</label>
<field name="pos_pending_payment"/>
</xpath>
</field>
</record>
</data></openerp>
Loading…
Cancel
Save