[IMP] analytic: in-depth refactoring

* analytic accounts can be shared among companies
* amount on analytic lines always refer to amount in company currency of which it belongs to
* add analytic groups (hierarchical way to classify analytic accounts)
* analytic tags:
- only present on analytic lines, not anymore on analytic accounts
- can be used to distribue/split an analytic lines among several analytic accounts
- enabled by a dedicated res.group
- added on several objects (vouchers, expenses, analytic defaults...) as well as in bank statement reconciliation widget (manual counterpart and account.reconcile.model)

* bunch of fixes made related to analytic

Was PR #22124. Was task 32386. Original work by tbe.
This commit is contained in:
Joren Van Onder
2018-01-31 15:53:04 +01:00
committed by qdp-odoo
parent cd30db60e6
commit 079a13a349
32 changed files with 374 additions and 140 deletions
+6 -1
View File
@@ -864,7 +864,10 @@ class AccountTax(models.Model):
def get_grouping_key(self, invoice_tax_val):
""" Returns a string that will be used to group account.invoice.tax sharing the same properties"""
self.ensure_one()
return str(invoice_tax_val['tax_id']) + '-' + str(invoice_tax_val['account_id']) + '-' + str(invoice_tax_val['account_analytic_id'])
return str(invoice_tax_val['tax_id']) + '-' + \
str(invoice_tax_val['account_id']) + '-' + \
str(invoice_tax_val['account_analytic_id']) + '-' + \
str(invoice_tax_val.get('analytic_tag_ids', []))
def _compute_amount(self, base_amount, price_unit, quantity=1.0, product=None, partner=None):
""" Returns the amount of a single tax. base_amount is the actual amount on which the tax is applied, which is
@@ -1048,6 +1051,7 @@ class AccountReconcileModel(models.Model):
amount = fields.Float(digits=0, required=True, default=100.0, help="Fixed amount will count as a debit if it is negative, as a credit if it is positive.")
tax_id = fields.Many2one('account.tax', string='Tax', ondelete='restrict')
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', ondelete='set null')
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
second_account_id = fields.Many2one('account.account', string='Second Account', ondelete='cascade', domain=[('deprecated', '=', False)])
second_journal_id = fields.Many2one('account.journal', string='Second Journal', ondelete='cascade', help="This field is ignored in a bank statement reconciliation.")
@@ -1059,6 +1063,7 @@ class AccountReconcileModel(models.Model):
second_amount = fields.Float(string='Second Amount', digits=0, required=True, default=100.0, help="Fixed amount will count as a debit if it is negative, as a credit if it is positive.")
second_tax_id = fields.Many2one('account.tax', string='Second Tax', ondelete='restrict', domain=[('type_tax_use', '=', 'purchase')])
second_analytic_account_id = fields.Many2one('account.analytic.account', string='Second Analytic Account', ondelete='set null')
second_analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Second Analytic Tags')
@api.onchange('name')
def onchange_name(self):
@@ -13,7 +13,7 @@ class AccountAnalyticLine(models.Model):
product_id = fields.Many2one('product.product', string='Product')
general_account_id = fields.Many2one('account.account', string='Financial Account', ondelete='restrict', readonly=True,
related='move_id.account_id', store=True, domain=[('deprecated', '=', False)])
move_id = fields.Many2one('account.move.line', string='Move Line', ondelete='cascade', index=True)
move_id = fields.Many2one('account.move.line', string='Journal Item', ondelete='cascade', index=True)
code = fields.Char(size=8)
ref = fields.Char(string='Ref.')
+7 -3
View File
@@ -940,6 +940,7 @@ class AccountInvoice(models.Model):
'sequence': tax['sequence'],
'account_analytic_id': tax['analytic'] and line.account_analytic_id.id or False,
'account_id': self.type in ('out_invoice', 'in_invoice') and (tax['account_id'] or line.account_id.id) or (tax['refund_account_id'] or line.account_id.id),
'analytic_tag_ids': tax['analytic'] and line.analytic_tag_ids.ids or False,
}
# If the taxes generate moves on the same financial account as the invoice line,
@@ -948,7 +949,6 @@ class AccountInvoice(models.Model):
# to ensure the tax move is allocated to the proper analytic account.
if not vals.get('account_analytic_id') and line.account_analytic_id and vals['account_id'] == line.account_id.id:
vals['account_analytic_id'] = line.account_analytic_id.id
return vals
@api.multi
@@ -1056,9 +1056,9 @@ class AccountInvoice(models.Model):
'product_id': line.product_id.id,
'uom_id': line.uom_id.id,
'account_analytic_id': line.account_analytic_id.id,
'analytic_tag_ids': analytic_tag_ids,
'tax_ids': tax_ids,
'invoice_id': self.id,
'analytic_tag_ids': analytic_tag_ids
}
res.append(move_line_dict)
return res
@@ -1075,6 +1075,8 @@ class AccountInvoice(models.Model):
if tax.amount_type == "group":
for child_tax in tax.children_tax_ids:
done_taxes.append(child_tax.id)
analytic_tag_ids = [(4, analytic_tag.id, None) for analytic_tag in tax_line.analytic_tag_ids]
res.append({
'invoice_tax_line_id': tax_line.id,
'tax_line_id': tax_line.tax_id.id,
@@ -1085,6 +1087,7 @@ class AccountInvoice(models.Model):
'price': tax_line.amount_total,
'account_id': tax_line.account_id.id,
'account_analytic_id': tax_line.account_analytic_id.id,
'analytic_tag_ids': analytic_tag_ids,
'invoice_id': self.id,
'tax_ids': [(6, 0, list(done_taxes))] if tax_line.tax_id.include_base_amount else []
})
@@ -1261,7 +1264,6 @@ class AccountInvoice(models.Model):
'debit': line['price'] > 0 and line['price'],
'credit': line['price'] < 0 and -line['price'],
'account_id': line['account_id'],
'analytic_line_ids': line.get('analytic_line_ids', []),
'amount_currency': line['price'] > 0 and abs(line.get('amount_currency', False)) or -abs(line.get('amount_currency', False)),
'currency_id': line.get('currency_id', False),
'quantity': line.get('quantity', 1.00),
@@ -1733,6 +1735,7 @@ class AccountInvoiceTax(models.Model):
'tax_id': tax.tax_id.id,
'account_id': tax.account_id.id,
'account_analytic_id': tax.account_analytic_id.id,
'analytic_tag_ids': tax.analytic_tag_ids.ids or False,
})
if tax.invoice_id and key in tax_grouped[tax.invoice_id.id]:
tax.base = tax_grouped[tax.invoice_id.id][key]['base']
@@ -1744,6 +1747,7 @@ class AccountInvoiceTax(models.Model):
tax_id = fields.Many2one('account.tax', string='Tax', ondelete='restrict')
account_id = fields.Many2one('account.account', string='Tax Account', required=True, domain=[('deprecated', '=', False)])
account_analytic_id = fields.Many2one('account.analytic.account', string='Analytic account')
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
amount = fields.Monetary('Tax Amount')
amount_rounding = fields.Monetary('Amount Delta')
amount_total = fields.Monetary(string="Amount Total", compute='_compute_amount_total')
+43 -7
View File
@@ -173,6 +173,8 @@ class AccountMove(models.Model):
for move in self:
if not move.journal_id.update_posted:
raise UserError(_('You cannot modify a posted entry of this journal.\nFirst you should set the journal to allow cancelling entries.'))
# We remove all the analytics entries for this journal
move.mapped('line_ids.analytic_line_ids').unlink()
if self.ids:
self.check_access_rights('write')
self.check_access_rule('write')
@@ -469,7 +471,7 @@ class AccountMoveLine(models.Model):
tax_ids = fields.Many2many('account.tax', string='Taxes')
tax_line_id = fields.Many2one('account.tax', string='Originator tax', ondelete='restrict')
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account')
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic tags')
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
company_id = fields.Many2one('res.company', related='account_id.company_id', string='Company', store=True)
counterpart = fields.Char("Counterpart", compute='_get_counterpart', help="Compute the counter part accounts of this journal item for this journal entry. This can be needed in reports.")
@@ -1452,13 +1454,19 @@ class AccountMoveLine(models.Model):
currency_id = invoice_currency.id
return debit, credit, amount_currency, currency_id
def _get_analytic_tag_ids(self):
self.ensure_one()
return self.analytic_tag_ids.filtered(lambda r: not r.active_analytic_distribution).ids
@api.multi
def create_analytic_lines(self):
""" Create analytic items upon validation of an account.move.line having an analytic account. This
method first remove any existing analytic item related to the line before creating any new one.
""" Create analytic items upon validation of an account.move.line having an analytic account or an analytic distribution.
"""
self.mapped('analytic_line_ids').unlink()
for obj_line in self:
for tag in obj_line.analytic_tag_ids.filtered('active_analytic_distribution'):
for distribution in tag.analytic_distribution_ids:
vals_line = obj_line._prepare_analytic_distribution_line(distribution)
self.env['account.analytic.line'].create(vals_line)
if obj_line.analytic_account_id:
vals_line = obj_line._prepare_analytic_line()[0]
self.env['account.analytic.line'].create(vals_line)
@@ -1473,16 +1481,40 @@ class AccountMoveLine(models.Model):
'name': self.name,
'date': self.date,
'account_id': self.analytic_account_id.id,
'tag_ids': [(6, 0, self.analytic_tag_ids.ids)],
'tag_ids': [(6, 0, self._get_analytic_tag_ids())],
'unit_amount': self.quantity,
'product_id': self.product_id and self.product_id.id or False,
'product_uom_id': self.product_uom_id and self.product_uom_id.id or False,
'amount': self.company_currency_id.with_context(date=self.date or fields.Date.context_today(self)).compute(amount, self.analytic_account_id.currency_id) if self.analytic_account_id.currency_id else amount,
'amount': amount,
'general_account_id': self.account_id.id,
'ref': self.ref,
'move_id': self.id,
'user_id': self.invoice_id.user_id.id or self._uid,
'partner_id': self.partner_id.id,
'company_id': self.env.user.company_id.id,
}
def _prepare_analytic_distribution_line(self, distribution):
""" Prepare the values used to create() an account.analytic.line upon validation of an account.move.line having
analytic tags with analytic distribution.
"""
self.ensure_one()
amount = -self.balance * distribution.percentage / 100.0
return {
'name': self.name,
'date': self.date,
'account_id': distribution.account_id.id,
'partner_id': self.partner_id.id,
'tag_ids': [(6, 0, [distribution.tag_id.id] + self._get_analytic_tag_ids())],
'unit_amount': self.quantity,
'product_id': self.product_id and self.product_id.id or False,
'product_uom_id': self.product_uom_id and self.product_uom_id.id or False,
'amount': amount,
'general_account_id': self.account_id.id,
'ref': self.ref,
'move_id': self.id,
'user_id': self.invoice_id.user_id.id or self._uid,
'company_id': self.env.user.company_id.id
}
@api.model
@@ -1528,7 +1560,7 @@ class AccountMoveLine(models.Model):
domain += [('account_id', 'in', context['account_ids'].ids)]
if context.get('analytic_tag_ids'):
domain += ['|', ('analytic_account_id.tag_ids', 'in', context['analytic_tag_ids'].ids), ('analytic_tag_ids', 'in', context['analytic_tag_ids'].ids)]
domain += [('analytic_tag_ids', 'in', context['analytic_tag_ids'].ids)]
if context.get('analytic_account_ids'):
domain += [('analytic_account_id', 'in', context['analytic_account_ids'].ids)]
@@ -1689,6 +1721,8 @@ class AccountPartialReconcile(models.Model):
'debit': abs(rounded_amt) if rounded_amt < 0 else 0.0,
'credit': rounded_amt if rounded_amt > 0 else 0.0,
'account_id': line.account_id.id,
'analytic_account_id': line.analytic_account_id.id,
'analytic_tag_ids': line.analytic_tag_ids.ids,
'tax_exigible': True,
'amount_currency': self.amount_currency and line.currency_id.round(-line.amount_currency * amount / line.balance) or 0.0,
'currency_id': line.currency_id.id,
@@ -1701,6 +1735,8 @@ class AccountPartialReconcile(models.Model):
'debit': rounded_amt if rounded_amt > 0 else 0.0,
'credit': abs(rounded_amt) if rounded_amt < 0 else 0.0,
'account_id': line.tax_line_id.cash_basis_account.id,
'analytic_account_id': line.analytic_account_id.id,
'analytic_tag_ids': line.analytic_tag_ids.ids,
'tax_line_id': line.tax_line_id.id,
'tax_exigible': True,
'amount_currency': self.amount_currency and line.currency_id.round(line.amount_currency * amount / line.balance) or 0.0,
@@ -28,6 +28,7 @@ class ResConfigSettings(models.TransientModel):
module_account_accountant = fields.Boolean(string='Accounting')
group_analytic_accounting = fields.Boolean(string='Analytic Accounting',
implied_group='analytic.group_analytic_accounting')
group_analytic_tags = fields.Boolean(string='Analytic Tags', implied_group='analytic.group_analytic_tags')
group_warning_account = fields.Boolean(string="Warnings in Invoices", implied_group='account.group_warning_account')
group_cash_rounding = fields.Boolean(string="Cash Rounding", implied_group='account.group_cash_rounding')
module_account_asset = fields.Boolean(string='Assets Management')
@@ -81,6 +81,8 @@ var _t = core._t;
* id: integer
* display_name: string
* }
* analytic_tag_ids: {
* }
* label: string
* amount: number,
* [journal_id]: {
@@ -92,7 +94,7 @@ var _t = core._t;
*/
var StatementModel = BasicModel.extend({
avoidCreate: false,
quickCreateFields: ['account_id', 'amount', 'analytic_account_id', 'label', 'tax_id'],
quickCreateFields: ['account_id', 'amount', 'analytic_account_id', 'label', 'tax_id', 'analytic_tag_ids'],
/**
* @override
@@ -449,7 +451,7 @@ var StatementModel = BasicModel.extend({
quickCreateProposition: function (handle, reconcileModelId) {
var line = this.getLine(handle);
var reconcileModel = _.find(this.reconcileModels, function (r) {return r.id === reconcileModelId;});
var fields = ['account_id', 'amount', 'amount_type', 'analytic_account_id', 'journal_id', 'label', 'tax_id'];
var fields = ['account_id', 'amount', 'amount_type', 'analytic_account_id', 'journal_id', 'label', 'tax_id', 'analytic_tag_ids'];
this._blurProposition(handle);
var focus = this._formatQuickCreate(line, _.pick(reconcileModel, fields));
@@ -555,6 +557,7 @@ var StatementModel = BasicModel.extend({
* @returns {Deferred}
*/
updateProposition: function (handle, values) {
var self = this;
var line = this.getLine(handle);
var prop = _.last(_.filter(line.reconciliation_proposition, '__focus'));
if (!prop) {
@@ -562,7 +565,23 @@ var StatementModel = BasicModel.extend({
line.reconciliation_proposition.push(prop);
}
_.each(values, function (value, fieldName) {
prop[fieldName] = values[fieldName];
if (fieldName === 'analytic_tag_ids') {
switch (value.operation) {
case "ADD_M2M":
if (!_.findWhere(prop.analytic_tag_ids, {id: value.ids.id})) {
prop.analytic_tag_ids.push(value.ids);
}
break;
case "FORGET":
var id = self.localData[value.ids[0]].ref;
prop.analytic_tag_ids = _.filter(prop.analytic_tag_ids, function (val) {
return val.id !== id;
});
break;
}
} else {
prop[fieldName] = values[fieldName];
}
});
if ('account_id' in values) {
prop.account_code = prop.account_id ? this.accounts[prop.account_id.id] : '';
@@ -857,6 +876,13 @@ var StatementModel = BasicModel.extend({
_formatNameGet: function (value) {
return value ? (value.id ? value : {'id': value[0], 'display_name': value[1]}) : false;
},
_formatMany2ManyTags: function (value) {
var res = [];
for (var i=0, len=value.length; i<len; i++) {
res[i] = {data: {'id': value[i][0], 'display_name': value[i][1]}};
}
return res;
},
/**
* Format each propositions (amount, label, account_id)
*
@@ -949,6 +975,7 @@ var StatementModel = BasicModel.extend({
'account_id': account,
'account_code': account ? this.accounts[account.id] : '',
'analytic_account_id': this._formatNameGet(values.analytic_account_id),
'analytic_tag_ids': this._formatMany2ManyTags(values.analytic_tag_ids || []),
'journal_id': this._formatNameGet(values.journal_id),
'tax_id': this._formatNameGet(values.tax_id),
'debit': 0,
@@ -1061,6 +1088,7 @@ var StatementModel = BasicModel.extend({
// But since we need to change the amount (and thus its semantics) into base_amount
// It might be useful to have a trace in the RPC for debugging purposes
computed_with_tax: prop.computed_with_tax,
analytic_tag_ids: [[6, null, _.pluck(prop.analytic_tag_ids, 'id')]]
};
if (!isNaN(prop.id)) {
result.counterpart_aml_id = prop.id;
@@ -1083,7 +1111,7 @@ var StatementModel = BasicModel.extend({
* datas allowing manual reconciliation
*/
var ManualModel = StatementModel.extend({
quickCreateFields: ['account_id', 'journal_id', 'amount', 'analytic_account_id', 'label', 'tax_id'],
quickCreateFields: ['account_id', 'journal_id', 'amount', 'analytic_account_id', 'label', 'tax_id', 'analytic_tag_ids'],
//--------------------------------------------------------------------------
// Public
@@ -414,7 +414,13 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
this._renderCreate(state);
}
var data = this.model.get(this.handleCreateRecord).data;
this.model.notifyChanges(this.handleCreateRecord, state.createForm);
this.model.notifyChanges(this.handleCreateRecord, {analytic_tag_ids: {operation: 'REPLACE_WITH', ids: []}});
_.each(state.createForm.analytic_tag_ids, function (tag) {
self.model.notifyChanges(self.handleCreateRecord, {analytic_tag_ids: {operation: 'ADD_M2M', ids: tag}});
});
var record = this.model.get(this.handleCreateRecord);
_.each(this.fields, function (field, fieldName) {
if (self._avoidFieldUpdate[fieldName]) return;
@@ -467,7 +473,7 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
},
/**
* create account_id, tax_id, analytic_account_id, label and amount field
* create account_id, tax_id, analytic_account_id, analytic_tag_ids, label and amount fields
*
* @private
* @param {object} state - statement line
@@ -490,6 +496,10 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
relation: 'account.analytic.account',
type: 'many2one',
name: 'analytic_account_id',
}, {
relation: 'account.analytic.tag',
type: 'many2many',
name: 'analytic_tag_ids',
}, {
type: 'char',
name: 'label',
@@ -516,6 +526,9 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
self.fields.analytic_account_id = new relational_fields.FieldMany2One(self,
'analytic_account_id', record, {mode: 'edit'});
self.fields.analytic_tag_ids = new relational_fields.FieldMany2ManyTags(self,
'analytic_tag_ids', record, {mode: 'edit'});
self.fields.label = new basic_fields.FieldChar(self,
'label', record, {mode: 'edit'});
@@ -528,6 +541,7 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
self.fields.journal_id.appendTo($create.find('.create_journal_id .o_td_field'));
self.fields.tax_id.appendTo($create.find('.create_tax_id .o_td_field'));
self.fields.analytic_account_id.appendTo($create.find('.create_analytic_account_id .o_td_field'));
self.fields.analytic_tag_ids.appendTo($create.find('.create_analytic_tag_ids .o_td_field'));
self.fields.label.appendTo($create.find('.create_label .o_td_field'))
.then(addRequiredStyle.bind(self, self.fields.label));
self.fields.amount.appendTo($create.find('.create_amount .o_td_field'))
@@ -174,10 +174,14 @@
<td class="o_td_label">Tax</td>
<td class="o_td_field"></td>
</tr>
<tr class="create_analytic_account_id">
<tr class="create_analytic_account_id" groups="analytic.group_analytic_accounting">
<td class="o_td_label">Analytic Acc.</td>
<td class="o_td_field"></td>
</tr>
<tr class="create_analytic_tag_ids" groups="analytic.group_analytic_tags">
<td class="o_td_label">Analytic Tags.</td>
<td class="o_td_field"></td>
</tr>
</table>
<table class="pull-right">
<tbody>
@@ -105,6 +105,10 @@ var db = {
{id: 9, display_name: "Delta PC - Delta PC"},
]
},
'account.analytic.tag': {
fields: {},
records: []
},
'account.bank.statement': {
fields: {},
reconciliation_widget_preprocess: function () {
@@ -572,8 +576,8 @@ QUnit.module('account', {
assert.strictEqual(widget.$('.o_input_dropdown input').val(), "Agrolait", "the partner many2one should display agrolait");
assert.strictEqual(clientAction.widgets[2].$('.o_input_dropdown input').val(), "Camptocamp", "the partner many2one should display Camptocamp");
widget.$('.accounting_view tfoot td:first').trigger('click');
assert.strictEqual(widget.$('.create .o_input').length, 6,
"create panel should display 5 fields (account_id, tax_id, analytic_account_id, label, amount)");
assert.strictEqual(widget.$('.create input.o_input').length, 7,
"create panel should contain 7 fields (account_id, tax_id, journal_id, analytic_account_id, analytic_tag_ids, label, amount)");
assert.strictEqual(widget.$('.create .create_account_id .o_required_modifier, .create .create_label .o_required_modifier, .create .create_amount .o_required_modifier').length, 3,
"account_id, label and amount should be required fields");
assert.strictEqual(widget.$('.create .create_label input').val(), 'SAJ/2014/002 and SAJ/2014/003',
@@ -643,13 +647,15 @@ QUnit.module('account', {
"counterpart_aml_id": 109,
"credit": 650,
"debit": 0,
"name": "INV/2017/0002"
"name": "INV/2017/0002",
"analytic_tag_ids": [[6, null, []]]
},
{
"counterpart_aml_id": 112,
"credit": 525,
"debit": 0,
"name": "INV/2017/0003"
"name": "INV/2017/0003",
"analytic_tag_ids": [[6, null, []]]
}],
payment_aml_ids: [], new_aml_dicts: []}]],
"Should call process_reconciliations with ids");
@@ -691,7 +697,8 @@ QUnit.module('account', {
account_id: 287,
credit: 1175,
debit: 0,
name: 'SAJ/2014/002 and SAJ/2014/003'
name: 'SAJ/2014/002 and SAJ/2014/003',
analytic_tag_ids: [[6, null, []]]
}]}]],
"Should call process_reconciliations with ids");
});
@@ -736,7 +743,8 @@ QUnit.module('account', {
counterpart_aml_id: 109,
credit: 650,
debit: 0,
name: 'INV/2017/0002'
name: 'INV/2017/0002',
analytic_tag_ids: [[6, null, []]]
}],
payment_aml_ids: [],
new_aml_dicts: [{
@@ -59,4 +59,4 @@ class AccountingTestTemplConsistency(HttpCase):
'''Test fields consistency for ('account.reconcile.model', 'account.reconcile.model.template')
'''
self.check_fields_consistency('account.reconcile.model.template', 'account.reconcile.model', exceptions=['chart_template_id'])
self.check_fields_consistency('account.reconcile.model', 'account.reconcile.model.template', exceptions=['company_id', 'journal_id', 'analytic_account_id', 'second_journal_id', 'second_analytic_account_id'])
self.check_fields_consistency('account.reconcile.model', 'account.reconcile.model.template', exceptions=['company_id', 'journal_id', 'analytic_account_id', 'second_journal_id', 'second_analytic_account_id', 'analytic_tag_ids', 'second_analytic_tag_ids'])
+14 -20
View File
@@ -8,15 +8,20 @@
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='tag_ids']" position="after">
<field name="general_account_id"/>
<field name="ref"/>
<field name="partner_id"/>
<field name="move_id" readonly="1"/>
</xpath>
<xpath expr="//field[@name='amount']" position="after">
<field name="product_id"/>
<field name="product_uom_id" class="oe_inline"/>
</xpath>
<group name="amount" position="after">
<group/> <!-- put Accounting group under Amount group -->
<group name="accounting" string="Accounting">
<field name="general_account_id"/>
<field name="move_id"/>
</group>
</group>
</data>
</field>
</record>
@@ -73,29 +78,18 @@
parent="menu_analytic_accounting"
groups="analytic.group_analytic_accounting" sequence="0"/>
<record model="ir.actions.act_window" id="analytic_line_reporting_pivot">
<field name="name">Analytic Entries</field>
<field name="res_model">account.analytic.line</field>
<field name="view_mode">pivot</field>
<field name="view_id" ref="analytic.view_account_analytic_line_pivot"/>
<field name="view_type">form</field>
<field name="search_view_id" ref="analytic.view_account_analytic_line_filter"/>
</record>
<menuitem
name="Analytic Entries"
parent="account.account_reports_management_menu"
action="analytic_line_reporting_pivot"
id="menu_action_analytic_lines_reporting"
sequence="19"
groups="analytic.group_analytic_accounting"/>
<menuitem
id="account_analytic_tag_menu"
name="Analytic Tags"
parent="menu_analytic_accounting"
action="analytic.account_analytic_tag_action"
groups="analytic.group_analytic_tags"/>
<menuitem
id="account_analytic_group_menu"
name="Analytic Account Groups"
parent="menu_analytic_accounting"
action="analytic.account_analytic_group_action"
groups="analytic.group_analytic_accounting"/>
</data>
@@ -75,7 +75,7 @@
<field domain="[('company_id', '=', parent.company_id)]" name="account_id" groups="account.group_account_user"/>
<field name="invoice_line_tax_ids" context="{'type':parent.get('type')}" domain="[('type_tax_use','!=','none'),('company_id', '=', parent.company_id)]" widget="many2many_tags" options="{'no_create': True}"/>
<field domain="[('company_id', '=', parent.company_id)]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
</group>
</group>
@@ -272,7 +272,7 @@
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"
domain="[('company_id', '=', parent.company_id)]"
context="{'default_partner_id': parent.partner_id}"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_accounting" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="quantity"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="price_unit"/>
@@ -295,6 +295,7 @@
<field name="manual" invisible="1"/>
<field name="account_id" groups="account.group_account_user"/>
<field name="account_analytic_id" domain="[('company_id', '=', parent.company_id)]" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="amount"/>
<field name="amount_rounding" invisible="1"/>
<field name="amount_total" invisible="1"/>
@@ -428,7 +429,7 @@
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"
domain="[('company_id', '=', parent.company_id)]"
context="{'default_partner_id': parent.partner_id}"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_accounting" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="quantity"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="price_unit"/>
@@ -502,6 +503,7 @@
<field name="account_id" groups="account.group_account_user"/>
<field name="amount" invisible="1"/>
<field name="account_analytic_id" domain="[('company_id', '=', parent.company_id)]" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="amount_rounding" invisible="1"/>
<field name="amount_total"/>
<field name="currency_id" invisible="1"/>
+1 -1
View File
@@ -39,7 +39,7 @@
<!-- Configuration Sub-Menu -->
<menuitem id="account_account_menu" name="Accounting" parent="account.menu_finance_configuration" sequence="2" groups="account.group_account_invoice"/>
<menuitem id="account_management_menu" name="Management" parent="account.menu_finance_configuration" sequence="3" groups="account.group_account_manager"/>
<menuitem id="menu_analytic_accounting" name="Analytic Accounting" parent="menu_finance_configuration" groups="analytic.group_analytic_accounting" sequence="5"/>
<menuitem id="menu_analytic_accounting" name="Analytic Accounting" parent="menu_finance_configuration" groups="analytic.group_analytic_accounting,analytic.group_analytic_tags" sequence="5"/>
<menuitem id="menu_action_currency_form" action="base.action_currency_form" name="Currencies" parent="account_account_menu" sequence="1" groups="base.group_multi_currency"/>
<menuitem id="menu_account_reports" name="Financial Reports" parent="menu_finance_configuration" groups="account.group_account_user"/>
+9 -6
View File
@@ -840,7 +840,8 @@
<field name="account_id" domain="[('company_id', '=', company_id)]"/>
<field name="amount_type"/>
<field name="tax_id" domain="[('company_id', '=', company_id)]" options="{'no_create': True}" />
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" domain="[('company_id', '=', company_id)]"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
<group>
@@ -862,7 +863,8 @@
<field name="second_account_id" string="Account" domain="[('company_id', '=', company_id)]"/>
<field name="second_amount_type" string="Amount type"/>
<field name="second_tax_id" string="Tax" domain="[('company_id', '=', company_id)]" widget="selection"/>
<field name="second_analytic_account_id" string="Analytic Account" groups="analytic.group_analytic_accounting" domain="[('company_id', '=', company_id)]"/>
<field name="second_analytic_account_id" string="Analytic Account" groups="analytic.group_analytic_accounting"/>
<field name="second_analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags"/>
</group>
<group>
<field name="second_label" string="Journal Item Label"/>
@@ -1185,9 +1187,9 @@
<group string="States">
<field name="blocked"/>
</group>
<group groups="analytic.group_analytic_accounting" string="Analytic">
<field name="analytic_account_id"/>
<field name="analytic_tag_ids" widget="many2many_tags"/>
<group string="Analytic" groups="analytic.group_analytic_accounting,analytic.group_analytic_tags">
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags"/>
</group>
</group>
<field name="narration" colspan="4" nolabel="1" placeholder="Add an internal note..."/>
@@ -1227,6 +1229,7 @@
<field name="partner_id"/>
<field name="account_id" options='{"no_open":True}' domain="[('company_id', '=', company_id)]" groups="account.group_account_user"/>
<field name="analytic_account_id" groups="account.group_account_user"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="reconciled" invisible="1"/>
<field name="full_reconcile_id"/>
<field name="debit" sum="Total Debit"/>
@@ -1473,7 +1476,7 @@
domain="['|', ('parent_id', '=', False), ('is_company', '=', True)]"/>
<field name="name"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags"/>
<field name="amount_currency" groups="base.group_multi_currency"/>
<field name="company_currency_id" invisible="1"/>
<field name="company_id" invisible="1"/>
@@ -359,8 +359,24 @@
<div class="mt16" id="analytic_account_link">
<button name="%(analytic.action_analytic_account_form)d" icon="fa-arrow-right" type="action" string="Analytic Accounts" class="btn-link"/>
</div>
<div>
<button name="%(analytic.account_analytic_tag_action)d" icon="fa-arrow-right" type="action" string="Tags for Multidimensional Analytics" class="btn-link"/>
<div id="analytic_account_groups_link">
<button name="%(analytic.account_analytic_group_action)d" icon="fa-arrow-right" type="action" string="Analytic Account Groups" class="btn-link"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" groups="account.group_account_user">
<div class="o_setting_left_pane">
<field name="group_analytic_tags"/>
</div>
<div class="o_setting_right_pane">
<label for="group_analytic_tags"/>
<div class="text-muted">
Allows to tag analytic entries and to manage analytic distributions
</div>
<div class="content-group" attrs="{'invisible': [('group_analytic_tags', '=', False)]}">
<div class="mt16">
<button name="%(analytic.account_analytic_tag_action)d" icon="fa-arrow-right" type="action" string="Analytic Tags" class="btn-link"/>
</div>
</div>
</div>
@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
class AccountAnalyticDefault(models.Model):
@@ -12,6 +13,7 @@ class AccountAnalyticDefault(models.Model):
sequence = fields.Integer(string='Sequence', help="Gives the sequence order when displaying a list of analytic distribution")
analytic_id = fields.Many2one('account.analytic.account', string='Analytic Account')
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
product_id = fields.Many2one('product.product', string='Product', ondelete='cascade', help="Select a product which will use analytic account specified in analytic default (e.g. create new customer invoice or Sales order if we select this product, it will automatically take this as an analytic account)")
partner_id = fields.Many2one('res.partner', string='Partner', ondelete='cascade', help="Select a partner which will use analytic account specified in analytic default (e.g. create new customer invoice or Sales order if we select this partner, it will automatically take this as an analytic account)")
user_id = fields.Many2one('res.users', string='User', ondelete='cascade', help="Select a user which will use analytic account specified in analytic default.")
@@ -19,6 +21,11 @@ class AccountAnalyticDefault(models.Model):
date_start = fields.Date(string='Start Date', help="Default start date for this Analytic Account.")
date_stop = fields.Date(string='End Date', help="Default end date for this Analytic Account.")
@api.constrains('analytic_id', 'analytic_tag_ids')
def _check_account_or_tags(self):
if any(not default.analytic_id and not default.analytic_tag_ids for default in self):
raise ValidationError(_('An analytic default requires at least an analytic account or an analytic tag.'))
@api.model
def account_get(self, product_id=None, partner_id=None, user_id=None, date=None, company_id=None):
domain = []
@@ -62,13 +69,17 @@ class AccountInvoiceLine(models.Model):
rec = self.env['account.analytic.default'].account_get(self.product_id.id, self.invoice_id.partner_id.id, self.env.uid,
fields.Date.today(), company_id=self.company_id.id)
self.account_analytic_id = rec.analytic_id.id
self.analytic_tag_ids = rec.analytic_tag_ids.ids
return res
def _set_additional_fields(self, invoice):
if not self.account_analytic_id:
if not self.account_analytic_id or not self.analytic_tag_ids:
rec = self.env['account.analytic.default'].account_get(
self.product_id.id, self.invoice_id.partner_id.id, self.env.uid,
fields.Date.today(), company_id=self.company_id.id)
if rec:
self.account_analytic_id = rec.analytic_id.id
if self.account_analytic_id:
self.account_analytic_id = rec.analytic_id.id
if self.analytic_tag_ids:
self.analytic_tag_ids = rec.analytic_tag_ids.ids
super(AccountInvoiceLine, self)._set_additional_fields(invoice)
@@ -5,8 +5,9 @@
<field name="model">account.analytic.default</field>
<field name="arch" type="xml">
<tree string="Analytic Defaults">
<field name="sequence"/>
<field name="sequence" widget="handle"/>
<field name="analytic_id" required="0" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="product_id"/>
<field name="partner_id"/>
<field name="user_id"/>
@@ -23,7 +24,8 @@
<field name="arch" type="xml">
<form string="Analytic Defaults">
<group col="4">
<field name="analytic_id" required="1" groups="analytic.group_analytic_accounting"/>
<field name="analytic_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="sequence"/>
<separator string="Conditions" colspan="4"/>
<field name="product_id"/>
@@ -280,6 +280,7 @@ class AccountVoucher(models.Model):
'move_id': move_id,
'partner_id': self.partner_id.id,
'analytic_account_id': line.account_analytic_id and line.account_analytic_id.id or False,
'analytic_tag_ids': [(6, 0, line.analytic_tag_ids.ids)],
'quantity': 1,
'credit': abs(amount) if self.voucher_type == 'sale' else 0.0,
'debit': abs(amount) if self.voucher_type == 'purchase' else 0.0,
@@ -366,6 +367,7 @@ class AccountVoucherLine(models.Model):
quantity = fields.Float(digits=dp.get_precision('Product Unit of Measure'),
required=True, default=1)
account_analytic_id = fields.Many2one('account.analytic.account', 'Analytic Account')
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
company_id = fields.Many2one('res.company', related='voucher_id.company_id', string='Company', store=True, readonly=True)
tax_ids = fields.Many2many('account.tax', string='Tax', help="Only for tax excluded from price")
currency_id = fields.Many2one('res.currency', related='voucher_id.currency_id')
@@ -86,6 +86,7 @@
<field name="account_id"/>
<field name="tax_ids" widget="many2many_tags"/>
<field name="account_analytic_id" domain="[('company_id', '=', company_id)]" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
</group>
</group>
@@ -104,6 +105,7 @@
<field name="name"/>
<field name="account_id"/>
<field name="account_analytic_id" domain="[('company_id', '=', company_id)]" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="quantity"/>
<field name="price_unit"/>
<field name="tax_ids" widget="many2many_tags"/>
@@ -252,6 +254,7 @@
<field name="name"/>
<field name="account_id" groups="account.group_account_user"/>
<field name="account_analytic_id" domain="[('company_id', '=', parent.company_id)]" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="quantity"/>
<field name="price_unit"/>
<field name="tax_ids" widget="many2many_tags" domain="[('type_tax_use','=','sale')]"/>
@@ -368,6 +371,7 @@
<field name="name"/>
<field name="account_id" groups="account.group_account_user"/>
<field name="account_analytic_id" domain="[('company_id', '=', parent.company_id)]" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="quantity"/>
<field name="price_unit"/>
<field name="tax_ids" widget="many2many_tags" domain="[('type_tax_use','=','purchase')]"/>
@@ -10,22 +10,18 @@
<record id="analytic_our_super_product" model="account.analytic.account">
<field name="name">Our Super Product</field>
<field name="partner_id" ref="base.res_partner_2"/>
<field name="tag_ids" eval="[(6,0, (ref('analytic.tag_contract'),))]"/>
</record>
<record id="analytic_seagate_p2" model="account.analytic.account">
<field name="name">Seagate P2</field>
<field name="partner_id" ref="base.res_partner_2"/>
<field name="tag_ids" eval="[(6,0, (ref('analytic.tag_contract'),))]"/>
</record>
<record id="analytic_millennium_industries" model="account.analytic.account">
<field name="name">Millennium Industries</field>
<field name="partner_id" ref="base.res_partner_3"/>
<field name="tag_ids" eval="[(6,0, (ref('analytic.tag_contract'),))]"/>
</record>
<record id="analytic_integration_c2c" model="account.analytic.account">
<field name="name">CampToCamp</field>
<field name="partner_id" ref="base.res_partner_12"/>
<field name="tag_ids" eval="[(6,0, (ref('analytic.tag_contract'),))]"/>
</record>
<record id="analytic_agrolait" model="account.analytic.account">
<field name="name">Agrolait</field>
+69 -12
View File
@@ -1,16 +1,57 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.osv import expression
from odoo.exceptions import ValidationError
class AccountAnalyticDistribution(models.Model):
_name = 'account.analytic.distribution'
_rec_name = 'account_id'
account_id = fields.Many2one('account.analytic.account', string='Analytic Account', required=True)
percentage = fields.Float(string='Percentage', required=True, default=100.0)
name = fields.Char(string='Name', related='account_id.name')
tag_id = fields.Many2one('account.analytic.tag', string="Parent tag", required=True)
_sql_constraints = [
('check_percentage', 'CHECK(percentage >= 0 AND percentage <= 100)',
'The percentage of an analytic distribution should be between 0 and 100.')
]
class AccountAnalyticTag(models.Model):
_name = 'account.analytic.tag'
_description = 'Analytic Tags'
name = fields.Char(string='Analytic Tag', index=True, required=True)
color = fields.Integer('Color Index')
active = fields.Boolean(default=True, help="Set active to false to hide the Analytic Tag without removing it.")
active_analytic_distribution = fields.Boolean('Analytic Distribution')
analytic_distribution_ids = fields.One2many('account.analytic.distribution', 'tag_id', string="Analytic Accounts")
company_id = fields.Many2one('res.company', string='Company')
class AccountAnalyticGroup(models.Model):
_name = 'account.analytic.group'
_description = 'Analytic Categories'
_parent_store = True
_rec_name = 'complete_name'
name = fields.Char(required=True)
description = fields.Text(string='Description')
parent_id = fields.Many2one('account.analytic.group', string="Parent", ondelete='cascade')
parent_left = fields.Integer('Left Parent', index=True)
parent_right = fields.Integer('Right Parent', index=True)
children_ids = fields.One2many('account.analytic.group', 'parent_id', string="Childrens")
complete_name = fields.Char('Complete Name', compute='_compute_complete_name', store=True)
company_id = fields.Many2one('res.company', string='Company')
@api.depends('name', 'parent_id.complete_name')
def _compute_complete_name(self):
for group in self:
if group.parent_id:
group.complete_name = '%s / %s' % (group.parent_id.complete_name, group.name)
else:
group.complete_name = group.name
class AccountAnalyticAccount(models.Model):
_name = 'account.analytic.account'
@@ -20,22 +61,31 @@ class AccountAnalyticAccount(models.Model):
@api.multi
def _compute_debit_credit_balance(self):
res_currency_obj = self.env['res.currency']
analytic_line_obj = self.env['account.analytic.line']
domain = [('account_id', 'in', self.mapped('id'))]
if self._context.get('from_date', False):
domain.append(('date', '>=', self._context['from_date']))
if self._context.get('to_date', False):
domain.append(('date', '<=', self._context['to_date']))
if self._context.get('tag_ids'):
tag_domain = expression.OR([[('tag_ids', 'in', [tag])] for tag in self._context['tag_ids']])
domain = expression.AND([domain, tag_domain])
if self._context.get('company_ids'):
domain.append(('company_id', 'in', self._context['company_ids']))
account_amounts = analytic_line_obj.search_read(domain, ['account_id', 'amount'])
user_currency = self.env.user.company_id.currency_id
account_amounts = analytic_line_obj.search_read(domain, ['account_id', 'amount', 'currency_id'])
account_ids = set([line['account_id'][0] for line in account_amounts])
data_debit = {account_id: 0.0 for account_id in account_ids}
data_credit = {account_id: 0.0 for account_id in account_ids}
for account_amount in account_amounts:
if account_amount['amount'] < 0.0:
data_debit[account_amount['account_id'][0]] += account_amount['amount']
currency_id = account_amount['currency_id'][0]
amount = res_currency_obj.browse(currency_id).compute(account_amount['amount'], user_currency)
if amount < 0.0:
data_debit[account_amount['account_id'][0]] += amount
else:
data_credit[account_amount['account_id'][0]] += account_amount['amount']
data_credit[account_amount['account_id'][0]] += amount
for account in self:
account.debit = abs(data_debit.get(account.id, 0.0))
@@ -46,10 +96,11 @@ class AccountAnalyticAccount(models.Model):
code = fields.Char(string='Reference', index=True, track_visibility='onchange')
active = fields.Boolean('Active', help="If the active field is set to False, it will allow you to hide the account without removing it.", default=True)
tag_ids = fields.Many2many('account.analytic.tag', 'account_analytic_account_tag_rel', 'account_id', 'tag_id', string='Tags', copy=True)
group_id = fields.Many2one('account.analytic.group', string='Group')
line_ids = fields.One2many('account.analytic.line', 'account_id', string="Analytic Lines")
company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.user.company_id)
company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id)
# use auto_join to speed up name_search call
partner_id = fields.Many2one('res.partner', string='Customer', auto_join=True, track_visibility='onchange')
@@ -66,9 +117,9 @@ class AccountAnalyticAccount(models.Model):
for analytic in self:
name = analytic.name
if analytic.code:
name = '['+analytic.code+'] '+name
name = '[' + analytic.code + '] ' + name
if analytic.partner_id:
name = name +' - '+analytic.partner_id.commercial_partner_id.name
name = name + ' - ' + analytic.partner_id.commercial_partner_id.name
res.append((analytic.id, name))
return res
@@ -101,8 +152,14 @@ class AccountAnalyticLine(models.Model):
account_id = fields.Many2one('account.analytic.account', 'Analytic Account', required=True, ondelete='restrict', index=True)
partner_id = fields.Many2one('res.partner', string='Partner')
user_id = fields.Many2one('res.users', string='User', default=_default_user)
tag_ids = fields.Many2many('account.analytic.tag', 'account_analytic_line_tag_rel', 'line_id', 'tag_id', string='Tags', copy=True)
company_id = fields.Many2one('res.company', string='Company', required=True, readonly=True, default=lambda self: self.env.user.company_id)
currency_id = fields.Many2one(related="company_id.currency_id", string="Currency", readonly=True, store=True)
group_id = fields.Many2one('account.analytic.group', related='account_id.group_id', store=True, readonly=True)
company_id = fields.Many2one(related='account_id.company_id', string='Company', store=True, readonly=True)
currency_id = fields.Many2one(related="company_id.currency_id", string="Currency", readonly=True)
@api.multi
@api.constrains('company_id', 'account_id')
def _check_company_id(self):
for line in self:
if line.account_id.company_id and line.company_id.id != line.account_id.company_id.id:
raise ValidationError(_('The selected account belongs to another company that the one you\'re trying to create an analytic item for'))
@@ -15,6 +15,20 @@
<field eval="True" name="global"/>
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
</record>
<record id="analytic_group_comp_rule" model="ir.rule">
<field name="name">Analytic line multi company rule</field>
<field name="model_id" ref="model_account_analytic_group"/>
<field eval="True" name="global"/>
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
</record>
<record id="analytic_tag_comp_rule" model="ir.rule">
<field name="name">Analytic line multi company rule</field>
<field name="model_id" ref="model_account_analytic_tag"/>
<field eval="True" name="global"/>
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
</record>
</data>
<data noupdate="0">
@@ -23,5 +37,10 @@
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<record id="group_analytic_tags" model="res.groups">
<field name="name">Analytic Accounting Tags</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
</data>
</odoo>
@@ -2,3 +2,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_analytic_account,access_account_analytic_account,model_account_analytic_account,group_analytic_accounting,1,1,1,1
access_account_analytic_line,access_account_analytic_line,model_account_analytic_line,group_analytic_accounting,1,1,1,1
access_account_analytic_tag,access_account_analytic_tag,model_account_analytic_tag,group_analytic_accounting,1,1,1,1
access_account_analytic_group,access_account_analytic_group,model_account_analytic_group,group_analytic_accounting,1,1,1,1
access_account_analytic_distribution,access_account_analytic_distribution,model_account_analytic_distribution,group_analytic_accounting,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_analytic_account access_account_analytic_account model_account_analytic_account group_analytic_accounting 1 1 1 1
3 access_account_analytic_line access_account_analytic_line model_account_analytic_line group_analytic_accounting 1 1 1 1
4 access_account_analytic_tag access_account_analytic_tag model_account_analytic_tag group_analytic_accounting 1 1 1 1
5 access_account_analytic_group access_account_analytic_group model_account_analytic_group group_analytic_accounting 1 1 1 1
6 access_account_analytic_distribution access_account_analytic_distribution model_account_analytic_distribution group_analytic_accounting 1 1 1 1
@@ -4,7 +4,7 @@
<field name="name">account.analytic.tag.tree</field>
<field name="model">account.analytic.tag</field>
<field name="arch" type="xml">
<tree string="Analytic Accounts Tags">
<tree string="Analytic Tags">
<field name="name"/>
</tree>
</field>
@@ -14,7 +14,7 @@
<field name="name">account.analytic.tag.form</field>
<field name="model">account.analytic.tag</field>
<field name="arch" type="xml">
<form string="Analytic Accounts Tags">
<form string="Analytic Tags">
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" type="object" name="toggle_active" icon="fa-archive">
@@ -23,14 +23,23 @@
</div>
<group>
<field name="name"/>
<field name="active_analytic_distribution" groups="analytic.group_analytic_accounting"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
<field name="analytic_distribution_ids" nolabel="1" widget="one2many_list"
attrs="{'invisible': [('active_analytic_distribution', '=', False)]}" groups="analytic.group_analytic_accounting">
<tree string="Analytic Distribution" editable="bottom">
<field name="account_id"/>
<field name="percentage"/>
</tree>
</field>
</sheet>
</form>
</field>
</record>
<record id="account_analytic_tag_action" model="ir.actions.act_window">
<field name="name">Analytic Accounts Tags</field>
<field name="name">Analytic Tags</field>
<field name="res_model">account.analytic.tag</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
@@ -41,6 +50,51 @@
</field>
</record>
<record id="account_analytic_group_form_view" model="ir.ui.view">
<field name="name">account.analytic.group.form</field>
<field name="model">account.analytic.group</field>
<field name="arch" type="xml">
<form string="Analytic Account Groups">
<sheet>
<group>
<field name="name"/>
<field name="parent_id"/>
</group>
<group>
<field name="description"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="account_analytic_group_tree_view" model="ir.ui.view">
<field name="name">account.analytic.group.tree</field>
<field name="model">account.analytic.group</field>
<field name="arch" type="xml">
<tree string="Analytic Account Groups">
<field name="name"/>
</tree>
</field>
</record>
<record id="account_analytic_group_action" model="ir.actions.act_window">
<field name="name">Analytic Account Groups</field>
<field name="res_model">account.analytic.group</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Click to add a new analytic account group.
</p>
<p>
This allows to classify your analytic accounts.
</p>
</field>
</record>
<record id="view_account_analytic_line_tree" model="ir.ui.view">
<field name="name">account.analytic.line.tree</field>
<field name="model">account.analytic.line</field>
@@ -56,7 +110,7 @@
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="account_analytic_line_action">
<field name="context">{'search_default_group_date': 1, 'default_account_id': active_id}</field>
<field name="domain">[('account_id','=', active_id)]</field>
@@ -111,7 +165,7 @@
<field name="partner_id"/>
</group>
<group>
<field name="tag_ids" widget="many2many_tags"/>
<field name="group_id"/>
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
<field name="currency_id" options="{'no_create': True}" groups="base.group_multi_currency"/>
</group>
@@ -131,7 +185,7 @@
<field eval="8" name="priority"/>
<field name="arch" type="xml">
<tree string="Analytic Accounts">
<field name="display_name"/>
<field name="name" string="Name"/>
<field name="code"/>
<field name="partner_id"/>
<field name="active" invisible="1"/>
@@ -149,7 +203,6 @@
<field name="arch" type="xml">
<kanban class="o_kanban_mobile">
<field name="display_name"/>
<field name="tag_ids"/>
<field name="balance"/>
<field name="currency_id"/>
<templates>
@@ -179,7 +232,6 @@
<field name="arch" type="xml">
<search string="Analytic Account">
<field name="name" filter_domain="['|', ('name','ilike',self), ('code','ilike',self)]" string="Analytic Account"/>
<field name="tag_ids"/>
<field name="partner_id"/>
<filter string="Archived" domain="[('active', '=', False)]" name="inactive"/>
<group expand="0" string="Group By...">
@@ -226,22 +278,20 @@
<field name="priority">1</field>
<field name="arch" type="xml">
<form string="Analytic Entry">
<sheet string="Analytic Entry">
<group>
<group>
<field name="name"/>
<field name="account_id"/>
<field name="tag_ids" widget="many2many_tags"/>
<field name="date"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
<group string="Amount">
<field name="amount"/>
<field name="unit_amount"/>
<field name="currency_id" invisible="1"/>
</group>
<group>
<group name="analytic_entry" string="Analytic Entry">
<field name="name"/>
<field name="account_id"/>
<field name="tag_ids" widget="many2many_tags"/>
<field name="date"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
</sheet>
<group name="amount" string="Amount">
<field name="amount"/>
<field name="unit_amount"/>
<field name="currency_id" invisible="1"/>
</group>
</group>
</form>
</field>
</record>
+2
View File
@@ -44,6 +44,7 @@ class HrExpense(models.Model):
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, default=lambda self: self.env.user.company_id.currency_id)
company_currency_id = fields.Many2one('res.currency', string="Report Company Currency", related='sheet_id.currency_id', store=True)
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, oldname='analytic_account')
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags', states={'post': [('readonly', True)], 'done': [('readonly', True)]})
account_id = fields.Many2one('account.account', string='Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, default=_default_account_id, help="An expense account is expected")
description = fields.Text('Notes...', readonly=True, states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]})
payment_mode = fields.Selection([
@@ -273,6 +274,7 @@ class HrExpense(models.Model):
'product_id': expense.product_id.id,
'product_uom_id': expense.product_uom_id.id,
'analytic_account_id': expense.analytic_account_id.id,
'analytic_tag_ids': [(6, 0, expense.analytic_tag_ids.ids)],
'expense_id': expense.id,
'partner_id': partner_id,
'tax_ids': [(6, 0, expense.tax_ids.ids)],
@@ -26,6 +26,7 @@
<field name="employee_id"/>
<field name="sheet_id" invisible="not context.get('show_report', False)"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="total_amount" sum="Total Amount" widget="monetary" options="{'currency_field': 'currency_id'}"/>
<field name="state"/>
<field name="message_unread" invisible="1"/>
@@ -65,6 +66,7 @@
<field name="sheet_id" invisible="1"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="company_id" groups="base.group_multi_company"/>
</group><group>
<field name="total_amount" widget='monetary' options="{'currency_field': 'currency_id'}"/>
@@ -443,6 +445,7 @@
<field name="name"/>
<field name="state" invisible="1"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="message_unread" invisible="1"/>
<field name="attachment_number" string=" "/>
<button name="action_get_attachment_view" string="View Attachments" type="object" icon="fa-paperclip"/>
@@ -27,8 +27,6 @@ class ResConfigSettings(models.TransientModel):
implied_group="purchase.group_manage_vendor_price")
module_account_3way_match = fields.Boolean("3-way matching: purchases, receptions and bills")
is_installed_sale = fields.Boolean(string="Is the Sale Module Installed")
group_analytic_account_for_purchases = fields.Boolean('Analytic accounting for purchases',
implied_group='purchase.group_analytic_accounting')
po_lead = fields.Float(related='company_id.po_lead')
use_po_lead = fields.Boolean(
string="Security Lead Time for Purchase",
@@ -15,12 +15,6 @@
<field name="users" eval="[(4, ref('base.user_root'))]"/>
</record>
<record id="group_analytic_accounting" model="res.groups">
<field name="name">Analytic Accounting for Purchases</field>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="implied_ids" eval="[(4, ref('analytic.group_analytic_accounting'))]"/>
</record>
<record id="group_manage_vendor_price" model="res.groups">
<field name="name">Manage Vendor Price</field>
<field name="category_id" ref="base.module_category_hidden"/>
+4 -4
View File
@@ -206,8 +206,8 @@
<field name="move_dest_ids" invisible="1"/>
<field name="date_planned"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
<field name="account_analytic_id" context="{'default_partner_id':parent.partner_id}" groups="purchase.group_analytic_accounting"/>
<field name="analytic_tag_ids" groups="purchase.group_analytic_accounting" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="account_analytic_id" context="{'default_partner_id':parent.partner_id}" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="product_qty"/>
<field name="qty_received" attrs="{'column_invisible': [('parent.state', 'not in', ('purchase', 'done'))], 'readonly': [('product_type', 'in', ('consu', 'product'))]}"/>
<field name="qty_invoiced" attrs="{'column_invisible': [('parent.state', 'not in', ('purchase', 'done'))]}"/>
@@ -233,7 +233,7 @@
<group>
<field name="taxes_id" widget="many2many_tags" domain="[('type_tax_use', '=', 'purchase')]" options="{'no_create': True}"/>
<field name="date_planned" widget="date"/>
<field name="account_analytic_id" colspan="2" groups="purchase.group_analytic_accounting"/>
<field name="account_analytic_id" colspan="2" groups="analytic.group_analytic_accounting"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
</group>
</group>
@@ -487,7 +487,7 @@
domain="[('type_tax_use', '=', 'purchase')]"/>
<field name="date_planned" widget="date" readonly="1"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
<field name="account_analytic_id" colspan="4" groups="purchase.group_analytic_accounting"/>
<field name="account_analytic_id" colspan="4" groups="analytic.group_analytic_accounting"/>
</group>
</group>
<field name="name"/>
@@ -150,27 +150,6 @@
<menuitem id="menu_purchase_general_settings" name="Settings" parent="menu_purchase_config"
sequence="0" action="action_purchase_configuration" groups="base.group_system"/>
<record id="res_config_settings_view_form_account" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.purchase.account</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
<field name="arch" type="xml">
<div id="account_budget" position="before">
<div class="col-xs-12 col-md-6 o_setting_box" title="Allows you to specify an analytic account on purchase order lines." groups="account.group_account_user">
<div class="o_setting_left_pane">
<field name="group_analytic_account_for_purchases" class="oe_inline"/>
</div>
<div class="o_setting_right_pane">
<label string="Purchase Analytics" for="group_analytic_account_for_purchases"/>
<div class="text-muted">
Set analytic accounts in purchase orders
</div>
</div>
</div>
</div>
</field>
</record>
<record id="res_config_settings_view_form_stock" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.purchase.stock</field>
<field name="model">res.config.settings</field>
@@ -152,7 +152,7 @@
<field name="user_id" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}"/>
<field name="type_id" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}" widget="selection"/>
<field name="vendor_id" context="{'search_default_supplier':1, 'default_supplier':1, 'default_customer':0}" domain="[('supplier','=',True)]"/>
<field name="account_analytic_id" colspan="2" groups="purchase.group_analytic_accounting" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="account_analytic_id" colspan="2" groups="analytic.group_analytic_accounting" attrs="{'readonly': [('state','not in',('draft'))]}"/>
</group>
<group>
<field name="date_end" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}"/>
@@ -172,7 +172,7 @@
<field name="qty_ordered"/>
<field name="product_uom_id" groups="product.group_uom"/>
<field name="schedule_date" groups="base.group_no_one"/>
<field name="account_analytic_id" groups="purchase.group_analytic_accounting"/>
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
<field name="price_unit"/>
</tree>
<form string="Products">
@@ -182,7 +182,7 @@
<field name="qty_ordered"/>
<field name="product_uom_id" />
<field name="schedule_date"/>
<field name="account_analytic_id" groups="purchase.group_analytic_accounting"/>
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
</group>
</form>
+3 -3
View File
@@ -279,9 +279,9 @@
<div>
<field name="customer_lead" class="oe_inline"/> days
</div>
<label for="analytic_tag_ids" groups="analytic.group_analytic_accounting"/>
<label for="analytic_tag_ids" groups="analytic.group_analytic_tags"/>
<div>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_accounting" options="{'color_field': 'color'}"/>
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags" options="{'color_field': 'color'}"/>
</div>
</group>
</group>
@@ -319,7 +319,7 @@
attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"
context="{'company_id': parent.company_id}"
groups="product.group_uom" options='{"no_open": True}'/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_accounting" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags"/>
<field name="price_unit"
attrs="{'readonly': [('qty_invoiced', '&gt;', 0)]}"/>
<field name="tax_id" widget="many2many_tags" options="{'no_create': True}" domain="[('type_tax_use','=','sale'),('company_id','=',parent.company_id)]"