-
-
Notifications
You must be signed in to change notification settings - Fork 664
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #128 from pedrobaeza/8.0-stock_picking_invoicing_u…
…nified [8.0] [ADD] stock_picking_invoicing_unified
- Loading branch information
Showing
9 changed files
with
397 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg | ||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html | ||
:alt: License: AGPL-3 | ||
|
||
=============================== | ||
Stock Picking Invoicing Unified | ||
=============================== | ||
|
||
Odoo allows to select several pickings and click on *Create Draft Invoices* | ||
option to create the corresponding invoice(s)/refund(s). If you have | ||
selected several partners and you have checked the option *Group by partner*, | ||
it will create a single invoice or refund per partner. | ||
|
||
But it only takes into account the first picking for selecting the type of the | ||
invoice you are going to create (customer/supplier invoice/refund), mixing all | ||
the lines on it. And not only that: if you have returned pickings, the returned | ||
quantities are summed to the rest, instead of decreasing the amount to invoice, | ||
which is the common practise when you have some returns. | ||
|
||
This module fixes this problem, allowing to invoice them all together: | ||
if you have delivered and received goods for the same customer and you | ||
have checked the option *Group by partner*, you will have a single | ||
invoice with the goods delivered and received and the quantities of the | ||
goods received will be negative. So it will avoid you to send both an | ||
invoice and a refund to your customer and have to reconciliate them to | ||
compute the good residual amount. | ||
|
||
Usage | ||
===== | ||
|
||
* Select several pickings from any of the menus that allows it ( | ||
*Warehouse > All Operations* and click on any of the lines, | ||
*Purchases > Invoice Control > On Incoming Shipments*, etc). | ||
* Click on *More > Create Draft Invoices*. | ||
* In the resulting dialog, the proper invoices types that are going to be | ||
created are computed, and you have to select the journals for that types. | ||
* Click on *Create* button, and the invoices will be correctly created. | ||
* The lines of pickings that are not of the greater picking type are created | ||
with negative price unit. | ||
|
||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas | ||
:alt: Try me on Runbot | ||
:target: https://runbot.odoo-community.org/runbot/95/8.0 | ||
|
||
Known issues / Roadmap | ||
====================== | ||
|
||
* Add tests | ||
|
||
Bug Tracker | ||
=========== | ||
|
||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/account-invoicing/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 | ||
`here <https://github.com/OCA/account-invoicing/issues/new?body=module:%20 | ||
stock_picking_invoicing_unified%0Aversion:%20 | ||
8.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. | ||
|
||
|
||
Credits | ||
======= | ||
|
||
Contributors | ||
------------ | ||
* Ainara Galdona <ainaragaldona@avanzosc.es> | ||
* Pedro M. Baeza <pedro.baeza@serviciobaeza.com> | ||
|
||
Maintainer | ||
---------- | ||
|
||
.. image:: https://odoo-community.org/logo.png | ||
:alt: Odoo Community Association | ||
:target: https://odoo-community.org | ||
|
||
This module is maintained by the OCA. | ||
|
||
OCA, or the Odoo Community Association, is a nonprofit organization whose | ||
mission is to support the collaborative development of Odoo features and | ||
promote its widespread use. | ||
|
||
To contribute to this module, please visit https://odoo-community.org. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# -*- coding: utf-8 -*- | ||
# © 2016 Ainara Galdona <ainaragaldona@avanzosc.es> - Avanzosc S.L. | ||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html | ||
|
||
from . import models | ||
from . import wizard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# -*- coding: utf-8 -*- | ||
# © 2016 Ainara Galdona <ainaragaldona@avanzosc.es> - Avanzosc S.L. | ||
# © 2016 Serv. Tecnol. Avanzados - Pedro M. Baeza | ||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html | ||
|
||
{ | ||
"name": "Stock Picking Invoicing Unified", | ||
"summary": "Create invoices/refunds from pickings of different types", | ||
"version": "8.0.1.0.0", | ||
'author': 'Serv. Tecnol. Avanzados - Pedro M. Baeza, ' | ||
'AvanzOSC, ' | ||
'Odoo Community Association (OCA)', | ||
'website': "http://github.com/OCA/account-invoicing", | ||
"license": "AGPL-3", | ||
'contributors': [ | ||
"Ainara Galdona <ainaragaldona@avanzosc.es>", | ||
"Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>" | ||
], | ||
"depends": ['stock_account'], | ||
"category": "Warehouse Management", | ||
"data": ['wizard/stock_invoice_onshipping_view.xml'], | ||
"installable": True | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# Translation of Odoo Server. | ||
# This file contains the translation of the following modules: | ||
# * stock_picking_invoicing_unified | ||
# | ||
msgid "" | ||
msgstr "" | ||
"Project-Id-Version: Odoo Server 8.0\n" | ||
"Report-Msgid-Bugs-To: \n" | ||
"POT-Creation-Date: 2016-02-19 19:16+0000\n" | ||
"PO-Revision-Date: 2016-02-19 19:16+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: stock_picking_invoicing_unified | ||
#: field:stock.invoice.onshipping,purchase_journal:0 | ||
msgid "Purchase Journal" | ||
msgstr "Diario para facturas de compras" | ||
|
||
#. module: stock_picking_invoicing_unified | ||
#: field:stock.invoice.onshipping,purchase_refund_journal:0 | ||
msgid "Purchase Refund Journal" | ||
msgstr "Diario para rectificaciones de compras" | ||
|
||
#. module: stock_picking_invoicing_unified | ||
#: field:stock.invoice.onshipping,sale_journal:0 | ||
msgid "Sale Journal" | ||
msgstr "Diario para facturas de ventas" | ||
|
||
#. module: stock_picking_invoicing_unified | ||
#: field:stock.invoice.onshipping,sale_refund_journal:0 | ||
msgid "Sale Refund Journal" | ||
msgstr "Diario para rectificaciones de ventas" | ||
|
||
#. module: stock_picking_invoicing_unified | ||
#: field:stock.invoice.onshipping,show_purchase_journal:0 | ||
msgid "Show Purchase Journal" | ||
msgstr "Mostrar diario de compras" | ||
|
||
#. module: stock_picking_invoicing_unified | ||
#: field:stock.invoice.onshipping,show_purchase_refund_journal:0 | ||
msgid "Show Refund Purchase Journal" | ||
msgstr "Mostrar diario de rectificaciones de compras" | ||
|
||
#. module: stock_picking_invoicing_unified | ||
#: field:stock.invoice.onshipping,show_sale_refund_journal:0 | ||
msgid "Show Refund Sale Journal" | ||
msgstr "Mostrar diario de rectificaciones de ventas" | ||
|
||
#. module: stock_picking_invoicing_unified | ||
#: field:stock.invoice.onshipping,show_sale_journal:0 | ||
msgid "Show Sale Journal" | ||
msgstr "Mostrar diario de ventas" | ||
|
||
#. module: stock_picking_invoicing_unified | ||
#: model:ir.model,name:stock_picking_invoicing_unified.model_stock_invoice_onshipping | ||
msgid "Stock Invoice Onshipping" | ||
msgstr "Factura en el envío de existencias" | ||
|
||
#. module: stock_picking_invoicing_unified | ||
#: model:ir.model,name:stock_picking_invoicing_unified.model_stock_move | ||
msgid "Stock Move" | ||
msgstr "Movimiento de existencias" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# -*- coding: utf-8 -*- | ||
# (c) 2016 Ainara Galdona <ainaragaldona@avanzosc.es> - Avanzosc S.L. | ||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html | ||
|
||
from . import stock_move |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# -*- coding: utf-8 -*- | ||
# (c) 2016 Ainara Galdona <ainaragaldona@avanzosc.es> - Avanzosc S.L. | ||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html | ||
|
||
from openerp import models, api | ||
|
||
|
||
class StockMove(models.Model): | ||
|
||
_inherit = 'stock.move' | ||
|
||
@api.model | ||
def _get_invoice_line_vals(self, move, partner, inv_type): | ||
res = super(StockMove, self)._get_invoice_line_vals(move, partner, | ||
inv_type) | ||
# negative value on quantity | ||
if ((inv_type == 'out_invoice' and | ||
move.location_id.usage == 'customer') or | ||
(inv_type == 'out_refund' and | ||
move.location_dest_id.usage == 'customer') or | ||
(inv_type == 'in_invoice' and | ||
move.location_dest_id.usage == 'supplier') or | ||
(inv_type == 'in_refund' and | ||
move.location_id.usage == 'supplier')): | ||
res['quantity'] *= -1 | ||
return res |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# -*- coding: utf-8 -*- | ||
# © 2016 Ainara Galdona <ainaragaldona@avanzosc.es> - Avanzosc S.L. | ||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html | ||
|
||
from . import stock_invoice_onshipping |
157 changes: 157 additions & 0 deletions
157
stock_picking_invoicing_unified/wizard/stock_invoice_onshipping.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
# -*- coding: utf-8 -*- | ||
# © 2016 Ainara Galdona <ainaragaldona@avanzosc.es> - Avanzosc S.L. | ||
# © 2016 Serv. Tecnol. Avanzados - Pedro M. Baeza | ||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html | ||
|
||
from openerp import models, fields, api | ||
from openerp.tools import config | ||
|
||
|
||
class StockInvoiceOnshipping(models.TransientModel): | ||
|
||
_inherit = 'stock.invoice.onshipping' | ||
|
||
@api.model | ||
def _default_journal(self, journal_type): | ||
return self.env['account.journal'].search( | ||
[('type', '=', journal_type)])[:1] | ||
|
||
journal_id = fields.Many2one(required=False) | ||
sale_journal = fields.Many2one( | ||
comodel_name='account.journal', string='Sale Journal', | ||
domain="[('type', '=', 'sale')]", | ||
default=lambda self: self._default_journal('sale')) | ||
sale_refund_journal = fields.Many2one( | ||
comodel_name='account.journal', string='Sale Refund Journal', | ||
domain="[('type', '=', 'sale_refund')]", | ||
default=lambda self: self._default_journal('sale_refund')) | ||
purchase_journal = fields.Many2one( | ||
comodel_name='account.journal', string='Purchase Journal', | ||
domain="[('type', '=', 'purchase')]", | ||
default=lambda self: self._default_journal('purchase')) | ||
purchase_refund_journal = fields.Many2one( | ||
comodel_name='account.journal', string="Purchase Refund Journal", | ||
domain="[('type', '=', 'purchase_refund')]", | ||
default=lambda self: self._default_journal('purchase_refund')) | ||
show_sale_journal = fields.Boolean(string="Show Sale Journal") | ||
show_sale_refund_journal = fields.Boolean( | ||
string="Show Refund Sale Journal") | ||
show_purchase_journal = fields.Boolean(string="Show Purchase Journal") | ||
show_purchase_refund_journal = fields.Boolean( | ||
string="Show Refund Purchase Journal") | ||
|
||
@api.multi | ||
@api.onchange('group') | ||
def onchange_group(self): | ||
self.ensure_one() | ||
(sale_pickings, sale_refund_pickings, purchase_pickings, | ||
purchase_refund_pickings) = self.get_split_pickings() | ||
self.show_sale_journal = bool(sale_pickings) | ||
self.show_sale_refund_journal = bool(sale_refund_pickings) | ||
self.show_purchase_journal = bool(purchase_pickings) | ||
self.show_purchase_refund_journal = bool(purchase_refund_pickings) | ||
|
||
@api.multi | ||
def get_partner_sum(self, pickings, partner, inv_type, picking_type, | ||
usage): | ||
move_obj = self.env['stock.move'] | ||
pickings = pickings.filtered(lambda x: x.picking_type_id.code == | ||
picking_type and x.partner_id == partner) | ||
if picking_type == 'outgoing': | ||
moves = pickings.mapped('move_lines').filtered( | ||
lambda x: x.location_dest_id.usage == usage) | ||
else: | ||
moves = pickings.mapped('move_lines').filtered( | ||
lambda x: x.location_id.usage == usage) | ||
return (sum([(move_obj._get_price_unit_invoice(m, inv_type) * | ||
m.product_uom_qty) for m in moves]), | ||
moves.mapped('picking_id')) | ||
|
||
@api.multi | ||
def get_split_pickings_grouped(self, pickings): | ||
sale_pickings = self.env['stock.picking'] | ||
sale_refund_pickings = self.env['stock.picking'] | ||
purchase_pickings = self.env['stock.picking'] | ||
purchase_refund_pickings = self.env['stock.picking'] | ||
|
||
for partner in pickings.mapped('partner_id'): | ||
so_sum, so_pickings = self.get_partner_sum( | ||
pickings, partner, 'out_invoice', 'outgoing', 'customer') | ||
si_sum, si_pickings = self.get_partner_sum( | ||
pickings, partner, 'out_invoice', 'incoming', 'customer') | ||
if (so_sum - si_sum) >= 0: | ||
sale_pickings |= (so_pickings | si_pickings) | ||
else: | ||
sale_refund_pickings |= (so_pickings | si_pickings) | ||
pi_sum, pi_pickings = self.get_partner_sum( | ||
pickings, partner, 'in_invoice', 'incoming', 'supplier') | ||
po_sum, po_pickings = self.get_partner_sum( | ||
pickings, partner, 'in_invoice', 'outgoing', 'supplier') | ||
if (pi_sum - po_sum) >= 0: | ||
purchase_pickings |= (pi_pickings | po_pickings) | ||
else: | ||
purchase_refund_pickings |= (pi_pickings | po_pickings) | ||
return (sale_pickings, sale_refund_pickings, purchase_pickings, | ||
purchase_refund_pickings) | ||
|
||
@api.multi | ||
def get_split_pickings_nogrouped(self, pickings): | ||
sale_pickings = pickings.filtered( | ||
lambda x: x.picking_type_id.code == 'outgoing' and | ||
x.move_lines[:1].location_dest_id.usage == 'customer') | ||
# use [:1] instead of [0] to avoid a errors on empty pickings | ||
sale_refund_pickings = pickings.filtered( | ||
lambda x: x.picking_type_id.code == 'incoming' and | ||
x.move_lines[:1].location_id.usage == 'customer') | ||
purchase_pickings = pickings.filtered( | ||
lambda x: x.picking_type_id.code == 'incoming' and | ||
x.move_lines[:1].location_id.usage == 'supplier') | ||
purchase_refund_pickings = pickings.filtered( | ||
lambda x: x.picking_type_id.code == 'outgoing' and | ||
x.move_lines[:1].location_dest_id.usage == 'supplier') | ||
return (sale_pickings, sale_refund_pickings, purchase_pickings, | ||
purchase_refund_pickings) | ||
|
||
@api.multi | ||
def get_split_pickings(self): | ||
picking_obj = self.env['stock.picking'] | ||
pickings = picking_obj.browse(self.env.context.get('active_ids', [])) | ||
if self.group: | ||
return self.get_split_pickings_grouped(pickings) | ||
else: | ||
return self.get_split_pickings_nogrouped(pickings) | ||
|
||
@api.multi | ||
def create_invoice(self): | ||
if (config['test_enable'] and | ||
not self.env.context.get('test_picking_invoicing_unified')): | ||
return super(StockInvoiceOnshipping, self).create_invoice() | ||
self.ensure_one() | ||
res = [] | ||
(sale_pickings, sale_refund_pickings, purchase_pickings, | ||
purchase_refund_pickings) = self.get_split_pickings() | ||
if sale_pickings: | ||
pickings = sale_pickings.with_context( | ||
date_inv=self.invoice_date, inv_type='out_invoice') | ||
res += pickings.action_invoice_create( | ||
journal_id=self.sale_journal.id, | ||
group=self.group, type='out_invoice') | ||
if sale_refund_pickings: | ||
pickings = sale_refund_pickings.with_context( | ||
date_inv=self.invoice_date, inv_type='out_refund') | ||
res += pickings.action_invoice_create( | ||
journal_id=self.sale_refund_journal.id, | ||
group=self.group, type='out_refund') | ||
if purchase_pickings: | ||
pickings = purchase_pickings.with_context( | ||
date_inv=self.invoice_date, inv_type='in_invoice') | ||
res += pickings.action_invoice_create( | ||
journal_id=self.purchase_journal.id, | ||
group=self.group, type='in_invoice') | ||
if purchase_refund_pickings: | ||
pickings = purchase_refund_pickings.with_context( | ||
date_inv=self.invoice_date, inv_type='in_refund') | ||
res += pickings.action_invoice_create( | ||
journal_id=self.purchase_refund_journal.id, group=self.group, | ||
type='in_refund') | ||
return res |
Oops, something went wrong.