Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[13.0]]ADD] Add account_invoicing_mode #755

Merged
merged 9 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions account_invoice_base_invoicing_mode/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
13 changes: 13 additions & 0 deletions account_invoice_base_invoicing_mode/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2020 Camptocamp
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Account Invoice Base Invoicing Mode",
"version": "13.0.1.0.0",
"summary": "Base module for handling multiple invoicing mode",
"author": "Camptocamp, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/account-invoicing",
"license": "AGPL-3",
"category": "Accounting & Finance",
"depends": ["account", "queue_job", "sale"],
"data": ["views/res_partner.xml"],
}
4 changes: 4 additions & 0 deletions account_invoice_base_invoicing_mode/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import account_invoice
from . import queue_job
from . import res_partner
from . import sale_order
15 changes: 15 additions & 0 deletions account_invoice_base_invoicing_mode/models/account_invoice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from odoo import models

from odoo.addons.queue_job.job import job, related_action


class AccountMove(models.Model):
_inherit = "account.move"

@job(default_channel="root.invoice_validation")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the @job decorator deprecated?
OCA/queue#274

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is, but it still works for v13.
It will be changed on the v14 migration.

@related_action(action="related_action_open_invoice")
def _validate_invoice(self):
return self.sudo().action_post()
35 changes: 35 additions & 0 deletions account_invoice_base_invoicing_mode/models/queue_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from odoo import _, models


class QueueJob(models.Model):
_inherit = "queue.job"

def related_action_open_invoice(self):
"""Open a form view with the invoice related to the job."""
self.ensure_one()
model_name = self.model_name
records = self.env[model_name].browse(self.record_ids).exists()
if not records:
return None
action = {
"name": _("Related Record"),
"type": "ir.actions.act_window",
"view_type": "form",
"view_mode": "form",
"res_model": records._name,
}
if len(records) == 1:
action["res_id"] = records.id
else:
action.update(
{
"name": _("Related Records"),
"view_mode": "tree,form",
"view_id": "account.view_invoice_tree",
"domain": [("id", "in", records.ids)],
}
)
return action
15 changes: 15 additions & 0 deletions account_invoice_base_invoicing_mode/models/res_partner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from odoo import fields, models


class ResPartner(models.Model):
_inherit = "res.partner"

invoicing_mode = fields.Selection([("standard", "Standard")], default="standard")
one_invoice_per_order = fields.Boolean(
"One invoice per order",
default=False,
help="Do not group sale order into one invoice.",
)
10 changes: 10 additions & 0 deletions account_invoice_base_invoicing_mode/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from odoo import fields, models


class SaleOrder(models.Model):
_inherit = "sale.order"

invoicing_mode = fields.Selection(related="partner_invoice_id.invoicing_mode")
TDu marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 3 additions & 0 deletions account_invoice_base_invoicing_mode/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* `Camptocamp <https://www.camptocamp.com>`_:

* Thierry Ducrest <[email protected]>
9 changes: 9 additions & 0 deletions account_invoice_base_invoicing_mode/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
This is a base module for implementing different invoicing mode for customers.
It adds a selection field `invoicing_mode` in the Accounting tab of the partner
with a default value (Odoo standard invoicing mode).
But it serves no purpose installed on its own.

The following modules use it to install specific invoicing mode :

* `account_invoice_mode_at_shipping`
* `account_invoice_mode_monthly`
7 changes: 7 additions & 0 deletions account_invoice_base_invoicing_mode/readme/ROADMAP.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
On the version 13.0 PR it has been discussed to rename the modules like this:

* account_invoice_base_invoicing_mode -> partner_invoicing_mode
* account_invoice_mode_monthly -> partner_invoicing_mode_monthly
* account_invoice_mode_at_shipping -> partner_invoicing_mode_at_shipping

It would be great to do it, during the version 14.0 migration.
16 changes: 16 additions & 0 deletions account_invoice_base_invoicing_mode/views/res_partner.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_partner_property_form" model="ir.ui.view">
<field name="name">view_partner_property_form_base_invoicing_mode</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="account.view_partner_property_form" />
<field name="arch" type="xml">
<xpath expr="//page[@name='accounting']/group" position="inside">
<group name="invoicing_mode">
<field name="invoicing_mode" />
<field name="one_invoice_per_order" />
</group>
</xpath>
</field>
</record>
</odoo>
2 changes: 2 additions & 0 deletions account_invoice_mode_at_shipping/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import tests
12 changes: 12 additions & 0 deletions account_invoice_mode_at_shipping/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2020 Camptocamp
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Account Invoice Mode At Shipping",
"version": "13.0.1.0.0",
"summary": "Create invoices automatically when goods are shipped.",
"author": "Camptocamp, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/account-invoicing",
"license": "AGPL-3",
"category": "Accounting & Finance",
"depends": ["account", "account_invoice_base_invoicing_mode", "queue_job", "stock"],
}
3 changes: 3 additions & 0 deletions account_invoice_mode_at_shipping/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import res_partner
from . import stock_move
from . import stock_picking
10 changes: 10 additions & 0 deletions account_invoice_mode_at_shipping/models/res_partner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from odoo import fields, models


class ResPartner(models.Model):
_inherit = "res.partner"

invoicing_mode = fields.Selection(selection_add=([("at_shipping", "At Shipping")]))
19 changes: 19 additions & 0 deletions account_invoice_mode_at_shipping/models/stock_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from odoo import models


class StockMove(models.Model):
_inherit = "stock.move"

def _get_related_invoices(self):
""" Overridden from stock_account to return the customer invoices
related to this stock move.
"""
invoices = super()._get_related_invoices()
line_invoices = self.mapped("sale_line_id.order_id.invoice_ids").filtered(
lambda x: x.state == "posted"
)
invoices |= line_invoices
return invoices
53 changes: 53 additions & 0 deletions account_invoice_mode_at_shipping/models/stock_picking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from odoo import _, models

from odoo.addons.queue_job.job import job


class StockPicking(models.Model):
_inherit = "stock.picking"

def action_done(self):
res = super().action_done()

for picking in self:
if picking._invoice_at_shipping():
picking.with_delay()._invoicing_at_shipping()
return res

def _invoice_at_shipping(self):
"""Check if picking must be invoiced at shipping."""
self.ensure_one()
return (
self.picking_type_code == "outgoing"
and self.sale_id.partner_invoice_id.invoicing_mode == "at_shipping"
)

@job(default_channel="root.invoice_at_shipping")
def _invoicing_at_shipping(self):
self.ensure_one()
sales = self.env["sale.order"].browse()
# Filter out non invoicable sales order
for sale in self._get_sales_order_to_invoice():
if sale._get_invoiceable_lines():
TDu marked this conversation as resolved.
Show resolved Hide resolved
sales |= sale
# Split invoice creation on partner sales grouping on invoice settings
sales_one_invoice_per_order = sales.filtered(
"partner_invoice_id.one_invoice_per_order"
)
invoices = self.env["account.move"].browse()
if sales_one_invoice_per_order:
invoices |= sales_one_invoice_per_order._create_invoices(grouped=True)
sales_many_invoice_per_order = sales - sales_one_invoice_per_order
if sales_many_invoice_per_order:
invoices |= sales_many_invoice_per_order._create_invoices(grouped=False)
for invoice in invoices:
invoice.with_delay()._validate_invoice()
return invoices or _("Nothing to invoice.")

def _get_sales_order_to_invoice(self):
return self.mapped("move_lines.sale_line_id.order_id").filtered(
lambda r: r._get_invoiceable_lines()
)
3 changes: 3 additions & 0 deletions account_invoice_mode_at_shipping/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* `Camptocamp <https://www.camptocamp.com>`_:

* Thierry Ducrest <[email protected]>
4 changes: 4 additions & 0 deletions account_invoice_mode_at_shipping/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This module allows to select a `At shipping` invoicing mode for a customer.
It is based on `account_invoice_base_invoicing_mode`.
When this mode is selected the customer will be invoiced automatically on
delivery of the goods.
1 change: 1 addition & 0 deletions account_invoice_mode_at_shipping/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_invoice_mode_at_shipping
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from odoo.tests.common import SavepointCase


class TestInvoiceModeAtShipping(SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
TDu marked this conversation as resolved.
Show resolved Hide resolved
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.partner = cls.env.ref("base.res_partner_1")
cls.product = cls.env.ref("product.product_delivery_01")
cls.so1 = cls.env["sale.order"].create(
{
"partner_id": cls.partner.id,
"partner_invoice_id": cls.partner.id,
"partner_shipping_id": cls.partner.id,
"order_line": [
(
0,
0,
{
"name": "Line one",
"product_id": cls.product.id,
"product_uom_qty": 4,
"product_uom": cls.product.uom_id.id,
"price_unit": 123,
},
)
],
"pricelist_id": cls.env.ref("product.list0").id,
}
)
stock_location = cls.env.ref("stock.stock_location_stock")
inventory = cls.env["stock.inventory"].create(
{
"name": "Test Inventory",
"product_ids": [(6, 0, cls.product.ids)],
"state": "confirm",
"line_ids": [
(
0,
0,
{
"product_qty": 100,
"location_id": stock_location.id,
"product_id": cls.product.id,
"product_uom_id": cls.product.uom_id.id,
},
)
],
}
)
inventory.action_validate()

def test_invoice_created_at_shipping(self):
"""Check that an invoice is created when goods are shipped."""
self.partner.invoicing_mode = "at_shipping"
self.so1.action_confirm()
for picking in self.so1.picking_ids:
for line in picking.move_lines:
line.quantity_done = line.product_uom_qty
picking.action_assign()
picking.with_context(test_queue_job_no_delay=True).button_validate()
self.assertEqual(len(self.so1.invoice_ids), 1)
self.assertEqual(self.so1.invoice_ids.state, "posted")

def test_invoice_not_created_at_shipping(self):
"""Check that an invoice is not created when goods are shipped."""
self.partner.invoicing_mode = "standard"
self.so1.action_confirm()
for picking in self.so1.picking_ids:
for line in picking.move_lines:
line.quantity_done = line.product_uom_qty
picking.action_assign()
picking.button_validate()
self.assertEqual(len(self.so1.invoice_ids), 0)
2 changes: 2 additions & 0 deletions account_invoice_mode_monthly/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import tests
13 changes: 13 additions & 0 deletions account_invoice_mode_monthly/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2020 Camptocamp
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Account Invoice Mode Monthly",
"version": "13.0.1.0.0",
"summary": "Create invoices automatically on a monthly basis.",
"author": "Camptocamp, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/account-invoicing",
"license": "AGPL-3",
"category": "Accounting & Finance",
"depends": ["account", "account_invoice_base_invoicing_mode", "queue_job", "sale"],
"data": ["data/ir_cron.xml", "views/res_config_settings_views.xml"],
}
18 changes: 18 additions & 0 deletions account_invoice_mode_monthly/data/ir_cron.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo noupdate="1">
<record forcecreate="True" id="ir_cron_generate_monthly_invoice" model="ir.cron">
<field name="name">Generate Monthly Invoices</field>
<field eval="True" name="active" />
<field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
<field name="model_id" ref="model_sale_order" />
<field name="code">model.cron_generate_monthly_invoices()</field>
<field
name="nextcall"
eval="(DateTime.now().replace(hour=1,minute=0).strftime('%Y-%m-%d %H:%M:%S'))"
/>
</record>
</odoo>
4 changes: 4 additions & 0 deletions account_invoice_mode_monthly/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import res_company
from . import res_config_settings
from . import res_partner
from . import sale_order
21 changes: 21 additions & 0 deletions account_invoice_mode_monthly/models/res_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from odoo import fields, models


class ResCompany(models.Model):
_inherit = "res.company"

invoicing_mode_monthly_day_todo = fields.Integer(
"Invoicing Day",
default="31",
help="Day of the month to execute the invoicing. For a number higher"
"than the number of days in a month, the invoicing will be"
"executed on the last day of the month.",
)
invoicing_mode_monthly_last_execution = fields.Datetime(
string="Last execution",
help="Last execution of monthly invoicing.",
readonly=True,
)
Loading