Skip to content

Commit

Permalink
Merge PR #481 into 12.0
Browse files Browse the repository at this point in the history
Signed-off-by lmignon
  • Loading branch information
shopinvader-git-bot committed Oct 30, 2019
2 parents 48c4730 + 2e6ce9b commit 159b7d1
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 0 deletions.
1 change: 1 addition & 0 deletions setup/shopinvader_invoice/odoo/addons/shopinvader_invoice
6 changes: 6 additions & 0 deletions setup/shopinvader_invoice/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
3 changes: 3 additions & 0 deletions shopinvader_invoice/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Copyright 2019 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import services
14 changes: 14 additions & 0 deletions shopinvader_invoice/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2019 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Shopinvader Invoice",
"description": """Shopinvader Invoice module""",
"author": "ACSONE SA/NV",
"website": "https://shopinvader.com",
"category": "shopinvader",
"version": "12.0.1.0.0",
"license": "AGPL-3",
"depends": ["account", "shopinvader"],
"data": [],
"demo": [],
}
1 change: 1 addition & 0 deletions shopinvader_invoice/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* François Honoré <[email protected]>
2 changes: 2 additions & 0 deletions shopinvader_invoice/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This addon adds services to access a customer's open and paid invoices (only related to Sale orders/Cart).
These services can be useful if you want to give your customers access to their invoice history.
3 changes: 3 additions & 0 deletions shopinvader_invoice/services/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Copyright 2019 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import invoice
128 changes: 128 additions & 0 deletions shopinvader_invoice/services/invoice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Copyright 2019 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.addons.base_rest.components.service import to_int
from odoo.addons.component.core import Component


class InvoiceService(Component):
_inherit = "shopinvader.abstract.mail.service"
_name = "shopinvader.invoice.service"
_usage = "invoice"
_expose_model = "account.invoice"

def search(self, **params):
"""
Get every invoices related to logged user
:param params: dict/json
:return: dict
"""
return self._paginate_search(**params)

def _validator_search(self):
"""
Validator for the search
:return: dict
"""
schema = {
"per_page": {
"coerce": to_int,
"nullable": True,
"type": "integer",
},
"page": {"coerce": to_int, "nullable": True, "type": "integer"},
}
return schema

def _validator_return_search(self):
"""
Output validator for the search
:return: dict
"""
invoice_schema = {
"invoice_id": {"type": "integer"},
"number": {"type": "string"},
"date_invoice": {"type": "string"},
"amount_total": {"type": "float"},
"amount_tax": {"type": "float"},
"amount_untaxed": {"type": "float"},
"amount_due": {"type": "float"},
"type": {"type": "string"},
"state": {"type": "string"},
}
schema = {
"size": {"type": "integer"},
"data": {
"type": "list",
"schema": {"type": "dict", "schema": invoice_schema},
},
}
return schema

def _get_parser_invoice(self):
"""
Get the parser of account.invoice
:return: list
"""
to_parse = [
"id:invoice_id",
"number",
"date_invoice",
"amount_total",
"amount_tax",
"amount_untaxed",
"residual:amount_due",
]
return to_parse

def _get_selection_label(self, invoice, field):
"""
Get the translated label of the invoice selection field
:param invoice: account.invoice recordset
:param field: str
:return: str
"""
if field not in invoice._fields:
return ""
# _description_selection return a list of tuple (str, str).
# Exactly like the definition of Selection field but this function
# translate possible values.
type_dict = dict(
invoice._fields.get(field)._description_selection(invoice.env)
)
technical_value = invoice[field]
return type_dict.get(technical_value, technical_value)

def _to_json_invoice(self, invoice):
invoice.ensure_one()
parser = self._get_parser_invoice()
values = invoice.jsonify(parser)[0]
values.update(
{
"type": self._get_selection_label(invoice, "type"),
"state": self._get_selection_label(invoice, "state"),
}
)
return values

def _to_json(self, invoices):
res = []
for invoice in invoices:
res.append(self._to_json_invoice(invoice))
return res

def _get_base_search_domain(self):
"""
Get every account.invoice (customer invoices or refunds)
related to current user.
If the current user is the anonymous one, it'll return an invalid
domain (to have 0 invoice as result)
:return:
"""
if self.shopinvader_backend.anonymous_partner_id == self.partner:
return [(0, "=", 1)]
return [
("partner_id", "=", self.partner.id),
("type", "in", ("out_invoice", "out_refund")),
("state", "in", ["open", "paid"]),
("shopinvader_backend_id", "=", self.shopinvader_backend.id),
]
3 changes: 3 additions & 0 deletions shopinvader_invoice/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Copyright 2019 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_invoice_service
185 changes: 185 additions & 0 deletions shopinvader_invoice/tests/test_invoice_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Copyright 2019 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields
from odoo.addons.shopinvader.tests.common import CommonCase


class TestInvoiceService(CommonCase):
"""
Tests for
"""

def setUp(self, *args, **kwargs):
super(TestInvoiceService, self).setUp(*args, **kwargs)
self.invoice_obj = self.env["account.invoice"]
self.partner = self.env.ref("base.res_partner_2").copy()
self.product = self.env.ref("product.product_product_4")
self.precision = 2
with self.work_on_services(partner=self.partner) as work:
self.service = work.component(usage="invoice")
with self.work_on_services(
partner=self.backend.anonymous_partner_id
) as work:
self.service_guest = work.component(usage="invoice")

def _get_selection_label(self, invoice, field):
"""
Get the translated label of the invoice selection field
:param invoice: account.invoice recordset
:param field: str
:return: str
"""
technical_type = invoice[field]
type_dict = dict(
invoice._fields.get(field)._description_selection(invoice.env)
)
return type_dict.get(technical_type, technical_type)

def _check_data_content(self, data, invoices):
"""
Check data based on given invoices
:param data: list
:param invoices: account.invoice recordset
:return: bool
"""
# To have them into correct order
invoices = invoices.search(
[
("id", "in", invoices.ids),
# Invoice must be paid to be in data
("state", "in", ["open", "paid"]),
]
)
self.assertEquals(len(data), len(invoices))
for current_data, invoice in zip(data, invoices):
state_label = self._get_selection_label(invoice, "state")
type_label = self._get_selection_label(invoice, "type")
self.assertEquals(current_data.get("invoice_id"), invoice.id)
self.assertEquals(current_data.get("number"), invoice.number)
self.assertEquals(
current_data.get("date_invoice"),
fields.Date.to_string(invoice.date_invoice),
)
self.assertEquals(current_data.get("state"), state_label)
self.assertEquals(current_data.get("type"), type_label)
self.assertEquals(
current_data.get("amount_total"), invoice.amount_total
)
self.assertEquals(
current_data.get("amount_tax"), invoice.amount_tax
)
self.assertEquals(
current_data.get("amount_untaxed"), invoice.amount_untaxed
)
self.assertEquals(current_data.get("amount_due"), invoice.residual)
return True

def _create_invoice(
self, partner=False, inv_type="out_invoice", validate=False
):
"""
Create a new invoice
:param partner: res.partner
:param inv_type: str
:param validate: bool
:return: stock.invoice recordset
"""
partner = partner or self.partner
account = self.product.categ_id.property_account_expense_categ_id
values = {
"partner_id": partner.id,
"partner_shipping_id": partner.id,
"shopinvader_backend_id": self.backend.id,
"date_invoice": fields.Date.today(),
"type": inv_type,
"invoice_line_ids": [
(
0,
False,
{
"product_id": self.product.id,
"quantity": 10,
"price_unit": 1250,
"account_id": account.id,
"name": self.product.display_name,
},
)
],
}
invoice = self.invoice_obj.create(values)
if validate:
invoice.action_invoice_open()
return invoice

def test_get_invoice_anonymous(self):
"""
Test the get on guest mode (using anonymous user).
It should not return any result, even if the anonymous user has some
invoices
:return:
"""
# Check first without invoice related to the anonymous user
result = self.service_guest.dispatch("search")
data = result.get("data", [])
self.assertFalse(data)
# Then create a invoice related to the anonymous user
invoice = self._create_invoice(
partner=self.backend.anonymous_partner_id, validate=True
)
self.assertEquals(
invoice.partner_id, self.backend.anonymous_partner_id
)
result = self.service_guest.dispatch("search")
data = result.get("data", [])
self.assertFalse(data)
return

def test_get_invoice_logged(self):
"""
Test the get on a logged user.
In the first part, the user should have any invoice.
But to the second, he should have one.
:return:
"""
# Check first without invoice related to the partner
result = self.service.dispatch("search")
data = result.get("data", [])
self.assertFalse(data)
# Then create a invoice related to partner
invoice = self._create_invoice(partner=self.service.partner)
self.assertEquals(invoice.partner_id, self.service.partner)
result = self.service.dispatch("search")
data = result.get("data", [])
self._check_data_content(data, invoice)
invoice.action_invoice_open()
result = self.service.dispatch("search")
data = result.get("data", [])
self._check_data_content(data, invoice)
return

def test_get_multi_invoice(self):
"""
Test the get on a logged user.
In the first part, the user should have any invoice.
But to the second, he should have one.
:return:
"""
invoice1 = self._create_invoice(
partner=self.service.partner, validate=True
)
invoice2 = self._create_invoice(
partner=self.service.partner, validate=True
)
invoice3 = self._create_invoice(
partner=self.service.partner, validate=True
)
invoice4 = self._create_invoice(partner=self.service.partner)
invoices = invoice1 | invoice2 | invoice3 | invoice4
self.assertEquals(invoice1.partner_id, self.service.partner)
self.assertEquals(invoice2.partner_id, self.service.partner)
self.assertEquals(invoice3.partner_id, self.service.partner)
self.assertEquals(invoice4.partner_id, self.service.partner)
result = self.service.dispatch("search")
data = result.get("data", [])
self._check_data_content(data, invoices)
return

0 comments on commit 159b7d1

Please sign in to comment.