forked from OCA/contract
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add account_analytic_analysis_recurring
- Loading branch information
unknown
committed
Feb 4, 2014
1 parent
0749629
commit 456114c
Showing
5 changed files
with
337 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,22 @@ | ||
# -*- coding: utf-8 -*- | ||
############################################################################## | ||
# | ||
# OpenERP, Open Source Management Solution | ||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>) | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
############################################################################## | ||
|
||
import account_analytic_analysis_recurring |
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,46 @@ | ||
# -*- coding: utf-8 -*- | ||
############################################################################## | ||
# | ||
# OpenERP, Open Source Management Solution | ||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
############################################################################## | ||
|
||
|
||
{ | ||
'name': 'Contracts Management recurring', | ||
'version': '0.1', | ||
'category': 'Other', | ||
'description': """ | ||
This module add a new feature in contracts to manage recurring invoice | ||
======================================================================================= | ||
This is a backport of the new V8 feature available in trunk and saas. With the V8 release this module will be deprecated. | ||
It also add a little feature, you can use #START# and #END# in the contract line to automatically insert the dates of the invoiced period. | ||
""", | ||
'author': 'Yannick Buron', | ||
'website': 'http://openerp.com', | ||
'depends': ['base', 'account_analytic_analysis'], | ||
'data': [ | ||
'account_analytic_analysis_recurring_cron.xml', | ||
'account_analytic_analysis_recurring_view.xml', | ||
], | ||
'demo': [''], | ||
'test':[], | ||
'installable': True, | ||
'images': [], | ||
} | ||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
209 changes: 209 additions & 0 deletions
209
account_analytic_analysis_recurring/account_analytic_analysis_recurring.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,209 @@ | ||
# -*- coding: utf-8 -*- | ||
############################################################################## | ||
# | ||
# OpenERP, Open Source Management Solution | ||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
############################################################################## | ||
from dateutil.relativedelta import relativedelta | ||
import datetime | ||
import logging | ||
import time | ||
|
||
from openerp.osv import osv, fields | ||
from openerp.osv.orm import intersect, except_orm | ||
import openerp.tools | ||
from openerp.tools.translate import _ | ||
|
||
from openerp.addons.decimal_precision import decimal_precision as dp | ||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
class account_analytic_invoice_line(osv.osv): | ||
_name = "account.analytic.invoice.line" | ||
|
||
def _amount_line(self, cr, uid, ids, prop, unknow_none, unknow_dict, context=None): | ||
res = {} | ||
for line in self.browse(cr, uid, ids, context=context): | ||
res[line.id] = line.quantity * line.price_unit | ||
if line.analytic_account_id.pricelist_id: | ||
cur = line.analytic_account_id.pricelist_id.currency_id | ||
res[line.id] = self.pool.get('res.currency').round(cr, uid, cur, res[line.id]) | ||
return res | ||
|
||
_columns = { | ||
'product_id': fields.many2one('product.product','Product',required=True), | ||
'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account'), | ||
'name': fields.text('Description', required=True), | ||
'quantity': fields.float('Quantity', required=True), | ||
'uom_id': fields.many2one('product.uom', 'Unit of Measure',required=True), | ||
'price_unit': fields.float('Unit Price', required=True), | ||
'price_subtotal': fields.function(_amount_line, string='Sub Total', type="float",digits_compute= dp.get_precision('Account')), | ||
} | ||
_defaults = { | ||
'quantity' : 1, | ||
} | ||
|
||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', partner_id=False, price_unit=False, pricelist_id=False, company_id=None, context=None): | ||
context = context or {} | ||
uom_obj = self.pool.get('product.uom') | ||
company_id = company_id or False | ||
context.update({'company_id': company_id, 'force_company': company_id, 'pricelist_id': pricelist_id}) | ||
|
||
if not product: | ||
return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}} | ||
if partner_id: | ||
part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) | ||
if part.lang: | ||
context.update({'lang': part.lang}) | ||
|
||
result = {} | ||
res = self.pool.get('product.product').browse(cr, uid, product, context=context) | ||
result.update({'name':res.partner_ref or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': res.list_price or 0.0}) | ||
if res.description: | ||
result['name'] += '\n'+res.description | ||
|
||
res_final = {'value':result} | ||
if result['uom_id'] != res.uom_id.id: | ||
selected_uom = uom_obj.browse(cr, uid, result['uom_id'], context=context) | ||
new_price = uom_obj._compute_price(cr, uid, res.uom_id.id, res_final['value']['price_unit'], result['uom_id']) | ||
res_final['value']['price_unit'] = new_price | ||
return res_final | ||
|
||
|
||
class account_analytic_account(osv.osv): | ||
_name = "account.analytic.account" | ||
_inherit = "account.analytic.account" | ||
|
||
_columns = { | ||
'recurring_invoice_line_ids': fields.one2many('account.analytic.invoice.line', 'analytic_account_id', 'Invoice Lines'), | ||
'recurring_invoices' : fields.boolean('Generate recurring invoices automatically'), | ||
'recurring_rule_type': fields.selection([ | ||
('daily', 'Day(s)'), | ||
('weekly', 'Week(s)'), | ||
('monthly', 'Month(s)'), | ||
('yearly', 'Year(s)'), | ||
], 'Recurrency', help="Invoice automatically repeat at specified interval"), | ||
'recurring_interval': fields.integer('Repeat Every', help="Repeat every (Days/Week/Month/Year)"), | ||
'recurring_next_date': fields.date('Date of Next Invoice'), | ||
} | ||
|
||
_defaults = { | ||
'recurring_interval': 1, | ||
'recurring_next_date': lambda *a: time.strftime('%Y-%m-%d'), | ||
'recurring_rule_type':'monthly' | ||
} | ||
|
||
def onchange_recurring_invoices(self, cr, uid, ids, recurring_invoices, date_start=False, context=None): | ||
value = {} | ||
if date_start and recurring_invoices: | ||
value = {'value': {'recurring_next_date': date_start}} | ||
return value | ||
|
||
def _prepare_invoice(self, cr, uid, contract, context=None): | ||
context = context or {} | ||
|
||
inv_obj = self.pool.get('account.invoice') | ||
journal_obj = self.pool.get('account.journal') | ||
fpos_obj = self.pool.get('account.fiscal.position') | ||
lang_obj = self.pool.get('res.lang') | ||
|
||
if not contract.partner_id: | ||
raise osv.except_osv(_('No Customer Defined!'),_("You must first select a Customer for Contract %s!") % contract.name ) | ||
|
||
fpos = contract.partner_id.property_account_position or False | ||
journal_ids = journal_obj.search(cr, uid, [('type', '=','sale'),('company_id', '=', contract.company_id.id or False)], limit=1) | ||
if not journal_ids: | ||
raise osv.except_osv(_('Error!'), | ||
_('Please define a sale journal for the company "%s".') % (contract.company_id.name or '', )) | ||
|
||
partner_payment_term = contract.partner_id.property_payment_term and contract.partner_id.property_payment_term.id or False | ||
|
||
|
||
inv_data = { | ||
'reference': contract.code or False, | ||
'account_id': contract.partner_id.property_account_receivable.id, | ||
'type': 'out_invoice', | ||
'partner_id': contract.partner_id.id, | ||
'currency_id': contract.partner_id.property_product_pricelist.id or False, | ||
'journal_id': len(journal_ids) and journal_ids[0] or False, | ||
'date_invoice': contract.recurring_next_date, | ||
'origin': contract.name, | ||
'fiscal_position': fpos and fpos.id, | ||
'payment_term': partner_payment_term, | ||
'company_id': contract.company_id.id or False, | ||
} | ||
invoice_id = inv_obj.create(cr, uid, inv_data, context=context) | ||
|
||
for line in contract.recurring_invoice_line_ids: | ||
|
||
res = line.product_id | ||
account_id = res.property_account_income.id | ||
if not account_id: | ||
account_id = res.categ_id.property_account_income_categ.id | ||
account_id = fpos_obj.map_account(cr, uid, fpos, account_id) | ||
|
||
taxes = res.taxes_id or False | ||
tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes) | ||
|
||
if 'old_date' in context: | ||
lang_ids = lang_obj.search(cr, uid, [('code', '=', contract.partner_id.lang)], context=context) | ||
format = lang_obj.browse(cr, uid, lang_ids, context=context)[0].date_format | ||
line.name = line.name.replace('#START#', context['old_date'].strftime(format)) | ||
line.name = line.name.replace('#END#', context['next_date'].strftime(format)) | ||
|
||
invoice_line_vals = { | ||
'name': line.name, | ||
'account_id': account_id, | ||
'account_analytic_id': contract.id, | ||
'price_unit': line.price_unit or 0.0, | ||
'quantity': line.quantity, | ||
'uos_id': line.uom_id.id or False, | ||
'product_id': line.product_id.id or False, | ||
'invoice_id' : invoice_id, | ||
'invoice_line_tax_id': [(6, 0, tax_id)], | ||
} | ||
self.pool.get('account.invoice.line').create(cr, uid, invoice_line_vals, context=context) | ||
|
||
inv_obj.button_compute(cr, uid, [invoice_id], context=context) | ||
return invoice_id | ||
|
||
def recurring_create_invoice(self, cr, uid, automatic=False, context=None): | ||
context = context or {} | ||
current_date = time.strftime('%Y-%m-%d') | ||
|
||
contract_ids = self.search(cr, uid, [('recurring_next_date','<=', current_date), ('state','=', 'open'), ('recurring_invoices','=', True)]) | ||
for contract in self.browse(cr, uid, contract_ids, context=context): | ||
|
||
next_date = datetime.datetime.strptime(contract.recurring_next_date or current_date, "%Y-%m-%d") | ||
interval = contract.recurring_interval | ||
if contract.recurring_rule_type == 'daily': | ||
old_date = next_date-relativedelta(days=+interval) | ||
new_date = next_date+relativedelta(days=+interval) | ||
elif contract.recurring_rule_type == 'weekly': | ||
old_date = next_date-relativedelta(weeks=+interval) | ||
new_date = next_date+relativedelta(weeks=+interval) | ||
else: | ||
old_date = next_date+relativedelta(months=+interval) | ||
new_date = next_date+relativedelta(months=+interval) | ||
|
||
context['old_date'] = old_date | ||
context['next_date'] = datetime.datetime.strptime(contract.recurring_next_date or current_date,"%Y-%m-%d") | ||
invoice_id = self._prepare_invoice(cr, uid, contract, context=context) | ||
|
||
self.write(cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, context=context) | ||
return True | ||
|
16 changes: 16 additions & 0 deletions
16
account_analytic_analysis_recurring/account_analytic_analysis_recurring_cron.xml
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,16 @@ | ||
<?xml version="1.0" encoding='UTF-8'?> | ||
<openerp> | ||
<data> | ||
|
||
<record model="ir.cron" id="account_analytic_cron_for_invoice"> | ||
<field name="name">Generate Recurring Invoices from Contracts</field> | ||
<field name="interval_number">1</field> | ||
<field name="interval_type">days</field> | ||
<field name="numbercall">-1</field> | ||
<field name="model" eval="'account.analytic.account'"/> | ||
<field name="function" eval="'recurring_create_invoice'"/> | ||
<field name="args" eval="'()'"/> | ||
</record> | ||
|
||
</data> | ||
</openerp> |
44 changes: 44 additions & 0 deletions
44
account_analytic_analysis_recurring/account_analytic_analysis_recurring_view.xml
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,44 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<openerp> | ||
<data> | ||
|
||
<record id="account_analytic_account_recurring_form_form" model="ir.ui.view"> | ||
<field name="name">account.analytic.account.invoice.recurring.form.inherit</field> | ||
<field name="model">account.analytic.account</field> | ||
<field name="inherit_id" ref="account_analytic_analysis.account_analytic_account_form_form"/> | ||
<field eval="40" name="priority"/> | ||
<field name="arch" type="xml"> | ||
<group name='invoice_on_timesheets' position='after'> | ||
<separator string="Recurring Invoices" attrs="{'invisible': [('recurring_invoices','!=',True)]}"/> | ||
<div> | ||
<field name="recurring_invoices" on_change="onchange_recurring_invoices(recurring_invoices, date_start)" class="oe_inline"/> | ||
<label for="recurring_invoices" /> | ||
<button class="oe_link" name="recurring_create_invoice" attrs="{'invisible': [('recurring_invoices','!=',True)]}" string=". create invoices" type="object" groups="base.group_no_one"/> | ||
</div> | ||
<group attrs="{'invisible': [('recurring_invoices','!=',True)]}"> | ||
<label for="recurring_interval"/> | ||
<div> | ||
<field name="recurring_interval" class="oe_inline" attrs="{'required': [('recurring_invoices', '=', True)]}"/> | ||
<field name="recurring_rule_type" class="oe_inline" attrs="{'required': [('recurring_invoices', '=', True)]}"/> | ||
</div> | ||
<field name="recurring_next_date"/> | ||
</group> | ||
<label for="recurring_invoice_line_ids" attrs="{'invisible': [('recurring_invoices','=',False)]}"/> | ||
<div attrs="{'invisible': [('recurring_invoices','=',False)]}"> | ||
<field name="recurring_invoice_line_ids"> | ||
<tree string="Account Analytic Lines" editable="bottom"> | ||
<field name="product_id" on_change="product_id_change(product_id, uom_id, quantity, name, parent.partner_id, price_unit, parent.pricelist_id, parent.company_id)"/> | ||
<field name="name"/> | ||
<field name="quantity"/> | ||
<field name="uom_id"/> | ||
<field name="price_unit"/> | ||
<field name="price_subtotal"/> | ||
</tree> | ||
</field> | ||
</div> | ||
</group> | ||
</field> | ||
</record> | ||
|
||
</data> | ||
</openerp> |