diff --git a/contract/__openerp__.py b/contract/__openerp__.py
index bc9a51cb..c64a0d98 100644
--- a/contract/__openerp__.py
+++ b/contract/__openerp__.py
@@ -5,7 +5,7 @@
{
'name': 'Contracts Management recurring',
- 'version': '9.0.1.0.0',
+ 'version': '9.0.1.1.0',
'category': 'Contract Management',
'license': 'AGPL-3',
'author': "OpenERP SA,"
diff --git a/contract/data/contract_cron.xml b/contract/data/contract_cron.xml
index 95ae54de..26442c0d 100644
--- a/contract/data/contract_cron.xml
+++ b/contract/data/contract_cron.xml
@@ -8,7 +8,7 @@
days
-1
-
+
diff --git a/contract/i18n/es.po b/contract/i18n/es.po
index 878d56ea..af2995c7 100644
--- a/contract/i18n/es.po
+++ b/contract/i18n/es.po
@@ -1,21 +1,23 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * contract
-#
+#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: contract (9.0)\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-09-11 02:47+0000\n"
-"PO-Revision-Date: 2016-09-16 21:45+0000\n"
-"Last-Translator: OCA Transbot \n"
-"Language-Team: Spanish (http://www.transifex.com/oca/OCA-contract-9-0/language/es/)\n"
+"POT-Creation-Date: 2016-09-25 22:56+0000\n"
+"PO-Revision-Date: 2016-09-26 00:56+0100\n"
+"Last-Translator: Carlos Incaser \n"
+"Language-Team: Spanish (http://www.transifex.com/oca/OCA-contract-9-0/"
+"language/es/)\n"
+"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: es\n"
+"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 1.5.4\n"
#. module: contract
#: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form
@@ -51,7 +53,6 @@ msgstr "Contrato"
#. module: contract
#: model:ir.actions.act_window,name:contract.action_account_analytic_overdue_all
#: model:ir.ui.menu,name:contract.menu_action_account_analytic_overdue_all
-#: model:ir.ui.menu,name:contract.menu_config_contract
msgid "Contracts"
msgstr "Contratos"
@@ -96,7 +97,7 @@ msgid "Discount (%)"
msgstr "Descuento (%)"
#. module: contract
-#: code:addons/contract/models/contract.py:59
+#: code:addons/contract/models/contract.py:60
#, python-format
msgid "Discount should be less or equal to 100"
msgstr "El descuento debería ser menor o igual a 100"
@@ -106,7 +107,9 @@ msgstr "El descuento debería ser menor o igual a 100"
msgid ""
"Discount that is applied in generated invoices. It should be less or equal "
"to 100"
-msgstr "Descuento que es aplicado en las facturas generadas. Debería ser menor o igual a 100"
+msgstr ""
+"Descuento que es aplicado en las facturas generadas. Debería ser menor o "
+"igual a 100"
#. module: contract
#: model:ir.model.fields,field_description:contract.field_account_analytic_invoice_line_display_name
@@ -166,24 +169,40 @@ msgstr "Última actualización en"
#. module: contract
#: model:ir.ui.view,arch_db:contract.account_analytic_account_recurring_form_form
msgid "Legend (for the markers inside invoice lines description)"
-msgstr "Leyenda (para los marcadores dentro de descripción en lineas de factura)"
+msgstr ""
+"Leyenda (para los marcadores dentro de descripción en lineas de factura)"
#. module: contract
#: selection:account.analytic.account,recurring_rule_type:0
msgid "Month(s)"
msgstr "Mes(es)"
+#. module: contract
+#: selection:account.analytic.account,recurring_rule_type:0
+msgid "Month(s) last day"
+msgstr "Mes(es) último día"
+
#. module: contract
#: model:ir.ui.view,arch_db:contract.view_account_analytic_account_contract_search
msgid "Next Invoice"
msgstr "Próxima factura"
#. module: contract
-#: code:addons/contract/models/contract.py:196
+#: code:addons/contract/models/contract.py:230
#, python-format
msgid "Please define a sale journal for the company '%s'."
msgstr "Por favor define un diario de ventas para la compañía '%s'."
+#. module: contract
+#: selection:account.analytic.account,recurring_invoicing_type:0
+msgid "Post-paid"
+msgstr "Pospago"
+
+#. module: contract
+#: selection:account.analytic.account,recurring_invoicing_type:0
+msgid "Pre-paid"
+msgstr "Prepago"
+
#. module: contract
#: model:ir.model.fields,field_description:contract.field_account_analytic_account_pricelist_id
msgid "Pricelist"
@@ -199,6 +218,11 @@ msgstr "Producto"
msgid "Quantity"
msgstr "Cantidad"
+#. module: contract
+#: model:ir.model.fields,field_description:contract.field_account_analytic_account_recurring_invoicing_type
+msgid "Invoicing type"
+msgstr "Tipo de facturación"
+
#. module: contract
#: model:ir.model.fields,field_description:contract.field_account_analytic_account_recurring_rule_type
msgid "Recurrency"
@@ -225,6 +249,12 @@ msgstr "Repetir cada (días/semana/mes/año)"
msgid "Specify Interval for automatic invoice generation."
msgstr "Especifica el intervalo para la generación de facturas automática."
+#. module: contract
+#: model:ir.model.fields,help:contract.field_account_analytic_account_recurring_invoicing_type
+msgid "Specify if process date is 'from' or 'to' invoicing date"
+msgstr ""
+"Especifica si la fecha de proceso es desde o hasta la fecha de facturación"
+
#. module: contract
#: model:ir.model.fields,field_description:contract.field_account_analytic_invoice_line_price_subtotal
msgid "Sub Total"
@@ -251,7 +281,7 @@ msgid "Year(s)"
msgstr "Año(s)"
#. module: contract
-#: code:addons/contract/models/contract.py:188
+#: code:addons/contract/models/contract.py:222
#, python-format
msgid "You must first select a Customer for Contract %s!"
msgstr "¡Seleccione un cliente para este contrato %s!"
diff --git a/contract/models/contract.py b/contract/models/contract.py
index 33b72d37..ebd61df6 100644
--- a/contract/models/contract.py
+++ b/contract/models/contract.py
@@ -7,7 +7,6 @@
from dateutil.relativedelta import relativedelta
import logging
-import time
from openerp import api, fields, models
from openerp.addons.decimal_precision import decimal_precision as dp
@@ -52,11 +51,13 @@ class AccountAnalyticInvoiceLine(models.Model):
else:
line.price_subtotal = subtotal
- @api.one
+ @api.multi
@api.constrains('discount')
def _check_discount(self):
- if self.discount > 100:
- raise ValidationError(_("Discount should be less or equal to 100"))
+ for line in self:
+ if line.discount > 100:
+ raise ValidationError(
+ _("Discount should be less or equal to 100"))
@api.multi
@api.onchange('product_id')
@@ -109,6 +110,7 @@ class AccountAnalyticAccount(models.Model):
recurring_invoice_line_ids = fields.One2many(
comodel_name='account.analytic.invoice.line',
inverse_name='analytic_account_id',
+ copy=True,
string='Invoice Lines')
recurring_invoices = fields.Boolean(
string='Generate recurring invoices automatically')
@@ -116,11 +118,19 @@ class AccountAnalyticAccount(models.Model):
[('daily', 'Day(s)'),
('weekly', 'Week(s)'),
('monthly', 'Month(s)'),
+ ('monthlylastday', 'Month(s) last day'),
('yearly', 'Year(s)'),
],
default='monthly',
string='Recurrency',
help="Specify Interval for automatic invoice generation.")
+ recurring_invoicing_type = fields.Selection(
+ [('pre-paid', 'Pre-paid'),
+ ('post-paid', 'Post-paid'),
+ ],
+ default='pre-paid',
+ string='Invoicing type',
+ help="Specify if process date is 'from' or 'to' invoicing date")
recurring_interval = fields.Integer(
default=1,
string='Repeat Every',
@@ -144,12 +154,35 @@ class AccountAnalyticAccount(models.Model):
if self.date_start and self.recurring_invoices:
self.recurring_next_date = self.date_start
+ @api.model
+ def get_relalive_delta(self, recurring_rule_type, interval):
+ if recurring_rule_type == 'daily':
+ return relativedelta(days=interval)
+ elif recurring_rule_type == 'weekly':
+ return relativedelta(weeks=interval)
+ elif recurring_rule_type == 'monthly':
+ return relativedelta(months=interval)
+ elif recurring_rule_type == 'monthlylastday':
+ return relativedelta(months=interval, day=31)
+ else:
+ return relativedelta(years=interval)
+
@api.model
def _insert_markers(self, line, date_start, next_date, date_format):
- line = line.replace('#START#', date_start.strftime(date_format))
- date_end = next_date - relativedelta(days=1)
- line = line.replace('#END#', date_end.strftime(date_format))
- return line
+ contract = line.analytic_account_id
+ if contract.recurring_invoicing_type == 'pre-paid':
+ date_from = date_start
+ date_to = next_date - relativedelta(days=1)
+ else:
+ date_from = (date_start -
+ self.get_relalive_delta(contract.recurring_rule_type,
+ contract.recurring_interval) +
+ relativedelta(days=1))
+ date_to = date_start
+ name = line.name
+ name = name.replace('#START#', date_from.strftime(date_format))
+ name = name.replace('#END#', date_to.strftime(date_format))
+ return name
@api.model
def _prepare_invoice_line(self, line, invoice_id):
@@ -172,7 +205,7 @@ class AccountAnalyticAccount(models.Model):
[('code', '=', contract.partner_id.lang)])
date_format = lang.date_format or '%m/%d/%Y'
name = self._insert_markers(
- name, self.env.context['old_date'],
+ line, self.env.context['old_date'],
self.env.context['next_date'], date_format)
invoice_line_vals.update({
'name': name,
@@ -181,68 +214,61 @@ class AccountAnalyticAccount(models.Model):
})
return invoice_line_vals
- @api.model
- def _prepare_invoice(self, contract):
- if not contract.partner_id:
+ @api.multi
+ def _prepare_invoice(self):
+ self.ensure_one()
+ if not self.partner_id:
raise ValidationError(
_("You must first select a Customer for Contract %s!") %
- contract.name)
- journal = contract.journal_id or self.env['account.journal'].search(
+ self.name)
+ journal = self.journal_id or self.env['account.journal'].search(
[('type', '=', 'sale'),
- ('company_id', '=', contract.company_id.id)],
+ ('company_id', '=', self.company_id.id)],
limit=1)
if not journal:
raise ValidationError(
_("Please define a sale journal for the company '%s'.") %
- (contract.company_id.name or '',))
+ (self.company_id.name or '',))
currency = (
- contract.pricelist_id.currency_id or
- contract.partner_id.property_product_pricelist.currency_id or
- contract.company_id.currency_id
+ self.pricelist_id.currency_id or
+ self.partner_id.property_product_pricelist.currency_id or
+ self.company_id.currency_id
)
invoice = self.env['account.invoice'].new({
- 'reference': contract.code,
+ 'reference': self.code,
'type': 'out_invoice',
- 'partner_id': contract.partner_id.address_get(
+ 'partner_id': self.partner_id.address_get(
['invoice'])['invoice'],
'currency_id': currency.id,
'journal_id': journal.id,
- 'date_invoice': contract.recurring_next_date,
- 'origin': contract.name,
- 'company_id': contract.company_id.id,
- 'contract_id': contract.id,
+ 'date_invoice': self.recurring_next_date,
+ 'origin': self.name,
+ 'company_id': self.company_id.id,
+ 'contract_id': self.id,
+ 'user_id': self.partner_id.user_id.id,
})
# Get other invoice values from partner onchange
invoice._onchange_partner_id()
return invoice._convert_to_write(invoice._cache)
- @api.model
- def _create_invoice(self, contract):
- invoice_vals = self._prepare_invoice(contract)
+ @api.multi
+ def _create_invoice(self):
+ self.ensure_one()
+ invoice_vals = self._prepare_invoice()
invoice = self.env['account.invoice'].create(invoice_vals)
- for line in contract.recurring_invoice_line_ids:
+ for line in self.recurring_invoice_line_ids:
invoice_line_vals = self._prepare_invoice_line(line, invoice.id)
self.env['account.invoice.line'].create(invoice_line_vals)
invoice.compute_taxes()
return invoice
- @api.model
- def recurring_create_invoice(self, automatic=False):
- current_date = time.strftime('%Y-%m-%d')
- contracts = self.search(
- [('recurring_next_date', '<=', current_date),
- ('account_type', '=', 'normal'),
- ('recurring_invoices', '=', True)])
- for contract in contracts:
+ @api.multi
+ def recurring_create_invoice(self):
+ for contract in self:
old_date = fields.Date.from_string(
contract.recurring_next_date or fields.Date.today())
- interval = contract.recurring_interval
- if contract.recurring_rule_type == 'daily':
- new_date = old_date + relativedelta(days=interval)
- elif contract.recurring_rule_type == 'weekly':
- new_date = old_date + relativedelta(weeks=interval)
- else:
- new_date = old_date + relativedelta(months=interval)
+ new_date = old_date + self.get_relalive_delta(
+ contract.recurring_rule_type, contract.recurring_interval)
ctx = self.env.context.copy()
ctx.update({
'old_date': old_date,
@@ -251,9 +277,16 @@ class AccountAnalyticAccount(models.Model):
'force_company': contract.company_id.id,
})
# Re-read contract with correct company
- contract = contract.with_context(ctx)
- self.with_context(ctx)._create_invoice(contract)
+ contract.with_context(ctx)._create_invoice()
contract.write({
'recurring_next_date': new_date.strftime('%Y-%m-%d')
})
return True
+
+ @api.model
+ def cron_recurring_create_invoice(self):
+ contracts = self.search(
+ [('recurring_next_date', '<=', fields.date.today()),
+ ('account_type', '=', 'normal'),
+ ('recurring_invoices', '=', True)])
+ return contracts.recurring_create_invoice()
diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py
index 7adb7335..a04b8511 100644
--- a/contract/tests/test_contract.py
+++ b/contract/tests/test_contract.py
@@ -2,9 +2,6 @@
# © 2016 Carlos Dauden
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-from dateutil.relativedelta import relativedelta
-import datetime
-
from openerp.exceptions import ValidationError
from openerp.tests.common import TransactionCase
@@ -15,14 +12,14 @@ class TestContract(TransactionCase):
super(TestContract, self).setUp()
self.partner = self.env.ref('base.res_partner_2')
self.product = self.env.ref('product.product_product_2')
- self.tax = self.env.ref('l10n_generic_coa.sale_tax_template')
- self.product.taxes_id = self.tax.ids
self.product.description_sale = 'Test description sale'
self.contract = self.env['account.analytic.account'].create({
'name': 'Test Contract',
'partner_id': self.partner.id,
'pricelist_id': self.partner.property_product_pricelist.id,
'recurring_invoices': True,
+ 'date_start': '2016-02-15',
+ 'recurring_next_date': '2016-02-29',
})
self.contract_line = self.env['account.analytic.invoice.line'].create({
'analytic_account_id': self.contract.id,
@@ -33,11 +30,6 @@ class TestContract(TransactionCase):
'price_unit': 100,
'discount': 50,
})
- self.current_date = datetime.date.today()
- self.contract_daily = self.contract.copy()
- self.contract_daily.recurring_rule_type = 'daily'
- self.contract_weekly = self.contract.copy()
- self.contract_weekly.recurring_rule_type = 'weekly'
def test_check_discount(self):
with self.assertRaises(ValidationError):
@@ -58,35 +50,53 @@ class TestContract(TransactionCase):
self.invoice_monthly = self.env['account.invoice'].search(
[('contract_id', '=', self.contract.id)])
self.assertTrue(self.invoice_monthly)
- new_date = self.current_date + relativedelta(
- months=self.contract.recurring_interval)
- self.assertEqual(self.contract.recurring_next_date,
- new_date.strftime('%Y-%m-%d'))
+ self.assertEqual(self.contract.recurring_next_date, '2016-03-29')
self.inv_line = self.invoice_monthly.invoice_line_ids[0]
self.assertAlmostEqual(self.inv_line.price_subtotal, 50.0)
- self.assertTrue(self.inv_line.invoice_line_tax_ids)
+ self.assertEqual(self.contract.partner_id.user_id,
+ self.invoice_monthly.user_id)
def test_contract_daily(self):
- self.contract_daily.pricelist_id = False
- self.contract_daily.recurring_create_invoice()
+ self.contract.recurring_next_date = '2016-02-29'
+ self.contract.recurring_rule_type = 'daily'
+ self.contract.pricelist_id = False
+ self.contract.cron_recurring_create_invoice()
invoice_daily = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract_daily.id)])
+ [('contract_id', '=', self.contract.id)])
self.assertTrue(invoice_daily)
- new_date = self.current_date + relativedelta(
- days=self.contract_daily.recurring_interval)
- self.assertEqual(self.contract_daily.recurring_next_date,
- new_date.strftime('%Y-%m-%d'))
+ self.assertEqual(self.contract.recurring_next_date, '2016-03-01')
def test_contract_weekly(self):
- self.contract_weekly.recurring_create_invoice()
+ self.contract.recurring_next_date = '2016-02-29'
+ self.contract.recurring_rule_type = 'weekly'
+ self.contract.recurring_invoicing_type = 'post-paid'
+ self.contract.recurring_create_invoice()
+ invoices_weekly = self.env['account.invoice'].search(
+ [('contract_id', '=', self.contract.id)])
+ self.assertTrue(invoices_weekly)
+ self.assertEqual(
+ self.contract.recurring_next_date, '2016-03-07')
+
+ def test_contract_yearly(self):
+ self.contract.recurring_next_date = '2016-02-29'
+ self.contract.recurring_rule_type = 'yearly'
+ self.contract.recurring_create_invoice()
invoices_weekly = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract_weekly.id)])
+ [('contract_id', '=', self.contract.id)])
self.assertTrue(invoices_weekly)
- new_date = self.current_date + relativedelta(
- weeks=self.contract_weekly.recurring_interval)
- self.assertEqual(self.contract_weekly.recurring_next_date,
- new_date.strftime('%Y-%m-%d'))
+ self.assertEqual(
+ self.contract.recurring_next_date, '2017-02-28')
+
+ def test_contract_monthly_lastday(self):
+ self.contract.recurring_next_date = '2016-02-29'
+ self.contract.recurring_invoicing_type = 'post-paid'
+ self.contract.recurring_rule_type = 'monthlylastday'
+ self.contract.recurring_create_invoice()
+ invoices_monthly_lastday = self.env['account.invoice'].search(
+ [('contract_id', '=', self.contract.id)])
+ self.assertTrue(invoices_monthly_lastday)
+ self.assertEqual(self.contract.recurring_next_date, '2016-03-31')
def test_onchange_partner_id(self):
self.contract._onchange_partner_id()
diff --git a/contract/views/contract.xml b/contract/views/contract.xml
index ca5d34ff..93b2f6b6 100644
--- a/contract/views/contract.xml
+++ b/contract/views/contract.xml
@@ -32,7 +32,7 @@
attrs="{'invisible': [('recurring_invoices','!=',True)]}"
string="⇒ Show recurring invoices" class="oe_link"/>
-
+
@@ -40,6 +40,7 @@
+