diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000000..98eb36db26e
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,40 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported |
+| ------- | ------------------ |
+| 12.0 | :white_check_mark: |
+| 11.0 | :white_check_mark: |
+| 10.0 | :white_check_mark: |
+| <=9.0 | :x: |
+
+## Reporting a Vulnerability
+
+Please share privately the details of your security vulnerability by contacting our Security Team:
+[Contact Info](https://www.odoo.com/security-report)
+
+Make sure to include as much information as possible, with the detailed steps to reproduce the problem,
+the versions that are affected, the expected results and actual results, and any other information that
+might help us react faster and more efficiently.
+
+We tend to prefer _text-based descriptions_ accompanied with a proof-of-concept script/exploit, rather
+than screenshots and videos.
+
+Our [Responsible Disclosure](https://www.odoo.com/security-report) page gives an overview of the
+process, including:
+
+ - Our Incident Response Procedure (what will happen after you report an issue)
+ - Our Rules (what you can and cannot do while researching security issues)
+ - Guidelines with **DO REPORT** and **DO NOT REPORT** issues
+ (what kind of issues will be accepted/rejected)
+
+
+## Important note
+
+We receive a majority of security reports that have little to no impact on the security of Odoo or
+the Odoo Cloud, and we ultimately have to reject them. To avoid a disappointing experience when
+contacting us, please try to put together a proof-of-concept attack and take a critical look at
+what's really at risk.
+If the proposed attack scenario turns out unrealistic, your report will probably be rejected.
+Also be sure to review our list of [non-qualifying issues](https://www.odoo.com/security-report#what).
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index c97b1d4d314..82de31b482c 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -832,7 +832,7 @@ class AccountInvoice(models.Model):
domain = {}
company_id = self.company_id.id
p = self.partner_id if not company_id else self.partner_id.with_context(force_company=company_id)
- type = self.type
+ type = self.type or self.env.context.get('type', 'out_invoice')
if p:
rec_account = p.property_account_receivable_id
pay_account = p.property_account_payable_id
@@ -1848,7 +1848,7 @@ class AccountInvoiceLine(models.Model):
self_lang = self
if part.lang:
self_lang = self.with_context(lang=part.lang)
-
+
product = self_lang.product_id
account = self.get_invoice_line_account(type, product, fpos, company)
if account:
diff --git a/addons/account/models/account_journal_dashboard.py b/addons/account/models/account_journal_dashboard.py
index 97ca3d99717..8a893a05ffb 100644
--- a/addons/account/models/account_journal_dashboard.py
+++ b/addons/account/models/account_journal_dashboard.py
@@ -4,6 +4,7 @@ from datetime import datetime, timedelta
from babel.dates import format_datetime, format_date
from odoo import models, api, _, fields
+from odoo.osv import expression
from odoo.release import version
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF, safe_eval
from odoo.tools.misc import formatLang
@@ -368,10 +369,16 @@ class account_journal(models.Model):
action_name = 'action_view_bank_statement_tree'
elif self.type == 'sale':
action_name = 'action_invoice_tree1'
- self = self.with_context(use_domain=[('journal_id', '=', self.id)])
+ use_domain = expression.AND(
+ [self.env.context.get('use_domain', []), [('journal_id', '=', self.id)]]
+ )
+ self = self.with_context(use_domain=use_domain)
elif self.type == 'purchase':
action_name = 'action_vendor_bill_template'
- self = self.with_context(use_domain=[('journal_id', '=', self.id)])
+ use_domain = expression.AND(
+ [self.env.context.get('use_domain', []), [('journal_id', '=', self.id)]]
+ )
+ self = self.with_context(use_domain=use_domain)
else:
action_name = 'action_move_journal_line'
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index da30e616e85..fc844f15c71 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -936,7 +936,10 @@ class AccountMoveLine(models.Model):
debit_moves = debit_moves.sorted(key=lambda a: (a.date_maturity or a.date, a.currency_id))
credit_moves = credit_moves.sorted(key=lambda a: (a.date_maturity or a.date, a.currency_id))
# Compute on which field reconciliation should be based upon:
- field = self[0].account_id.currency_id and 'amount_residual_currency' or 'amount_residual'
+ if self[0].account_id.currency_id and self[0].account_id.currency_id != self[0].account_id.company_id.currency_id:
+ field = 'amount_residual_currency'
+ else:
+ field = 'amount_residual'
#if all lines share the same currency, use amount_residual_currency to avoid currency rounding error
if self[0].currency_id and all([x.amount_currency and x.currency_id == self[0].currency_id for x in self]):
field = 'amount_residual_currency'
diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py
index 7861644525a..abc17743e61 100644
--- a/addons/account/report/account_invoice_report.py
+++ b/addons/account/report/account_invoice_report.py
@@ -112,13 +112,13 @@ class AccountInvoiceReport(models.Model):
1 AS nbr,
ai.id AS invoice_id, ai.type, ai.state, pt.categ_id, ai.date_due, ai.account_id, ail.account_id AS account_line_id,
ai.partner_bank_id,
- SUM ((invoice_type.sign_qty * ail.quantity) / u.factor * u2.factor) AS product_qty,
+ SUM ((invoice_type.sign_qty * ail.quantity) / COALESCE(u.factor,1) * COALESCE(u2.factor,1)) AS product_qty,
SUM(ail.price_subtotal_signed * invoice_type.sign) AS price_total,
- (ai.amount_total * invoice_type.sign) / (SELECT count(*) FROM account_invoice_line l where invoice_id = ai.id) *
+ ai.amount_total_signed / (SELECT count(*) FROM account_invoice_line l where invoice_id = ai.id) *
count(*) * invoice_type.sign AS amount_total,
SUM(ABS(ail.price_subtotal_signed)) / CASE
- WHEN SUM(ail.quantity / u.factor * u2.factor) <> 0::numeric
- THEN SUM(ail.quantity / u.factor * u2.factor)
+ WHEN SUM(ail.quantity / COALESCE(u.factor,1) * COALESCE(u2.factor,1)) <> 0::numeric
+ THEN SUM(ail.quantity / COALESCE(u.factor,1) * COALESCE(u2.factor,1))
ELSE 1::numeric
END AS price_average,
ai.residual_company_signed / (SELECT count(*) FROM account_invoice_line l where invoice_id = ai.id) *
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_action.js b/addons/account/static/src/js/reconciliation/reconciliation_action.js
index 4690f15d801..b9e40d7f9f7 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_action.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_action.js
@@ -340,9 +340,12 @@ var StatementAction = AbstractAction.extend({
'context': self.model.getContext(),
});
_.each(result.handles, function (handle) {
- self._getWidget(handle).destroy();
- var index = _.findIndex(self.widgets, function (widget) {return widget.handle===handle;});
- self.widgets.splice(index, 1);
+ var widget = self._getWidget(handle);
+ if (widget) {
+ widget.destroy();
+ var index = _.findIndex(self.widgets, function (widget) {return widget.handle===handle;});
+ self.widgets.splice(index, 1);
+ }
});
// Get number of widget and if less than constant and if there are more to laod, load until constant
if (self.widgets.length < self.model.defaultDisplayQty
diff --git a/addons/account/tests/test_account_customer_invoice.py b/addons/account/tests/test_account_customer_invoice.py
index 03ca61aecf5..5bd6e7328d1 100644
--- a/addons/account/tests/test_account_customer_invoice.py
+++ b/addons/account/tests/test_account_customer_invoice.py
@@ -282,30 +282,30 @@ class TestAccountCustomerInvoice(AccountTestUsers):
dashboard_data = journal.get_journal_dashboard_datas()
self.assertEquals(dashboard_data['number_draft'], 2)
- self.assertEquals(dashboard_data['sum_draft'], '$ 68.42')
+ self.assertIn('68.42', dashboard_data['sum_draft'])
self.assertEquals(dashboard_data['number_waiting'], 0)
- self.assertEquals(dashboard_data['sum_waiting'], '$ 0.00')
+ self.assertIn('0.00', dashboard_data['sum_waiting'])
# Check Both
invoice.action_invoice_open()
dashboard_data = journal.get_journal_dashboard_datas()
self.assertEquals(dashboard_data['number_draft'], 1)
- self.assertEquals(dashboard_data['sum_draft'], '$ -13.30')
+ self.assertIn('-13.30', dashboard_data['sum_draft'])
self.assertEquals(dashboard_data['number_waiting'], 1)
- self.assertEquals(dashboard_data['sum_waiting'], '$ 81.72')
+ self.assertIn('81.72', dashboard_data['sum_waiting'])
# Check waiting payment
refund.action_invoice_open()
dashboard_data = journal.get_journal_dashboard_datas()
self.assertEquals(dashboard_data['number_draft'], 0)
- self.assertEquals(dashboard_data['sum_draft'], '$ 0.00')
+ self.assertIn('0.00', dashboard_data['sum_draft'])
self.assertEquals(dashboard_data['number_waiting'], 2)
- self.assertEquals(dashboard_data['sum_waiting'], '$ 68.42')
+ self.assertIn('68.42', dashboard_data['sum_waiting'])
# Check partial
receivable_account = refund.move_id.line_ids.mapped('account_id').filtered(lambda a: a.internal_type == 'receivable')
@@ -329,12 +329,12 @@ class TestAccountCustomerInvoice(AccountTestUsers):
dashboard_data = journal.get_journal_dashboard_datas()
self.assertEquals(dashboard_data['number_draft'], 0)
- self.assertEquals(dashboard_data['sum_draft'], '$ 0.00')
+ self.assertIn('0.00', dashboard_data['sum_draft'])
self.assertEquals(dashboard_data['number_waiting'], 2)
- self.assertEquals(dashboard_data['sum_waiting'], '$ 78.42')
+ self.assertIn('78.42', dashboard_data['sum_waiting'])
with patch('odoo.fields.Date.today', patched_today):
dashboard_data = journal.get_journal_dashboard_datas()
self.assertEquals(dashboard_data['number_late'], 2)
- self.assertEquals(dashboard_data['sum_late'], '$ 78.42')
+ self.assertIn('78.42', dashboard_data['sum_late'])
diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py
index 0fc4c2bc7dd..8ce32823042 100644
--- a/addons/account/tests/test_reconciliation.py
+++ b/addons/account/tests/test_reconciliation.py
@@ -91,6 +91,11 @@ class TestReconciliation(AccountingTestCase):
'code': 'CABA',
'type': 'general',
})
+ self.general_journal = self.env['account.journal'].create({
+ 'name': 'general',
+ 'code': 'GENE',
+ 'type': 'general',
+ })
# Tax Cash Basis
self.tax_cash_basis = self.env['account.tax'].create({
@@ -2034,3 +2039,68 @@ class TestReconciliationExec(TestReconciliation):
pay_receivable_line1 = payment1.move_line_ids.filtered(lambda l: l.account_id == self.account_rcv)
self.assertTrue(pay_receivable_line1.reconciled)
self.assertEqual(pay_receivable_line1.matched_debit_ids, move_caba1.tax_cash_basis_rec_id)
+
+ def test_reconciliation_with_currency(self):
+ #reconciliation on an account having a foreign currency being
+ #the same as the company one
+ account_rcv = self.account_rcv
+ account_rcv.currency_id = self.currency_euro_id
+ aml_obj = self.env['account.move.line'].with_context(
+ check_move_validity=False)
+ general_move1 = self.env['account.move'].create({
+ 'name': 'general1',
+ 'journal_id': self.general_journal.id,
+ })
+ aml_obj.create({
+ 'name': 'debit1',
+ 'account_id': account_rcv.id,
+ 'debit': 11,
+ 'move_id': general_move1.id,
+ })
+ aml_obj.create({
+ 'name': 'credit1',
+ 'account_id': self.account_rsa.id,
+ 'credit': 11,
+ 'move_id': general_move1.id,
+ })
+ general_move1.post()
+ general_move2 = self.env['account.move'].create({
+ 'name': 'general2',
+ 'journal_id': self.general_journal.id,
+ })
+ aml_obj.create({
+ 'name': 'credit2',
+ 'account_id': account_rcv.id,
+ 'credit': 10,
+ 'move_id': general_move2.id,
+ })
+ aml_obj.create({
+ 'name': 'debit2',
+ 'account_id': self.account_rsa.id,
+ 'debit': 10,
+ 'move_id': general_move2.id,
+ })
+ general_move2.post()
+ general_move3 = self.env['account.move'].create({
+ 'name': 'general3',
+ 'journal_id': self.general_journal.id,
+ })
+ aml_obj.create({
+ 'name': 'credit3',
+ 'account_id': account_rcv.id,
+ 'credit': 1,
+ 'move_id': general_move3.id,
+ })
+ aml_obj.create({
+ 'name': 'debit3',
+ 'account_id': self.account_rsa.id,
+ 'debit': 1,
+ 'move_id': general_move3.id,
+ })
+ general_move3.post()
+ to_reconcile = ((general_move1 + general_move2 + general_move3)
+ .mapped('line_ids')
+ .filtered(lambda l: l.account_id.id == account_rcv.id))
+ to_reconcile.reconcile()
+ for aml in to_reconcile:
+ self.assertEqual(aml.amount_residual, 0.0)
diff --git a/addons/account_analytic_default/models/account_analytic_default.py b/addons/account_analytic_default/models/account_analytic_default.py
index 8a19f54c2a1..34052a51560 100644
--- a/addons/account_analytic_default/models/account_analytic_default.py
+++ b/addons/account_analytic_default/models/account_analytic_default.py
@@ -66,8 +66,13 @@ class AccountInvoiceLine(models.Model):
@api.onchange('product_id')
def _onchange_product_id(self):
res = super(AccountInvoiceLine, self)._onchange_product_id()
- rec = self.env['account.analytic.default'].account_get(self.product_id.id, self.invoice_id.commercial_partner_id.id, self.env.uid,
- fields.Date.today(), company_id=self.company_id.id)
+ rec = self.env['account.analytic.default'].account_get(
+ self.product_id.id,
+ self.invoice_id.commercial_partner_id.id,
+ self.invoice_id.user_id.id or 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
@@ -75,8 +80,12 @@ class AccountInvoiceLine(models.Model):
def _set_additional_fields(self, invoice):
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.commercial_partner_id.id, self.env.uid,
- fields.Date.today(), company_id=invoice.company_id.id)
+ self.product_id.id,
+ self.invoice_id.commercial_partner_id.id,
+ self.invoice_id.user_id.id or self.env.uid,
+ fields.Date.today(),
+ company_id=invoice.company_id.id
+ )
if rec:
if self.account_analytic_id:
self.account_analytic_id = rec.analytic_id.id
diff --git a/addons/account_voucher/models/account_voucher.py b/addons/account_voucher/models/account_voucher.py
index d868a1de62f..a3080952fcd 100644
--- a/addons/account_voucher/models/account_voucher.py
+++ b/addons/account_voucher/models/account_voucher.py
@@ -37,7 +37,7 @@ class AccountVoucher(models.Model):
('purchase', 'Purchase')
], string='Type', readonly=True, states={'draft': [('readonly', False)]}, oldname="type")
name = fields.Char('Payment Memo',
- readonly=True, states={'draft': [('readonly', False)]}, default='')
+ readonly=True, states={'draft': [('readonly', False)]}, default='',copy=False)
date = fields.Date("Bill Date", readonly=True,
index=True, states={'draft': [('readonly', False)]},
copy=False, default=fields.Date.context_today)
diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py
index 7f257e3d19f..412787c7878 100644
--- a/addons/calendar/models/calendar.py
+++ b/addons/calendar/models/calendar.py
@@ -323,6 +323,12 @@ class AlarmManager(models.AbstractModel):
'rrule': rule
}
+ # determine accessible events
+ events = self.env['calendar.event'].browse(result)
+ result = {
+ key: result[key]
+ for key in set(events._filter_access_rules('read').ids)
+ }
return result
def do_check_alarm_for_one_date(self, one_date, event, event_maxdelta, in_the_next_X_seconds, alarm_type, after=False, missing=False):
diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py
index dec8489a526..55b487d6340 100644
--- a/addons/crm/models/crm_lead.py
+++ b/addons/crm/models/crm_lead.py
@@ -312,9 +312,10 @@ class Lead(models.Model):
if vals.get('user_id') and 'date_open' not in vals:
vals['date_open'] = fields.Datetime.now()
- if context.get('default_partner_id') and not vals.get('email_from'):
- partner = self.env['res.partner'].browse(context['default_partner_id'])
- vals['email_from'] = partner.email
+ partner_id = vals.get('partner_id') or context.get('default_partner_id')
+ onchange_values = self._onchange_partner_id_values(partner_id)
+ onchange_values.update(vals) # we don't want to overwrite any existing key
+ vals = onchange_values
# context: no_log, because subtype already handle this
return super(Lead, self.with_context(context, mail_create_nolog=True)).create(vals)
@@ -324,7 +325,8 @@ class Lead(models.Model):
# stage change: update date_last_stage_update
if 'stage_id' in vals:
vals['date_last_stage_update'] = fields.Datetime.now()
- if vals.get('user_id') and 'date_open' not in vals:
+ # Only write the 'date_open' if no salesperson was assigned.
+ if vals.get('user_id') and 'date_open' not in vals and not self.mapped('user_id'):
vals['date_open'] = fields.Datetime.now()
# stage change with new stage: update probability and date_closed
if vals.get('stage_id') and 'probability' not in vals:
diff --git a/addons/event_sale/models/sale_order.py b/addons/event_sale/models/sale_order.py
index 52017208adc..7ca5360166c 100644
--- a/addons/event_sale/models/sale_order.py
+++ b/addons/event_sale/models/sale_order.py
@@ -81,7 +81,7 @@ class SaleOrderLine(models.Model):
@api.onchange('event_ticket_id')
def _onchange_event_ticket_id(self):
company = self.event_id.company_id or self.env.user.company_id
- currency = company.currency_id
+ currency = company.currency_id or self.env.user.company_id.currency_id
self.price_unit = currency._convert(
self.event_ticket_id.price, self.order_id.currency_id, self.order_id.company_id, self.order_id.date_order or fields.Date.today())
diff --git a/addons/hr_contract/models/hr_contract.py b/addons/hr_contract/models/hr_contract.py
index eef536458ad..453ec9ff076 100644
--- a/addons/hr_contract/models/hr_contract.py
+++ b/addons/hr_contract/models/hr_contract.py
@@ -51,7 +51,7 @@ class ContractType(models.Model):
_description = 'Contract Type'
_order = 'sequence, id'
- name = fields.Char(string='Contract Type', required=True)
+ name = fields.Char(string='Contract Type', required=True, translate=True)
sequence = fields.Integer(help="Gives the sequence when displaying a list of Contract.", default=10)
diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py
index 13a35b10f5a..d3ecb721635 100644
--- a/addons/hr_expense/models/hr_expense.py
+++ b/addons/hr_expense/models/hr_expense.py
@@ -151,14 +151,14 @@ class HrExpense(models.Model):
@api.model
def get_empty_list_help(self, help_message):
- if help_message and "oe_view_nocontent_smiling_face" not in help_message:
+ if help_message and "o_view_nocontent_smiling_face" not in help_message:
use_mailgateway = self.env['ir.config_parameter'].sudo().get_param('hr_expense.use_mailgateway')
alias_record = use_mailgateway and self.env.ref('hr_expense.mail_alias_expense') or False
if alias_record and alias_record.alias_domain and alias_record.alias_name:
link = "%(email)s " % {
'email': '%s@%s' % (alias_record.alias_name, alias_record.alias_domain)
}
- return '
%s
%s
' % (
+ return '%s
%s
' % (
_('Add a new expense,'),
_('or send receipts by email to %s.') % (link),)
return super(HrExpense, self).get_empty_list_help(help_message)
@@ -211,6 +211,25 @@ class HrExpense(models.Model):
# Business
# ----------------------------------------
+ @api.multi
+ def _prepare_move_values(self):
+ """
+ This function prepares move values related to an expense
+ """
+ self.ensure_one()
+ journal = self.sheet_id.bank_journal_id if self.payment_mode == 'company_account' else self.sheet_id.journal_id
+ account_date = self.sheet_id.accounting_date or self.date
+ move_values = {
+ 'journal_id': journal.id,
+ 'company_id': self.env.user.company_id.id,
+ 'date': account_date,
+ 'ref': self.sheet_id.name,
+ # force the name to the default value, to avoid an eventual 'default_name' in the context
+ # to set it to '' which cause no number to be given to the account.move when posted.
+ 'name': '/',
+ }
+ return move_values
+
@api.multi
def _get_account_move_by_sheet(self):
""" Return a mapping between the expense sheet of current expense and its account move
@@ -219,18 +238,8 @@ class HrExpense(models.Model):
move_grouped_by_sheet = {}
for expense in self:
# create the move that will contain the accounting entries
- account_date = expense.sheet_id.accounting_date or expense.date
if expense.sheet_id.id not in move_grouped_by_sheet:
- journal = expense.sheet_id.bank_journal_id if expense.payment_mode == 'company_account' else expense.sheet_id.journal_id
- move = self.env['account.move'].create({
- 'journal_id': journal.id,
- 'company_id': self.env.user.company_id.id,
- 'date': account_date,
- 'ref': expense.sheet_id.name,
- # force the name to the default value, to avoid an eventual 'default_name' in the context
- # to set it to '' which cause no number to be given to the account.move when posted.
- 'name': '/',
- })
+ move = self.env['account.move'].create(expense._prepare_move_values())
move_grouped_by_sheet[expense.sheet_id.id] = move
else:
move = move_grouped_by_sheet[expense.sheet_id.id]
diff --git a/addons/hr_holidays/models/hr.py b/addons/hr_holidays/models/hr.py
index 6a0dae5d5a2..2a8a5506217 100644
--- a/addons/hr_holidays/models/hr.py
+++ b/addons/hr_holidays/models/hr.py
@@ -206,8 +206,8 @@ class Employee(models.Model):
hr_vals['manager_id'] = values['parent_id']
if values.get('department_id') is not None:
hr_vals['department_id'] = values['department_id']
- holidays = self.env['hr.leave'].search(['|',('state', 'in', ['draft', 'confirm']),('date_from', '>', today_date), ('employee_id', 'in', self.ids)])
+ holidays = self.env['hr.leave'].sudo().search(['|',('state', 'in', ['draft', 'confirm']),('date_from', '>', today_date), ('employee_id', 'in', self.ids)])
holidays.write(hr_vals)
- allocations = self.env['hr.leave.allocation'].search([('state', 'in', ['draft', 'confirm']), ('employee_id', 'in', self.ids)])
+ allocations = self.env['hr.leave.allocation'].sudo().search([('state', 'in', ['draft', 'confirm']), ('employee_id', 'in', self.ids)])
allocations.write(hr_vals)
return res
diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py
index 73205a5a6a3..d727b98de7d 100644
--- a/addons/hr_holidays/models/hr_leave.py
+++ b/addons/hr_holidays/models/hr_leave.py
@@ -359,11 +359,17 @@ class HolidaysRequest(models.Model):
self.mode_company_id = False
self.department_id = False
+ @api.multi
+ def _sync_employee_details(self):
+ for holiday in self:
+ holiday.manager_id = holiday.employee_id.parent_id.id
+ if holiday.employee_id:
+ holiday.department_id = holiday.employee_id.department_id
+
@api.onchange('employee_id')
def _onchange_employee_id(self):
- self.manager_id = self.employee_id.parent_id.id
- if self.employee_id:
- self.department_id = self.employee_id.department_id
+ self._sync_employee_details()
+ self.holiday_status_id = False
@api.onchange('date_from', 'date_to', 'employee_id')
def _onchange_leave_dates(self):
@@ -427,7 +433,7 @@ class HolidaysRequest(models.Model):
def _check_date(self):
for holiday in self:
domain = [
- ('date_from', '<=', holiday.date_to),
+ ('date_from', '<', holiday.date_to),
('date_to', '>', holiday.date_from),
('employee_id', '=', holiday.employee_id.id),
('id', '!=', holiday.id),
@@ -535,7 +541,7 @@ class HolidaysRequest(models.Model):
if holiday.validation_type == 'hr':
holiday.message_subscribe(partner_ids=(holiday.employee_id.parent_id.user_id.partner_id | holiday.employee_id.leave_manager_id.partner_id).ids)
if employee_id:
- holiday._onchange_employee_id()
+ holiday._sync_employee_details()
if 'number_of_days' not in values and ('date_from' in values or 'date_to' in values):
holiday._onchange_leave_dates()
if leave_type.validation_type == 'no_validation':
@@ -574,7 +580,7 @@ class HolidaysRequest(models.Model):
for holiday in self:
if employee_id:
holiday.add_follower(employee_id)
- holiday._onchange_employee_id()
+ self._sync_employee_details()
if 'number_of_days' not in values and ('date_from' in values or 'date_to' in values):
holiday._onchange_leave_dates()
return result
diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py
index adaa50d7e61..ce96a2fac9b 100644
--- a/addons/hr_holidays/models/hr_leave_allocation.py
+++ b/addons/hr_holidays/models/hr_leave_allocation.py
@@ -207,7 +207,10 @@ class HolidaysAllocation(models.Model):
@api.depends('number_of_days', 'employee_id')
def _compute_number_of_hours_display(self):
for allocation in self:
- allocation.number_of_hours_display = allocation.number_of_days * (allocation.employee_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY)
+ if allocation.parent_id and allocation.parent_id.type_request_unit == "hour":
+ allocation.number_of_hours_display = allocation.number_of_days * HOURS_PER_DAY
+ else:
+ allocation.number_of_hours_display = allocation.number_of_days * (allocation.employee_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY)
@api.multi
@api.depends('number_of_hours_display', 'number_of_days_display')
diff --git a/addons/hr_holidays/tests/test_leave_requests.py b/addons/hr_holidays/tests/test_leave_requests.py
index 9f32d96e6c6..f15f9cfb9e5 100644
--- a/addons/hr_holidays/tests/test_leave_requests.py
+++ b/addons/hr_holidays/tests/test_leave_requests.py
@@ -158,9 +158,9 @@ class TestLeaveRequests(TestHrHolidaysBase):
self.employee_hrmanager.write({'department_id': self.hr_dept.id})
self.assertFalse(self.env['hr.leave'].search([('employee_id', 'in', self.hr_dept.member_ids.ids)]))
leave_form = Form(self.env['hr.leave'].sudo(self.user_hrmanager))
- leave_form.holiday_status_id = self.holidays_type_1
leave_form.holiday_type = 'department'
leave_form.department_id = self.hr_dept
+ leave_form.holiday_status_id = self.holidays_type_1
leave = leave_form.save()
leave.action_approve()
member_ids = self.hr_dept.member_ids.ids
diff --git a/addons/hw_scale/controllers/main.py b/addons/hw_scale/controllers/main.py
index bb98a8521d0..1ebb6e39af2 100644
--- a/addons/hw_scale/controllers/main.py
+++ b/addons/hw_scale/controllers/main.py
@@ -8,7 +8,7 @@ import time
from collections import namedtuple
from os import listdir
-from threading import Thread, Lock
+from threading import Event, Lock, Thread
from odoo import http
@@ -134,6 +134,7 @@ class Scale(Thread):
self.path_to_scale = ''
self.protocol = None
self.disabled = False
+ self.weight_request_event = Event()
def lockedstart(self):
with self.lock:
@@ -261,8 +262,8 @@ class Scale(Thread):
return None
def get_weight(self):
- self.repeats = 5
self.disabled = False
+ self.weight_request_event.set()
self.lockedstart()
return self.weight
@@ -361,11 +362,13 @@ class Scale(Thread):
self.device = self.get_device()
if not self.device:
# retry later to support "plug and play"
- time.sleep(10)
+ self.weight_request_event.clear()
+ self.weight_request_event.wait(10)
else:
self.disabled = self.protocol.disable
else:
- time.sleep(10)
+ self.weight_request_event.clear()
+ self.weight_request_event.wait(10)
scale_thread = None
diff --git a/addons/iap/models/iap.py b/addons/iap/models/iap.py
index af375d5050b..263bf86196a 100644
--- a/addons/iap/models/iap.py
+++ b/addons/iap/models/iap.py
@@ -162,7 +162,7 @@ class IapAccount(models.Model):
@api.model
def get(self, service_name, force_create=True):
- account = self.search([('service_name', '=', service_name), ('company_id', 'in', [self.env.user.company_id.id, False])])
+ account = self.search([('service_name', '=', service_name), ('company_id', 'in', [self.env.user.company_id.id, False])], limit=1, order='company_id desc, id desc')
if not account and force_create:
account = self.create({'service_name': service_name})
# Since the account did not exist yet, we will encounter a NoCreditError,
diff --git a/addons/iap/security/ir.model.access.csv b/addons/iap/security/ir.model.access.csv
index 7e423f3c57a..80fd4e2212f 100644
--- a/addons/iap/security/ir.model.access.csv
+++ b/addons/iap/security/ir.model.access.csv
@@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_client_iap_account_manager,iap.account.manager,model_iap_account,base.group_system,1,1,1,0
+access_client_iap_account_manager,iap.account.manager,model_iap_account,base.group_system,1,1,1,1
access_client_iap_account_user,iap.account.user,model_iap_account,base.group_user,1,0,1,0
diff --git a/addons/l10n_ae/__manifest__.py b/addons/l10n_ae/__manifest__.py
index d9542262df7..bc5da58dfe3 100644
--- a/addons/l10n_ae/__manifest__.py
+++ b/addons/l10n_ae/__manifest__.py
@@ -14,11 +14,14 @@ United Arab Emirates accounting chart and localization.
""",
'depends': ['base', 'account'],
'data': [
+ 'data/l10n_ae_data.xml',
'data/account_data.xml',
'data/l10n_ae_chart_data.xml',
'data/account.account.template.csv',
'data/l10n_ae_chart_post_data.xml',
'data/account_tax_template_data.xml',
+ 'data/fiscal_templates_data.xml',
'data/account_chart_template_data.xml',
+ 'views/report_invoice_templates.xml',
],
}
diff --git a/addons/l10n_ae/data/account_data.xml b/addons/l10n_ae/data/account_data.xml
index 48fcd567909..df9792faae5 100644
--- a/addons/l10n_ae/data/account_data.xml
+++ b/addons/l10n_ae/data/account_data.xml
@@ -1,9 +1,15 @@
-
+
-
- Tax 15%
+
+ Tax 5%
+
+
+ Tax 0%
+
+
+ Tax Exempted
diff --git a/addons/l10n_ae/data/account_tax_template_data.xml b/addons/l10n_ae/data/account_tax_template_data.xml
index eb60fb97ba8..b7299572170 100644
--- a/addons/l10n_ae/data/account_tax_template_data.xml
+++ b/addons/l10n_ae/data/account_tax_template_data.xml
@@ -1,21 +1,339 @@
-
-
- Tax 10%
- 10
+
+ Output VAT 5% (Dubai)
+ taxes
+
+
+ Output VAT 5% (Abu Dhabi)
+ taxes
+
+
+ Output VAT 5% (Sharjah)
+ taxes
+
+
+ Output VAT 5% (Ajman)
+ taxes
+
+
+ Output VAT 5% (Umm Al Quwain)
+ taxes
+
+
+ Output VAT 5% (Ras Al-Khaima)
+ taxes
+
+
+ Output VAT 5% (Fujairah)
+ taxes
+
+
+ Input VAT 5%
+ taxes
+
+
+ Exempted Output VAT
+ taxes
+
+
+ Exempted Input VAT
+ taxes
+
+
+ Output VAT 0%
+ taxes
+
+
+ Input VAT 0%
+ taxes
+
+
+ Import VAT 5%
+ taxes
+
+
+ Export VAT 0%
+ taxes
+
+
+ Reverse Charge TVA -5%(sales)
+ taxes
+
+
+ Reverse Charge TVA -5%(purchase)
+ taxes
+
+
+ Tourist Refund scheme 5%
+ taxes
+
+
+
+
+ TVA 5% (Dubai)
sale
+ 5
+ percent
+ TVA 5%
-
-
-
+
+
- Purchase Tax 10%
- 10
+
+
+ TVA 5% (Abu Dhabi)
+ sale
+ 5
+ percent
+ TVA 5%
+
+
+
+
+
+
+
+ TVA 5% (Sharjah)
+ sale
+ 5
+ percent
+ TVA 5%
+
+
+
+
+
+
+
+ TVA 5% (Ajman)
+ sale
+ 5
+ percent
+ TVA 5%
+
+
+
+
+
+
+
+ TVA 5% (Umm Al Quwain)
+ sale
+ 5
+ percent
+ TVA 5%
+
+
+
+
+
+
+
+ TVA 5% (Ras Al-Khaima)
+ sale
+ 5
+ percent
+ TVA 5%
+
+
+
+
+
+
+
+ TVA 5% (Fujairah)
+ sale
+ 5
+ percent
+ TVA 5%
+
+
+
+
+
+
+
+ Exempted Tax
+ sale
+ 0
+ percent
+ Exempted
+
+
+
+
+
+
+
+ TVA 0%
+ sale
+ 0
+ percent
+ TVA 0%
+
+
+
+
+
+
+
+ Export Tax 0%
+ sale
+ 0
+ percent
+ Export Tax
+
+
+
+
+ Reverse Charge TVA -5%(Sale)
+ none
+ -5.0
+ percent
+
+
+
+
+
+
+ Reverse Charge Provision (Dubai)
+ sale
+ 0.0
+ group
+ Supplies subject to reverse charge provisions
+
+
+
+
+ Reverse Charge Provision (Abi Dhabi)
+ sale
+ 0.0
+ group
+ Supplies subject to reverse charge provisions
+
+
+
+
+ Reverse Charge Provision (Sharjah)
+ sale
+ 0.0
+ group
+ Supplies subject to reverse charge provisions
+
+
+
+
+ Reverse Charge Provision (Ajman)
+ sale
+ 0.0
+ group
+ Supplies subject to reverse charge provisions
+
+
+
+
+ Reverse Charge Provision (Umm Al Quwain)
+ sale
+ 0.0
+ group
+ Supplies subject to reverse charge provisions
+
+
+
+
+ Reverse Charge Provision (Ras Al-Khaima)
+ sale
+ 0.0
+ group
+ Supplies subject to reverse charge provisions
+
+
+
+
+ Reverse Charge Provision (Fujairah)
+ sale
+ 0.0
+ group
+ Supplies subject to reverse charge provisions
+
+
+
+
+ Tourist Refund scheme 5%
+ sale
+ 5.0
+ percent
+ Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme
+
+
+
+
+
+
+
+ TVA 5%
purchase
+ 5
+ percent
+ TVA 5%
-
+
+
+
+
+
+ Exempted Tax
+ purchase
+ 0
+ percent
+ Exempted Tax
+
+
+
+
+
+
+
+ TVA 0%
+ purchase
+ 0
+ percent
+ TVA 0%
+
+
+
+
+
+
+
+ Import Tax 5%
+ purchase
+ 5
+ percent
+ Import Tax
+
+
+
+
+ Reverse Charge TVA -5%(Purchase)
+ none
+ -5.0
+ percent
+
+
+
+
+
+
+ Reverse Charge Provision
+ purchase
+ 0.0
+ group
+ Supplies subject to reverse charge provisions
+
+
diff --git a/addons/l10n_ae/data/fiscal_templates_data.xml b/addons/l10n_ae/data/fiscal_templates_data.xml
new file mode 100644
index 00000000000..2579a2ad720
--- /dev/null
+++ b/addons/l10n_ae/data/fiscal_templates_data.xml
@@ -0,0 +1,112 @@
+
+
+
+ Dubai
+
+ 16
+
+
+
+
+
+
+ Abu Dhabi
+
+ 16
+
+
+
+
+
+
+
+
+
+
+
+ Sharjah
+
+ 16
+
+
+
+
+
+
+
+
+
+
+
+ Ajman
+
+ 16
+
+
+
+
+
+
+
+
+
+
+
+ Umm Al Quwain
+
+ 16
+
+
+
+
+
+
+
+
+
+
+
+ Ras Al-Khaima
+
+ 16
+
+
+
+
+
+
+
+
+
+
+
+ Fujairah
+
+ 16
+
+
+
+
+
+
+
+
+
+
+
+ Non-UAE
+
+ 20
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/l10n_ae/data/l10n_ae_chart_data.xml b/addons/l10n_ae/data/l10n_ae_chart_data.xml
index 0c8202a34f2..eb648164386 100644
--- a/addons/l10n_ae/data/l10n_ae_chart_data.xml
+++ b/addons/l10n_ae/data/l10n_ae_chart_data.xml
@@ -1,5 +1,7 @@
+
+
U.A.E Chart of Accounts - Standard
6
@@ -7,6 +9,6 @@
105
100
-
+
diff --git a/addons/l10n_ae/data/l10n_ae_data.xml b/addons/l10n_ae/data/l10n_ae_data.xml
new file mode 100644
index 00000000000..94692698ecd
--- /dev/null
+++ b/addons/l10n_ae/data/l10n_ae_data.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ TRN
+
+
+ AED
+
+
+ GCC VAT implementing States
+
+
+
diff --git a/addons/l10n_ae/views/report_invoice_templates.xml b/addons/l10n_ae/views/report_invoice_templates.xml
new file mode 100644
index 00000000000..681fe038284
--- /dev/null
+++ b/addons/l10n_ae/views/report_invoice_templates.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+ Supply between United Arad Emirates and
+
+
+
+ Tax
+
+
+
+
+
+
+
diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py
index 0a6459a0953..fd4eb978262 100644
--- a/addons/l10n_fr_fec/wizard/account_fr_fec.py
+++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py
@@ -305,7 +305,7 @@ class AccountFrFec(models.TransientModel):
END
AS PieceRef,
TO_CHAR(am.date, 'YYYYMMDD') AS PieceDate,
- CASE WHEN aml.name IS NULL THEN '/' ELSE replace(replace(aml.name, '|', '/'), '\t', '') END AS EcritureLib,
+ CASE WHEN aml.name IS NULL OR aml.name = '' THEN '/' ELSE replace(replace(aml.name, '|', '/'), '\t', '') END AS EcritureLib,
replace(CASE WHEN aml.debit = 0 THEN '0,00' ELSE to_char(aml.debit, '000000000000000D99') END, '.', ',') AS Debit,
replace(CASE WHEN aml.credit = 0 THEN '0,00' ELSE to_char(aml.credit, '000000000000000D99') END, '.', ',') AS Credit,
CASE WHEN rec.name IS NULL THEN '' ELSE rec.name END AS EcritureLet,
@@ -388,7 +388,7 @@ class AccountFrFec(models.TransientModel):
rows_length = len(rows)
for i, row in enumerate(rows):
if not i == rows_length - 1:
- row.append(lineterminator)
+ row[-1] += lineterminator
writer.writerow(row)
fecvalue = fecfile.getvalue()
diff --git a/addons/l10n_mn/__init__.py b/addons/l10n_mn/__init__.py
new file mode 100644
index 00000000000..67dee8c60db
--- /dev/null
+++ b/addons/l10n_mn/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
diff --git a/addons/l10n_mn/__manifest__.py b/addons/l10n_mn/__manifest__.py
new file mode 100644
index 00000000000..a99552f8ee3
--- /dev/null
+++ b/addons/l10n_mn/__manifest__.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+{
+ "name" : "Mongolia - Accounting",
+ "version" : "1.0",
+ 'category': 'Localization',
+ "description": """
+This is the module to manage the accounting chart for Mongolia.
+===============================================================
+
+ * the Mongolia Official Chart of Accounts,
+ * the Tax Code Chart for Mongolia
+ * the main taxes used in Mongolia
+
+Financial requirement contributor: Baskhuu Lodoikhuu. BumanIT LLC
+""",
+ "depends": ['account'],
+ 'data': [
+ 'data/account_chart_template_data.xml',
+ 'data/account.account.tag.csv',
+ 'data/account.account.template.csv',
+ 'data/account.tax.group.csv',
+ 'data/account.tax.template.csv',
+ 'data/account_fiscal_position_template_data.xml',
+ 'data/account.chart.template.csv',
+ 'data/account_chart_template_configuration_data.xml',
+ ],
+}
diff --git a/addons/l10n_mn/data/account.account.tag.csv b/addons/l10n_mn/data/account.account.tag.csv
new file mode 100644
index 00000000000..b8667ffc12d
--- /dev/null
+++ b/addons/l10n_mn/data/account.account.tag.csv
@@ -0,0 +1,94 @@
+id,name,applicability
+account.account_tag_operating,CF: Үндсэн: Бусад,accounts
+account.account_tag_financing,CF: Хөрөнгө оруулалт: Бусад Урт хугацаат хөрөнгө,accounts
+account.account_tag_investing,CF: Санхүү: Ногдол ашиг,accounts
+account_tag_operating_product,CF: Үндсэн: Бараа үйлчилгээ,accounts
+account_tag_operating_loyalty,CF: Үндсэн: Эрхийн шимтгэл хураамж,accounts
+account_tag_operating_insurance,CF: Үндсэн: Нийгмийн Даатгал,accounts
+account_tag_operating_insurance2,CF: Үндсэн: Бусад Даатгал,accounts
+account_tag_operating_taxrefund,CF: Үндсэн: Татвар,accounts
+account_tag_operating_funding,CF: Үндсэн: Татаас санхүүжилт,accounts
+account_tag_operating_employee,CF: Үндсэн: Ажилчид,accounts
+account_tag_operating_cost,CF: Үндсэн: Ашиглалтын зардал,accounts
+account_tag_operating_transport,CF: Үндсэн: Түлш тээвэр сэлбэг,accounts
+account_tag_operating_interest,CF: Үндсэн: Зээлийн хүү,accounts
+account_tag_investing_fasset,CF: Хөрөнгө оруулалт: Үндсэн хөрөнгө,accounts
+account_tag_investing_iasset,CF: Хөрөнгө оруулалт: Биет бус хөрөнгө,accounts
+account_tag_investing_invest,CF: Хөрөнгө оруулалт: Хөрөнгө оруулалт,accounts
+account_tag_investing_loan,CF: Хөрөнгө оруулалт: Зээл урьдчилгаа,accounts
+account_tag_investing_interest,CF: Хөрөнгө оруулалт: Зээлийн хүү,accounts
+account_tag_investing_dividends,CF: Хөрөнгө оруулалт: Ногдол ашиг,accounts
+account_tag_financing_loan,"CF: Санхүү: Зээл, өрийн үнэт цаас",accounts
+account_tag_financing_rental,CF: Санхүү: Санхүүгийн түрээс,accounts
+account_tag_financing_stock,CF: Санхүү: Хувьцаа өмчийн бусад үнэт цаас,accounts
+account_tag_financing_donation,CF: Санхүү: Хандив,accounts
+account_tag_exchange,CF: Валютын ханшийн зөрүү,accounts
+vat_report_tag2,ТТ3а: НӨАТ-аас чөлөөлөгдөх борлуулалтын орлого (2),taxes
+vat_report_tag5,ТТ3a: Дотоодын зах зээлд борлуулсан барааны борлуулалтын орлого (5),taxes
+vat_report_tag6,ТТ3a: Бусад барааны борлуулалтын орлого (6),taxes
+vat_report_tag7,ТТ3a: Эрх борлуулсны орлого (7),taxes
+vat_report_tag8,ТТ3a: Татан буугдах үед үлдсэн барааны орлого (8),taxes
+vat_report_tag9,ТТ3a: Өрийн төлбөрт шилжүүлсэн барааны орлого (9),taxes
+vat_report_tag11,ТТ3a: Наториатын үйлчилгээний орлого (11),taxes
+vat_report_tag12,ТТ3a: Өрийн төлбөрт тооцсон ажил үйлчилгээний орлого (12),taxes
+vat_report_tag13,"ТТ3a: Цахилгаан, дулаан, ус, шуудан, холбооны үйлчилгээ үзүүлсний орлого (13)",taxes
+vat_report_tag14,"ТТ3a: Бараа түрээслүүлэх, эзэмшүүлэх, ашиглуулах үйлчилгээний орлого (14)",taxes
+vat_report_tag15,"ТТ3a: Байр түрээслүүлэх, эзэмшүүлэх, ашиглуулах үйлчилгээний орлого (15)",taxes
+vat_report_tag16,"ТТ3a: Үл хөдлөх, хөдлөх хөрөнгө түрээслүүлэх, эзэмшүүлэх ашиглуулах үйлчилгээий орлого (16)",taxes
+vat_report_tag17,"ТТ3a: Шинэ бүтээл, загвар, зохиогчийн эрх, барааны тэмдэг худалдсаны орлого (17)",taxes
+vat_report_tag18,"ТТ3a: Хонжворт сугалаа, төлбөрт таавар, бооцоот тоглоомын орлого (18)",taxes
+vat_report_tag19,ТТ3a: Зуучлалын үйлчилгээний орлого (19),taxes
+vat_report_tag20,"ТТ3a: Бусдаас авсан хүү, торгууль, алдангийн орлого (20)",taxes
+vat_report_tag21,ТТ3a: Хөрөнгийн үнэлгээний үйлчилгээний орлого (21),taxes
+vat_report_tag22,"ТТ3a: Төсвийн санхүүжилт, татаас, урамшууллын орлого (22)",taxes
+vat_report_tag23,"ТТ3a: Өмгөөлөл, хууль зүйн үйлчилгээний орлого (23)",taxes
+vat_report_tag24,"ТТ3a: Үсчин, гоо сайхан, засвар үйлчилгээ, угаалга, хими цэвэрлэгээний үйлчилгээний орлого (24)",taxes
+vat_report_tag25,ТТ3a: Хуулийн 13-т зааснаас бусад үйлчилгээний орлого (25),taxes
+vat_report_tag28,ТТ3a: Экспортонд гаргасан барааны борлуулалтын орлого (28),taxes
+vat_report_tag29,ТТ3a: Экспортолсон үйлчилгээний борлуулалтын орлого (29),taxes
+vat_report_tag33,"ТТ3а: Худалдан авсан нийт бараа, ажил үйлчилгээ (33)",taxes
+vat_report_tag35,"ТТ3a: НӨАТ-тэй авсан Импортын бараа, ажил үйлчилгээний худалдан авалт (35)",taxes
+vat_report_tag36,"ТТ3a: Дотоодын зах зээлээс НӨАТ-тэй авсан бараа, ажил, үйлчилгээ (36)",taxes
+vat_report_tag37,"ТТ3a: Суутган төлөгчөөр бүртгүүлэх үед НӨАТ-тэй авсан бараа, үйлчилгээ (37)",taxes
+vat_report_tag38,"ТТ3a: Мал аж ахуй, тариалан эрхлэгчээс НӨАТ-тэй авсан бараа, үйлчилгээ (38)",taxes
+vat_report_tag41,"ТТ3а: Суудлын автомашин, түүний эд анги сэлбэгт төлсөн НӨАТ (41)",taxes
+vat_report_tag42,"ТТ3а: Ажлын хэрэгцээнд зориулж авсан бараа, үйлчилгээнд төлсөн НӨАТ (42)",taxes
+vat_report_tag43,"ТТ3а: Үндсэн хөрөнгө бэлтгэхэд зориулж импортоор авсан бараа, үйлчилгээнд төлсөн НӨАТ (43)",taxes
+vat_report_tag44,"ТТ3а: Чөлөөлөгдөх үйлдвэрлэл, үйлчилгээнд зориулж авсан бараа, ажил, үйлчилгээнд төлсөн НӨАТ (44)",taxes
+vat_report_tag45,"ТТ3а: Хайгуулын ажил болон ашиглалтын өмнөх үйл ажиллагаанд зориулж импортоор авсан бараа, ажил, үйлчилгээнд төлсөн НӨАТ (45)",taxes
+vat_report_tag46,"ТТ3а: Импортоор оруулсан бусад хамааралгүй бараа, ажил, үйлчилгээнд төлсөн НӨАТ (46)",taxes
+vat_report_tag48,ТТ3а: Cанхүүгийн түрээсээр дотоодын зах зээлд борлуулсны орлого (48),taxes
+vat_report_tag49,"ТТ3а: Факторинг, форфайтинг хэлцлийн үйлчилгээний орлого (49)",taxes
+vat_report_tag51,ТТ3а: Санхүүгийн түрээсийн зүйлийг худалдан авахад төлсөн түрээсийн төлбөр (51),taxes
+vat_report_tag52,"ТТ3а: Факторинг, форфайтинг хэлцлийг авахад төлсөн төлбөр (52)",taxes
+vat_report_tag58,"ТТ3а: Борлуулалтын орлогын буцаалт, хөнгөлөлт (58)",accounts
+vat_report_tag59,"ТТ3а: Худалдан авалтын буцаалт, хөнгөлөлт (59)",accounts
+tax_report_tag2,ТТ02: Татвараас чөлөөлөгдөх орлого (2),accounts
+tax_report_tag5,ТТ02: Бусад орлогын дүн (5),accounts
+tax_report_tag7,ТТ02: Үндсэн ажил үйлчилгээний орлого (7),accounts
+tax_report_tag8,ТТ02: Туслах ажил үйлчилгээний орлого (8),accounts
+tax_report_tag9,"ТТ02: Хувьцаа, үнэт цаас борлуулсны орлого (9)",accounts
+tax_report_tag10,"ТТ02: Үнэ төлбөргүйгээр бусдаас авсан бараа, үйлчилгээ (10)",accounts
+tax_report_tag11,ТТ02: Биет бус хөрөнгө борлуулсны орлого (11),accounts
+tax_report_tag12,ТТ02: Техникийн зөвлөх үйлчилгээний орлого (12),accounts
+tax_report_tag13,"ТТ02: Гэрээний хүү, торгууль, хохирлын нөхөн төлбөрийн орлого (13)",accounts
+tax_report_tag14,ТТ02: Гадаад валютын ханшийн зөрүүгийн бодит орлого (14),accounts
+tax_report_tag15,ТТ02: Хөдлөх болон үл хөдлөх эд хөрөнгийн түрээсийн орлого (15),accounts
+tax_report_tag16,ТТ02: Хөдлөх эд хөрөнгө борлуулсны орлого (16),accounts
+tax_report_tag17,ТТ02: Албан татвар ногдох бусад орлого (17),accounts
+tax_report_tag22,ТТ02: Татвар ногдох орлогоос хасагдахгүй зардал (22),accounts
+tax_report_tag23,ТТ02: Татвар ногдох орлогыг бууруулах (23),accounts
+tax_report_tag25,ТТ02: Сайн дурын даатгалын хураамжийн хэтрэлт (25),accounts
+tax_report_tag27,ТТ02: Өмнөх жилийн татварын алдагдлаас тайлант хугацаанд шилжүүлсэн дүн (27),accounts
+tax_report_tag30,ТТ02: Татварын хөнгөлөлт (30),accounts
+tax_report_tag32,"ТТ02: Эротик хэвлэл, бичлэг, тоглолт борлуулалтын орлого (32)",accounts
+tax_report_tag34,"ТТ02: Таавар, бооцоот тоглоом, хонжворт сугалааны орлого (34)",accounts
+tax_report_tag35,"ТТ02: Таавар, бооцоот тоглоом, хонжворт сугалааны орлого олоход зарцуулсан зардал (35)",accounts
+tax_report_tag36,ТТ02: Хонжворт олгосон мөнгө болон барааны үнэ (36),accounts
+tax_report_tag39,ТТ02: Хүүгийн орлого (39),accounts
+tax_report_tag41,"ТТ02: Гадаад улсад олсон ногдол ашиг, хүүгийн орлого (41)",accounts
+tax_report_tag43,ТТ02: Гадаад улсад олсон орлого (43),accounts
+tax_report_tag46,ТТ02: Ногдол ашгийн орлого (46),accounts
+tax_report_tag48,ТТ02: Эрхийн шимтгэлийн орлого (48),accounts
+tax_report_tag50,ТТ02: Эрх борлуулсны орлого (50),accounts
+tax_report_tag52,ТТ02: Үл хөдлөх хөрөнгө борлуулсны орлого (52),accounts
diff --git a/addons/l10n_mn/data/account.account.template.csv b/addons/l10n_mn/data/account.account.template.csv
new file mode 100644
index 00000000000..f64558c3e6b
--- /dev/null
+++ b/addons/l10n_mn/data/account.account.template.csv
@@ -0,0 +1,285 @@
+id,code,name,user_type_id/id,tag_ids/id,reconcile,chart_template_id:id
+account_template_1001_0101,10010101,Бэлэн мөнгө,account.data_account_type_liquidity,,0,mn_chart_1
+account_template_1003_0101,10030101,Жижиг мөнгөн сан,account.data_account_type_liquidity,,0,mn_chart_1
+account_template_1101_0101,11010101,Банкинд байгаа мөнгө,account.data_account_type_liquidity,,0,mn_chart_1
+account_template_1103_0101,11030101,Хугацаагүй хадгаламж,account.data_account_type_liquidity,,0,mn_chart_1
+account_template_1104_0101,11040101,Банкны төлбөрийн темринал,account.data_account_type_liquidity,,0,mn_chart_1
+account_template_1109_0101,11090101,Замд яваа мөнгө,account.data_account_type_current_assets,,1,mn_chart_1
+account_template_1110_0101,11100101,Бусад мөнгө түүнтэй адилтгах хөрөнгө,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1201_0101,12010101,Худалдааны авлага ААН,account.data_account_type_receivable,account_tag_operating_product,1,mn_chart_1
+account_template_1201_0201,12010201,Худалдааны авлага Хувь хүн,account.data_account_type_receivable,account_tag_operating_product,1,mn_chart_1
+account_template_1201_0301,12010301,Худалдааны авлага Холбоотой тал,account.data_account_type_receivable,account_tag_operating_product,1,mn_chart_1
+account_template_1202_0101,12020101,Авлагын бичиг,account.data_account_type_current_assets,account_tag_operating_product,0,mn_chart_1
+account_template_1203_0101,12030101,Найдваргүй авлагын хасагдуулга,account.data_account_type_current_assets,account_tag_operating_product,0,mn_chart_1
+account_template_1204_0101,12040101,Урьдчилж төлсөн ААНОАТатвар,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1204_0201,12040201,Урьдчилж төлсөн ХХОАТатвар,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1204_0301,12040301,Урьдчилж төлсөн НӨАТатвар,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1204_0401,12040401,Урьдчилж төлсөн Гаалийн албан татвар,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1204_0501,12040501,Урьдчилж төлсөн Үл хөдлөх хөрөнгийн албан татвар,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1204_0601,12040601,Урьдчилж төлсөн Газрын төлбөр,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1204_0701,12040701,Урьдчилж төлсөн АТӨЯХАТатвар,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1204_0801,12040801,Урьдчилж төлсөн Агаарын бохирдлын татвар,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1204_0901,12040901,Урьдчилж төлсөн Онцгой албан татвар,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1204_1001,12041001,Урьдчилж төлсөн Байгалийн нөөц ашиглалтын төлбөр,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1204_9901,12049901,Урьдчилж төлсөн Бусад татвар,account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1205_0101,12050101,"Урьдчилж төлсөн ЭМ, НДШ",account.data_account_type_current_assets,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_1205_0201,12050201,Урьдчилж төлсөн Сайн дурын даатгалын хураамж,account.data_account_type_current_assets,account_tag_operating_insurance2,0,mn_chart_1
+account_template_1206_0101,12060101,"Цалингийн урьдчилгаа, богино хугацаат зээл",account.data_account_type_receivable,account_tag_operating_employee,1,mn_chart_1
+account_template_1206_0201,12060201,"Төлбөр, дутагдал",account.data_account_type_receivable,account_tag_operating_employee,1,mn_chart_1
+account_template_1206_0301,12060301,Худалдааны бус авлага ААН,account.data_account_type_receivable,account.account_tag_operating,1,mn_chart_1
+account_template_1206_0401,12060401,Худалдааны бус авлага Хувь хүн,account.data_account_type_receivable,account.account_tag_operating,1,mn_chart_1
+account_template_1206_0501,12060501,Худалдааны бус авлага Холбоотой тал,account.data_account_type_receivable,account.account_tag_operating,1,mn_chart_1
+account_template_1206_0601,12060601,Богино хугацаат зээлийн авлага ААН,account.data_account_type_receivable,account.account_tag_operating,1,mn_chart_1
+account_template_1206_0701,12060701,Богино хугацаат зээлийн авлага Хувь хүн,account.data_account_type_receivable,account.account_tag_operating,1,mn_chart_1
+account_template_1206_0801,12060801,Богино хугацаат зээлийн авлага Холбоотой тал,account.data_account_type_receivable,account.account_tag_operating,1,mn_chart_1
+account_template_1206_0901,12060901,Ногдол ашгийн авлага,account.data_account_type_receivable,account_tag_investing_dividends,1,mn_chart_1
+account_template_1206_1001,12061001,Барьцаалсан авлага,account.data_account_type_receivable,account.account_tag_operating,1,mn_chart_1
+account_template_1206_1101,12061101,Хүүний авлага,account.data_account_type_receivable,account_tag_operating_interest,1,mn_chart_1
+account_template_1206_1201,12061201,Үндсэн хөрөнгө худалдааны авлага,account.data_account_type_receivable,account_tag_investing_fasset,1,mn_chart_1
+account_template_1206_1301,12061301,Биет бус хөрөнгө худалдааны авлага,account.data_account_type_receivable,account_tag_investing_iasset,1,mn_chart_1
+account_template_1206_1401,12061401,Хөрөнгө оруулалт худалдааны авлага,account.data_account_type_receivable,account_tag_investing_invest,1,mn_chart_1
+account_template_1206_9901,12069901,Бусад худалдааны бус авлага,account.data_account_type_receivable,account.account_tag_operating,1,mn_chart_1
+account_template_1301_0101,13010101,Түргэн борлогдох үнэт цаас,account.data_account_type_current_assets,account.account_tag_financing,0,mn_chart_1
+account_template_1301_0201,13010201,Хугацаат хадгаламж,account.data_account_type_current_assets,account.account_tag_financing,0,mn_chart_1
+account_template_1302_0101,13020101,Бусад дэлгэрэнгүй орлого болох бодит үнэ цэнээр илэрхийлэх санхүүгийн хөрөнгө,account.data_account_type_current_assets,account.account_tag_operating,0,mn_chart_1
+account_template_1303_0101,13030101,Хорогдуулсан өртөгөөр илэрхийлэгдэх санхүүгийн өртөг,account.data_account_type_current_assets,account.account_tag_operating,0,mn_chart_1
+account_template_1401_0101,14010101,Агуулахад байгаа бараа материал,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1401_0401,14010401,Татан авалтын өртөгийн зөрүү,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1401_0701,14010701,Амьтан,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1401_0801,14010801,Мал,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1401_0901,14010901,Ургамал,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1402_0101,14020101,Агуулахад байгаа түүхий эд материал,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1403_0301,14030301,Агуулахад байгаа үндсэн хөрөнгийн хэсэг,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1403_0401,14030401,Агуулахад байгаа хангамжийн материал,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1403_0501,14030501,Ашиглалтанд байгаа хангамжийн материал,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1403_0502,14030502,Ашиглалтанд байгаа хангамжийн материалын элэгдэл,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1404_0101,14040101,Дуусаагүй үйлдвэрлэл - Шууд материал,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1404_0201,14040201,Дуусаагүй үйлдвэрлэл - Шууд хөдөлмөр,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1404_0301,14040301,Дуусаагүй үйлдвэрлэл - НЗ,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1404_0401,14040401,Хагас боловсруулсан бүтээгдэхүүн,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1405_0101,14050101,Агентад байгаа бараа,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1406_0101,14060101,Замд яваа бараа,account.data_account_type_current_assets,,0,mn_chart_1
+account_template_1407_0101,14070101,Ирж буй барааны өртөгийн түр данс,account.data_account_type_current_assets,,1,mn_chart_1
+account_template_1408_0101,14080101,Гарч буй барааны өртөгийн түр данс,account.data_account_type_current_assets,,1,mn_chart_1
+account_template_1501_0101,15010101,Урьдчилж төлсөн түрээс,account.data_account_type_prepayments,account_tag_operating_cost,0,mn_chart_1
+account_template_1501_0201,15010201,Урьдчилж төлсөн хураамж,account.data_account_type_prepayments,account_tag_operating_loyalty,0,mn_chart_1
+account_template_1501_0301,15010301,Урьдчилж төлсөн цалин,account.data_account_type_prepayments,account_tag_operating_employee,0,mn_chart_1
+account_template_1501_0901,15010901,Урьдчилж төлсөн бусад зардал,account.data_account_type_prepayments,account.account_tag_operating,0,mn_chart_1
+account_template_1502_0101,15020101,Бэлтгэн нийлүүлэгчид төлсөн урьдчилгаа,account.data_account_type_prepayments,account_tag_operating_product,0,mn_chart_1
+account_template_1502_0201,15020201,"Дараа тайлангийн урьдчилгаа, томилолт",account.data_account_type_prepayments,account_tag_operating_employee,0,mn_chart_1
+account_template_1502_0301,15020301,Үндсэн хөрөнгө худалдан авахад төлсөн урьдчилгаа,account.data_account_type_prepayments,account_tag_investing_fasset,0,mn_chart_1
+account_template_1502_0401,15020401,Биет бус хөрөнгө худалдан авахад төлсөн урьдчилгаа,account.data_account_type_prepayments,account_tag_investing_iasset,0,mn_chart_1
+account_template_1502_0501,15020501,Хөрөнгө оруулалт худалдан авахад төлсөн урьдчилгаа,account.data_account_type_prepayments,account_tag_investing_invest,0,mn_chart_1
+account_template_1502_0601,15020601,Хангамжийн зүйлс худалдан авахад төлсөн урьдчилгаа,account.data_account_type_prepayments,account_tag_operating_product,0,mn_chart_1
+account_template_1502_0901,15020901,Бусад худалдааны бус урьдчилгаа,account.data_account_type_prepayments,account.account_tag_operating,0,mn_chart_1
+account_template_1601_0101,16010101,Бусад эргэлтийн хөрөнгө,account.data_account_type_current_assets,account.account_tag_operating,0,mn_chart_1
+account_template_1701_0101,17010101,Борлуулах зорилгоор эзэмшиж буй эргэлтийн бус хөрөнгө,account.data_account_type_current_assets,account.account_tag_operating,0,mn_chart_1
+account_template_2001_0101,20010101,Биет хөрөнгө - Газрын сайржуулалт,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2001_0201,20010201,Биет хөрөнгө - Газрын сайжруулалтын элэгдэл,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2002_0101,20020101,"Биет хөрөнгө - Барилга, байгууламж",account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2002_0201,20020201,"Биет хөрөнгө - Барилга, байгууламжын элэгдэл",account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2003_0101,20030101,Биет хөрөнгө - Машин тоног төхөөрөмж,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2003_0201,20030201,Биет хөрөнгө - Машин тоног төхөөрөмжийн элэгдэл,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2004_0101,20040101,Биет хөрөнгө - Тээврийн хэрэгсэл,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2004_0201,20040201,Биет хөрөнгө - Тээврийн хэрэгслийн элэгдэл,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2005_0101,20050101,Биет хөрөнгө - Тавилга эд хогшил,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2005_0201,20050201,Биет хөрөнгө - Тавилга эд хогшлын элэгдэл,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2006_0101,20060101,"Биет хөрөнгө - Компьютер, дагалдах хэрэгсэл",account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2006_0201,20060201,"Биет хөрөнгө - Компьютер, дагалдах хэрэгслийн элэгдэл",account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2008_0101,20080101,Биет хөрөнгө - Дуусаагүй барилга,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2008_0201,20080201,Биет хөрөнгө - Дуусаагүй барилгын элэгдэл,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2009_0101,20090101,Биет хөрөнгө - Бусад хөрөнгө,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2009_0201,20090201,Биет хөрөнгө - Бусад хөрөнгийн элэгдэл,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2101_0101,21010101,Биет бус хөрөнгө - Зохиогчийн эрх,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2101_0201,21010201,Биет бус хөрөнгө - Зохиогчийн эрхийн хорогдол,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2102_0101,21020101,Биет бус хөрөнгө - Програм хангамж,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2102_0201,21020201,Биет бус хөрөнгө - Програм хангамжын хорогдол,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2103_0101,21030101,Биет бус хөрөнгө - Патент,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2103_0201,21030201,Биет бус хөрөнгө - Патентын хорогдол,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2104_0101,21040101,Биет бус хөрөнгө - Барааны тэмдэг,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2104_0201,21040201,Биет бус хөрөнгө - Барааны тэмдэгийн хорогдол,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2105_0101,21050101,Биет бус хөрөнгө - Тусгай зөвшөөрөл,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2105_0201,21050201,Биет бус хөрөнгө - Тусгай зөвшөөрлийн хорогдол,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2109_0101,21090101,Биет бус хөрөнгө - Бусад хөрөнгө,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2109_0201,21090201,Биет бус хөрөнгө - Бусад хөрөнгийн элэгдэл,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2401_0101,24010101,Биологийн хөрөнгө - Мал амьтад,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2401_0201,24010201,Биологийн хөрөнгө - Мал амьтадын хорогдол,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2402_0101,24020101,Биологийн хөрөнгө - Ургамал,account.data_account_type_fixed_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2402_0201,24020201,Биологийн хөрөнгө - Ургамалын хорогдол,account.data_account_type_fixed_assets,,0,mn_chart_1
+account_template_2201_0101,22010101,Ашиг алдагдлаарх бодит үнэ цэнээр илэрхийлэх санхүүгийн хөрөнгө,account.data_account_type_non_current_assets,account_tag_investing_invest,0,mn_chart_1
+account_template_2204_0101,22040101,"Хараат компани, хамтарсан үйлдвэрт оруулсан хөрөнгө оруулалт",account.data_account_type_non_current_assets,account_tag_investing_invest,0,mn_chart_1
+account_template_2205_0101,22050101,Охин компанид оруулсан хөрөнгө оруулалт,account.data_account_type_non_current_assets,account_tag_investing_invest,0,mn_chart_1
+account_template_2301_0101,23010101,Хайгуул ба үнэлгээний хөрөнгө,account.data_account_type_non_current_assets,account_tag_investing_invest,0,mn_chart_1
+account_template_2501_0101,25010101,Хойшлогдсон татварын хөрөнгө,account.data_account_type_non_current_assets,account.account_tag_financing,0,mn_chart_1
+account_template_2601_0101,26010101,Хөрөнгө оруулалтын зориулалттай үндсэн хөрөнгө,account.data_account_type_non_current_assets,account_tag_investing_invest,0,mn_chart_1
+account_template_2601_0201,26010201,Үндсэн хөрөнгийн үнэ цэнийн бууралт,account.data_account_type_non_current_assets,account_tag_investing_fasset,0,mn_chart_1
+account_template_2701_0101,27010101,Урт хугацаат зээлийн авлага ААН,account.data_account_type_non_current_assets,account_tag_investing_loan,0,mn_chart_1
+account_template_2701_0201,27010201,Урт хугацаат зээлийн авлага Хувь хүн,account.data_account_type_non_current_assets,account_tag_investing_loan,0,mn_chart_1
+account_template_2701_0301,27010301,Урт хугацаат зээлийн авлага Холбоотой тал,account.data_account_type_non_current_assets,account_tag_investing_loan,0,mn_chart_1
+account_template_2709_0101,27090101,Бусад эргэлтийн бус хөрөнгө,account.data_account_type_non_current_assets,account.account_tag_financing,0,mn_chart_1
+account_template_3101_0101,31010101,Худалдааны БН-д өгөх өглөг ААН,account.data_account_type_payable,account_tag_operating_product,1,mn_chart_1
+account_template_3101_0201,31010201,Худалдааны БН-д өгөх өглөг Хувь хүн,account.data_account_type_payable,account_tag_operating_product,1,mn_chart_1
+account_template_3101_0301,31010301,Худалдааны БН-д өгөх өглөг Холбоотой тал,account.data_account_type_payable,account_tag_operating_product,1,mn_chart_1
+account_template_3201_0101,32010101,Урьдчилж орсон худалдааны орлого,account.data_account_type_current_liabilities,account_tag_operating_product,1,mn_chart_1
+account_template_3202_0101,32020101,Урьдчилж орсон түрээсийн орлого,account.data_account_type_current_liabilities,account_tag_operating_product,1,mn_chart_1
+account_template_3203_0101,32030101,Урьдчилж орсон зээлийн хүүгийн орлого,account.data_account_type_current_liabilities,account_tag_operating_interest,1,mn_chart_1
+account_template_3301_0101,33010101,Цалингийн өглөг,account.data_account_type_payable,account_tag_operating_employee,1,mn_chart_1
+account_template_3401_0101,34010101,Татварын өглөг - ААНОАТатвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_0201,34010201,Татварын өглөг - НӨАТатвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_0301,34010301,Татварын өглөг - ХХОАТатвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_0401,34010401,Татварын өглөг - Гаалийн албан татвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_0501,34010501,Татварын өглөг - Үл хөдлөх хөрөнгийн албан татвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_0601,34010601,Татварын өглөг - Газрын албан татвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_0701,34010701,Татварын өглөг - АТӨЯХАТатвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_0801,34010801,Татварын өглөг - Агаарын бохирдлын татвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_0901,34010901,Татварын өглөг - Онцгой албан татвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_1001,34011001,Татварын өглөг - Байгалийн нөөц ашиглалтын татвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_1101,34011101,Татварын өглөг – НХАТатвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3401_9901,34019901,Татварын өглөг - Бусад татвар,account.data_account_type_current_liabilities,account_tag_operating_taxrefund,0,mn_chart_1
+account_template_3402_0101,34020101,"ЭМ, НДШ-ийн өглөг",account.data_account_type_current_liabilities,account_tag_operating_insurance,0,mn_chart_1
+account_template_3402_0201,34020201,Сайн дурын даатгалын өглөг,account.data_account_type_current_liabilities,account_tag_operating_insurance2,0,mn_chart_1
+account_template_3501_0101,35010101,Банкны богино хугацаат зээлийн өглөг,account.data_account_type_payable,account_tag_operating_interest,1,mn_chart_1
+account_template_3501_0201,35010201,ББСБ-ын богино хугацаат зээлийн өглөг,account.data_account_type_payable,account_tag_operating_interest,1,mn_chart_1
+account_template_3501_0301,35010301,Хувь хүний богино хугацаат зээлийн өглөг,account.data_account_type_payable,account_tag_operating_interest,1,mn_chart_1
+account_template_3502_0101,35020101,Банкны богино хугацаат зээлийн хүү,account.data_account_type_payable,account_tag_operating_interest,1,mn_chart_1
+account_template_3502_0201,35020201,ББСБ-ын богино хугацаат зээлийн хүү,account.data_account_type_payable,account_tag_operating_interest,1,mn_chart_1
+account_template_3502_0301,35020301,Хувь хүний богино хугацаат зээлийн хүү,account.data_account_type_payable,account_tag_operating_interest,1,mn_chart_1
+account_template_3601_0101,36010101,Ногдол ашгийн өглөг,account.data_account_type_current_liabilities,account_tag_investing_dividends,0,mn_chart_1
+account_template_3701_0101,37010101,Нөөц өр төлбөр,account.data_account_type_current_liabilities,account_tag_exchange,0,mn_chart_1
+account_template_3801_0101,38010101,Даатгалын өглөг,account.data_account_type_payable,account_tag_operating_insurance2,1,mn_chart_1
+account_template_3802_0101,38020101,Гишүүнчлэлийн өглөг,account.data_account_type_payable,account_tag_operating_loyalty,1,mn_chart_1
+account_template_3803_0101,38030101,Ажилчдад өгөх өглөг,account.data_account_type_payable,account_tag_operating_employee,1,mn_chart_1
+account_template_3804_0101,38040101,Хувь хүмүүст өгөх өглөг,account.data_account_type_payable,account.account_tag_operating,1,mn_chart_1
+account_template_3805_0101,38050101,"Баталгаа, барьцааны өглөг",account.data_account_type_payable,account.account_tag_operating,1,mn_chart_1
+account_template_3806_0101,38060101,Худалдааны бус бэлтгэн нийлүүлэгчид өгөх өглөг - ААН,account.data_account_type_payable,account.account_tag_operating,1,mn_chart_1
+account_template_3806_0201,38060201,Худалдааны бус бэлтгэн нийлүүлэгчид өгөх өглөг - Хувь хүн,account.data_account_type_payable,account.account_tag_operating,1,mn_chart_1
+account_template_3806_0301,38060301,Худалдааны бус бэлтгэн нийлүүлэгчид өгөх өглөг - Холбоотой тал,account.data_account_type_payable,account.account_tag_operating,1,mn_chart_1
+account_template_3806_0401,38060401,Худалдан авсан үндсэн хөрөнгийн өглөг,account.data_account_type_payable,account_tag_investing_fasset,1,mn_chart_1
+account_template_3806_0501,38060501,Худалдан авсан хөрөнгө оруулалтын өглөг,account.data_account_type_payable,account_tag_investing_fasset,1,mn_chart_1
+account_template_3809_0101,38090101,Бусад богино хугацаат өр төлбөр,account.data_account_type_current_liabilities,account.account_tag_operating,0,mn_chart_1
+account_template_3901_0101,39010101,Борлуулах зорилгоор эзэмшиж буй эргэлтийн бус хөрөнгөнд хамаарах өр,account.data_account_type_current_liabilities,account_tag_exchange,0,mn_chart_1
+account_template_4001_0101,40010101,Банкны урт хугацаат зээл,account.data_account_type_non_current_liabilities,account_tag_investing_loan,0,mn_chart_1
+account_template_4001_0201,40010201,ББСБ-ын урт хугацаат зээл,account.data_account_type_non_current_liabilities,account_tag_investing_loan,0,mn_chart_1
+account_template_4001_0301,40010301,Хувь хүнээс авсан урт хугацаат зээл,account.data_account_type_non_current_liabilities,account_tag_investing_loan,0,mn_chart_1
+account_template_4001_0401,40010401,Холбоотой талын урт хугацаат зээл,account.data_account_type_non_current_liabilities,account_tag_investing_loan,0,mn_chart_1
+account_template_4002_0101,40020101,Урт хугацаат нөөц өр төлбөр,account.data_account_type_non_current_liabilities,account_tag_investing_loan,0,mn_chart_1
+account_template_4003_0101,40030101,Хойшлогдсон татварын өр,account.data_account_type_non_current_liabilities,account_tag_investing_loan,0,mn_chart_1
+account_template_4009_0101,40090101,Бусад урт хугацаат өр төлбөр,account.data_account_type_non_current_liabilities,account_tag_investing_loan,0,mn_chart_1
+account_template_4101_0101,41010101,Эздийн өмч - төрийн,account.data_account_type_equity,,0,mn_chart_1
+account_template_4102_0101,41020101,Эздийн өмч - хувийн,account.data_account_type_equity,,0,mn_chart_1
+account_template_4103_0101,41030101,Эздийн өмч - хувьцаат,account.data_account_type_equity,,0,mn_chart_1
+account_template_4201_0101,42010101,Халаасны хувьцаа,account.data_account_type_equity,,0,mn_chart_1
+account_template_4301_0101,43010101,Нэмж төлөгдсөн капитал,account.data_account_type_equity,,0,mn_chart_1
+account_template_4401_0101,44010101,Хөрөнгийн дахин үнэлгээний нэмэгдэл,account.data_account_type_equity,,0,mn_chart_1
+account_template_4501_0101,45010101,Гадаад валютын хөрвүүлэлтийн нөөц,account.data_account_type_equity,,0,mn_chart_1
+account_template_4509_0101,45090101,Эздийн өмчийн бусад хэсэг,account.data_account_type_equity,,0,mn_chart_1
+account_template_4601_0101,46010101,Тайлант үеийн хуримтлагдсан ашиг,account.data_account_type_equity,,0,mn_chart_1
+account_template_4601_0201,46010201,Өмнөх үеийн хуримтлагдсан ашиг,account.data_account_type_equity,,0,mn_chart_1
+account_template_5101_0101,51010101,Борлуулалтын орлого,account.data_account_type_revenue,"account_tag_operating_product,tax_report_tag7",0,mn_chart_1
+account_template_5101_0201,51010201,Борлуулалтын орлого (НӨАТ-с чөлөөлөгдөх),account.data_account_type_revenue,"account_tag_operating_product,tax_report_tag2",0,mn_chart_1
+account_template_5102_0101,51020101,Түрээсийн орлого,account.data_account_type_other_income,"account_tag_operating_product,tax_report_tag5",0,mn_chart_1
+account_template_5103_0101,51030101,Хүүний орлого,account.data_account_type_other_income,"account_tag_operating_interest,tax_report_tag39",0,mn_chart_1
+account_template_5104_0101,51040101,Ногдол ашгийн орлого,account.data_account_type_other_income,"account_tag_investing_dividends,tax_report_tag46",0,mn_chart_1
+account_template_5105_0101,51050101,Эрхийн шимтгэлийн орлого,account.data_account_type_other_income,"account_tag_operating_loyalty,tax_report_tag48",0,mn_chart_1
+account_template_5201_0101,52010101,Тооллогын зөрүүний олз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5201_0201,52010201,Тооллогын зөрүүний алдагдал,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5201_0901,52010901,Бусад дэлгэрэнгүй орлого,account.data_account_type_other_income,"account.account_tag_operating,tax_report_tag8",0,mn_chart_1
+account_template_5301_0101,53010101,Валютын ханшийн зөрүүний хэрэгжсэн олз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5301_0201,53010201,Валютын ханшийн зөрүүний хэрэгжээгүй олз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5302_0101,53020101,Валютын ханшийн зөрүүний хэрэгжсэн гарз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5302_0201,53020201,Валютын ханшийн зөрүүний хэрэгжээгүй гарз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5401_0101,54010101,Үндсэн хөрөнгө борлуулсны олз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5401_0201,54010201,Үндсэн хөрөнгө борлуулсны гарз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5501_0101,55010101,Биет бус хөрөнгө борлуулсны олз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5501_0201,55010201,Биет бус хөрөнгө борлуулсны гарз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5601_0101,56010101,Хөрөнгө оруулалт борлуулсны олз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5601_0201,56010201,Хөрөнгө оруулалт борлуулсны гарз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5701_0101,57010101,Үндсэн бус үйл ажиллагааны бусад олз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_5701_0201,57010201,Үндсэн бус үйл ажиллагааны бусад гарз,account.data_account_type_other_income,,0,mn_chart_1
+account_template_6001_0101,60010101,Борлуулалтын хөнгөлөлт,account.data_account_type_revenue,,0,mn_chart_1
+account_template_6002_0101,60020101,Борлуулалтын урамшуулал,account.data_account_type_revenue,,0,mn_chart_1
+account_template_6003_0101,60030101,Борлуулалтын буцаалт,account.data_account_type_revenue,,0,mn_chart_1
+account_template_6004_0101,60040101,Бэлнээр төлсөний хөнгөлөлт,account.data_account_type_revenue,,0,mn_chart_1
+account_template_6005_0101,60050101,Хугацаанаас өмнө төлсний хөнгөлөлт,account.data_account_type_revenue,,0,mn_chart_1
+account_template_6006_0101,60060101,Борлуулалтын үнийн бууралт,account.data_account_type_revenue,,0,mn_chart_1
+account_template_6101_0101,61010101,"Борлуулсан бараа, бүтээгдэхүүний өртөг",account.data_account_type_direct_costs,,0,mn_chart_1
+account_template_6102_0101,61020101,Борлуулсан үйлчилгээний өртөг,account.data_account_type_direct_costs,,0,mn_chart_1
+account_template_7001_0101,70010101,Удирд - Үндсэн цалингийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7001_0201,70010201,Удирд - Нэмэгдэл цалингийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0101,70020101,Удирд - ЭМД-ийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0201,70020201,Удирд - НДШ-ийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0301,70020301,Удирд - Банкны шимтгэл хураамжын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0401,70020401,Удирд - Гадаад томилолтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0402,70020402,Удирд - Дотоод томилолтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0501,70020501,Удирд - Бичиг хэргийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0601,70020601,Удирд - Шуудан холбооны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0602,70020602,Удирд - Суурин утасны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0603,70020603,Удирд - Үүрэн утасны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0604,70020604,Удирд - Интернетийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0701,70020701,Удирд - Мэргэжлийн үйлчилгээний зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0801,70020801,Удирд - Хүний нөөцийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_0901,70020901,Удирд - Сонин сэтгүүл захиалгын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_1001,70021001,Удирд - Даатгалын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_1101,70021101,Удирд - Ашиглалтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_1201,70021201,Удирд - Засварын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_1301,70021301,Удирд - Элэгдэл хорогдлын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_1401,70021401,Удирд - Түрээсийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_1501,70021501,Удирд - Харуул хамгаалалтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_1601,70021601,Удирд - Цэвэрлэгээ үйлчилгээний зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_1701,70021701,Удирд - Тээврийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_1801,70021801,Удирд - Шатахууны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_1901,70021901,Удирд - Хүлээн авалтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_2001,70022001,Удирд - Зар сурталчилгааны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_2101,70022101,Удирд - Төсөл хөгжлийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_2201,70022201,"Удирд - Судалгаа, туршилтын зардал",account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_2301,70022301,"Удирд - Хүргэлт, түгээлтийн зардал",account.data_account_type_expenses,,0,mn_chart_1
+account_template_7002_9901,70029901,Удирд - Үйл ажиллагааны бусад зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7101_0101,71010101,Борл - Үндсэн цалингийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7101_0201,71010201,Борл - Нэмэгдэл цалингийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0101,71020101,Борл - ЭМД-ийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0201,71020201,Борл - НДШ-ийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0301,71020301,Борл - Банкны шимтгэл хураамжын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0401,71020401,Борл - Гадаад томилолтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0402,71020402,Борл - Дотоод томилолтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0501,71020501,Борл - Бичиг хэргийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0601,71020601,Борл - Шуудан холбооны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0602,71020602,Борл - Суурин утасны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0603,71020603,Борл - Үүрэн утасны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0604,71020604,Борл - Интернетийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0701,71020701,Борл - Мэргэжлийн үйлчилгээний зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0801,71020801,Борл - Хүний нөөцийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_0901,71020901,Борл - Сонин сэтгүүл захиалгын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_1001,71021001,Борл - Даатгалын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_1101,71021101,Борл - Ашиглалтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_1201,71021201,Борл - Засварын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_1301,71021301,Борл - Элэгдэл хорогдлын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_1401,71021401,Борл - Түрээсийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_1501,71021501,Борл - Харуул хамгаалалтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_1601,71021601,Борл - Цэвэрлэгээ үйлчилгээний зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_1701,71021701,Борл - Тээврийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_1801,71021801,Борл - Шатахууны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_1901,71021901,Борл - Хүлээн авалтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_2001,71022001,Борл - Зар сурталчилгааны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_2101,71022101,Борл - Төсөл хөгжлийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_2201,71022201,"Борл - Судалгаа, туршилтын зардал",account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_2301,71022301,"Борл - Хүргэлт, түгээлтийн зардал",account.data_account_type_expenses,,0,mn_chart_1
+account_template_7102_9901,71029901,Борл - Үйл ажиллагааны бусад зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7201_0101,72010101,Эргэлтийн хөрөнгө санхүүжүүлэх зээлийн хүүгийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7202_0101,72020101,Үндсэн хөрөнгө санхүүжүүлэх зээлийн хүүгийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7203_0101,72030101,Төсөл санхүүжүүлэх зээлийн хүүгийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7301_0101,73010101,"Хайгуул, үнэлгээний зардал",account.data_account_type_expenses,,0,mn_chart_1
+account_template_7401_0101,74010101,"Алданги, торгуулийн зардал",account.data_account_type_expenses,,0,mn_chart_1
+account_template_7402_0101,74020101,"Хандив, тусламжийн зардал",account.data_account_type_expenses,,0,mn_chart_1
+account_template_7403_0101,74030101,Найдваргүй авлагын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7404_0101,74040101,Болзошгүй алдагдлын санд төвлөрүүлсэн хөрөнгийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7405_0101,74050101,Франчайзын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7406_0101,74060101,Банкны үйлчилгээний шимтгэлийн зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7407_0101,74070101,Хөрөнгө акталж устгасны зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7408_0101,74080101,Үйлдвэрийн ослын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7409_0101,74090101,"Туслах үйлдвэрлэл, үйлчилгээний зардал",account.data_account_type_expenses,,0,mn_chart_1
+account_template_7410_0101,74100101,Үндсэн хөрөнгийн үнэ цэнийн бууралтын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_7499_0101,74990101,Бусад ерөнхий зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_9101_0101,91010101,Тайлант үеийн орлогын албан татварын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_9102_0101,91020101,Хойшлогдсон татварын зардал,account.data_account_type_expenses,,0,mn_chart_1
+account_template_9103_0101,91030101,Татвар ногдохгүй бусад дэлгэрэнгүй орлого,account.data_account_type_expenses,,0,mn_chart_1
+account_template_9301_0101,93010101,Эхний үлдэгдлийн эх үүсвэр,account.data_unaffected_earnings,,0,mn_chart_1
diff --git a/addons/l10n_mn/data/account.chart.template.csv b/addons/l10n_mn/data/account.chart.template.csv
new file mode 100644
index 00000000000..3c63ba77781
--- /dev/null
+++ b/addons/l10n_mn/data/account.chart.template.csv
@@ -0,0 +1,2 @@
+id,property_account_receivable_id/id,property_account_payable_id/id,property_account_expense_categ_id/id,property_account_income_categ_id/id,income_currency_exchange_account_id/id,expense_currency_exchange_account_id/id
+mn_chart_1,account_template_1201_0201,account_template_3101_0201,account_template_7002_1101,account_template_5101_0201,account_template_5301_0201,account_template_5302_0201
diff --git a/addons/l10n_mn/data/account.tax.group.csv b/addons/l10n_mn/data/account.tax.group.csv
new file mode 100644
index 00000000000..9e2a71ba648
--- /dev/null
+++ b/addons/l10n_mn/data/account.tax.group.csv
@@ -0,0 +1,7 @@
+id,name,sequence
+account_tax_group1,НӨАТ ногдох бараа,10
+account_tax_group2,НӨАТ ногдох үйлчилгээ,10
+account_tax_group3,Экспортын борлуулалт,11
+account_tax_group4,"Худалдан авсан бараа, үйлчилгээ",12
+account_tax_group5,Санхүүгийн түрээсийн зүйл,13
+account_tax_group6,НӨАТ-с чөлөөлөгдөх,14
diff --git a/addons/l10n_mn/data/account.tax.template.csv b/addons/l10n_mn/data/account.tax.template.csv
new file mode 100644
index 00000000000..e691ee773fb
--- /dev/null
+++ b/addons/l10n_mn/data/account.tax.template.csv
@@ -0,0 +1,40 @@
+"id","name","amount","price_include","sequence","account_id/id","refund_account_id/id","amount_type","type_tax_use","description","tax_group_id/id","tag_ids/id","chart_template_id/id"
+"account_tax_sale_vat1","НӨАТ бараа","10.0","True","1","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group1","vat_report_tag5","mn_chart_1"
+"account_tax_purchase_vat1","НӨАТ-тэй худалдан авалт","10.0","True","1","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group4","vat_report_tag33,vat_report_tag36","mn_chart_1"
+"account_tax_purchase_vat2","НӨАТ-гүй худалдан авалт","0.0","True","10","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","0%","account_tax_group4","vat_report_tag33","mn_chart_1"
+"account_tax_sale_vat2","НӨАТ 0% борлуулалт","0.0","True","10","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","0%","account_tax_group6","vat_report_tag2","mn_chart_1"
+"account_tax_sale_vat3","НӨАТ чөлөөлөх борлуулалт","0.0","True","10","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","0%","account_tax_group6","vat_report_tag2","mn_chart_1"
+"account_tax_sale_vat4","НӨАТ бусад барааны борлуулалт","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group1","vat_report_tag6","mn_chart_1"
+"account_tax_sale_vat5","НӨАТ эрх борлуулалт","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group1","vat_report_tag7","mn_chart_1"
+"account_tax_sale_vat6","НӨАТ татан буугдах үеийн борлуулалт","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group1","vat_report_tag8","mn_chart_1"
+"account_tax_sale_vat7","НӨАТ өрийн төлбөрт өгсөн бараа","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group1","vat_report_tag9","mn_chart_1"
+"account_tax_sale_vat8","НӨАТ нотариат үйлчилгээ","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag11","mn_chart_1"
+"account_tax_sale_vat9","НӨАТ өрийн төлбөрт өгсөн үйлчилгээ","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag12","mn_chart_1"
+"account_tax_sale_vat10","НӨАТ цахилгаан, дулаан, ус, шуудан, холбоо","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag13","mn_chart_1"
+"account_tax_sale_vat11","НӨАТ бараа түрээслүүлэх, эзэмшүүлэх","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag14","mn_chart_1"
+"account_tax_sale_vat12","НӨАТ байр түрээслүүлэх, эзэмшүүлэх","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag15","mn_chart_1"
+"account_tax_sale_vat13","НӨАТ хөрөнгө түрээслүүлэх, эзэмшүүлэх","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag16","mn_chart_1"
+"account_tax_sale_vat14","НӨАТ бүтээл, загвар, зохиогчийн эрх","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag17","mn_chart_1"
+"account_tax_sale_vat15","НӨАТ хонжворт сугалаа, таавар, бооцоо","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag18","mn_chart_1"
+"account_tax_sale_vat16","НӨАТ зуучлалын үйлчилгээ","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag19","mn_chart_1"
+"account_tax_sale_vat17","НӨАТ авсан хүү торгууль, алданги","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag20","mn_chart_1"
+"account_tax_sale_vat18","НӨАТ хөрөнгийн үнэлгээний үйлчилгээ","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag21","mn_chart_1"
+"account_tax_sale_vat19","НӨАТ төсвийн санхүүжилт, татаас","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag22","mn_chart_1"
+"account_tax_sale_vat20","НӨАТ хууль зүйн үйлчилгээ","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag23","mn_chart_1"
+"account_tax_sale_vat21","НӨАТ үсчин, гоо сайхан, угаалга, цэвэрлэгээ","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag24","mn_chart_1"
+"account_tax_sale_vat22","НӨАТ бусад үйлчилгээ","10.0","True","20","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group2","vat_report_tag25","mn_chart_1"
+"account_tax_sale_vat23","НӨАТ 0% экспорт бараа","0.0","True","30","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","0%","account_tax_group3","vat_report_tag28","mn_chart_1"
+"account_tax_sale_vat24","НӨАТ 0% экспорт үйлчилгээ","0.0","True","30","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","0%","account_tax_group3","vat_report_tag29","mn_chart_1"
+"account_tax_purchase_vat3","НӨАТ импортоор авсан бараа, үйлчилгээ","10.0","True","40","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group4","vat_report_tag33,vat_report_tag35","mn_chart_1"
+"account_tax_purchase_vat4","НӨАТ төлөгчөөр бүртгүүлэх үед худалдан авсан","10.0","True","40","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group4","vat_report_tag33,vat_report_tag37","mn_chart_1"
+"account_tax_purchase_vat5","НӨАТ мал аж ахуй эрхлэгчээс худалдан авсан","10.0","True","40","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group4","vat_report_tag33,vat_report_tag38","mn_chart_1"
+"account_tax_purchase_vat6","НӨАТ автомашин, эд анги худалдан авалт","10.0","True","40","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group4","vat_report_tag33,vat_report_tag41","mn_chart_1"
+"account_tax_purchase_vat7","НӨАТ ажлын хэрэгцээнд авсан бараа","10.0","True","40","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group4","vat_report_tag33,vat_report_tag42","mn_chart_1"
+"account_tax_purchase_vat8","НӨАТ импортоор авсан үндсэн хөрөнгө","10.0","True","40","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group4","vat_report_tag33,vat_report_tag43","mn_chart_1"
+"account_tax_purchase_vat9","НӨАТ чөлөөлөгдөх үйлдвэрлэлд зориулсан бараа, үйлчилгээ","10.0","True","40","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group4","vat_report_tag33,vat_report_tag44","mn_chart_1"
+"account_tax_purchase_vat10","НӨАТ хайгуулд зориулсан импортын бараа, үйлчилгээ","10.0","True","40","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group4","vat_report_tag33,vat_report_tag45","mn_chart_1"
+"account_tax_purchase_vat11","НӨАТ импортын бусад хамааралгүй бараа, үйлчилгээ","10.0","True","40","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group4","vat_report_tag33,vat_report_tag46","mn_chart_1"
+"account_tax_sale_vat25","НӨАТ дотоодод зарсан санхүүгийн түрээс","10.0","True","50","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group5","vat_report_tag48","mn_chart_1"
+"account_tax_sale_vat26","НӨАТ Факторинг, форфайтинг хэлцлийн үйлчилгээ","10.0","True","50","account_template_3401_0201","account_template_3401_0201","Percentage of Price","Sales","10%","account_tax_group5","vat_report_tag49","mn_chart_1"
+"account_tax_purchase_vat12","НӨАТ Санхүүгийн түрээсийн худалдан авалт","10.0","True","50","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group5","vat_report_tag51","mn_chart_1"
+"account_tax_purchase_vat13","НӨАТ Факторинг, форфайтинг худалдан авалт","10.0","True","50","account_template_1204_0301","account_template_1204_0301","Percentage of Price","Purchases","10%","account_tax_group5","vat_report_tag52","mn_chart_1"
diff --git a/addons/l10n_mn/data/account_chart_template_configuration_data.xml b/addons/l10n_mn/data/account_chart_template_configuration_data.xml
new file mode 100644
index 00000000000..2de735fa360
--- /dev/null
+++ b/addons/l10n_mn/data/account_chart_template_configuration_data.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/addons/l10n_mn/data/account_chart_template_data.xml b/addons/l10n_mn/data/account_chart_template_data.xml
new file mode 100644
index 00000000000..bb64e86c95e
--- /dev/null
+++ b/addons/l10n_mn/data/account_chart_template_data.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ Mongolia
+ 11
+ 10
+ 1109
+ 8
+
+
+
+
diff --git a/addons/l10n_mn/data/account_fiscal_position_template_data.xml b/addons/l10n_mn/data/account_fiscal_position_template_data.xml
new file mode 100644
index 00000000000..94780439b51
--- /dev/null
+++ b/addons/l10n_mn/data/account_fiscal_position_template_data.xml
@@ -0,0 +1,399 @@
+
+
+
+
+
+
+ Дотоодын борлуулалт - Худалдан авалт
+
+
+
+
+
+
+ НӨАТ 0% (3000000)
+
+
+
+
+ НӨАТ-гүй (2000000)
+
+
+
+
+ Гадаад борлуулалт - Худалдан авалт
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/l10n_mn/static/description/icon.png b/addons/l10n_mn/static/description/icon.png
new file mode 100644
index 00000000000..dae4b68b81e
Binary files /dev/null and b/addons/l10n_mn/static/description/icon.png differ
diff --git a/addons/l10n_za/__init__.py b/addons/l10n_za/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/addons/l10n_za/__manifest__.py b/addons/l10n_za/__manifest__.py
new file mode 100644
index 00000000000..9389b0fdd93
--- /dev/null
+++ b/addons/l10n_za/__manifest__.py
@@ -0,0 +1,26 @@
+# -*- encoding: utf-8 -*-
+
+# Copyright (C) 2017 Paradigm Digital ().
+
+{
+ 'name': 'South Africa - Accounting',
+ 'version': '1.0',
+ 'category': 'Localization',
+ 'description': """
+This is the latest basic South African localisation necessary to run Odoo in ZA:
+================================================================================
+ - a generic chart of accounts
+ - SARS VAT Ready Structure""",
+ 'author': 'Paradigm Digital',
+ 'website': 'https://www.paradigmdigital.co.za',
+ 'depends': ['account', 'base_vat'],
+ 'data': [
+ 'data/account.account.tag.csv',
+ 'data/account.tax.group.csv',
+ 'data/account_chart_template_data.xml',
+ 'data/account.account.template.csv',
+ 'data/account_tax_template_data.xml',
+ 'data/account_chart_template_post_data.xml',
+ 'data/account_chart_template_configure_data.xml',
+ ],
+}
diff --git a/addons/l10n_za/data/account.account.tag.csv b/addons/l10n_za/data/account.account.tag.csv
new file mode 100644
index 00000000000..27d71d89298
--- /dev/null
+++ b/addons/l10n_za/data/account.account.tag.csv
@@ -0,0 +1,17 @@
+id,name,applicability
+tag_ST1,ST1,taxes
+tag_ST1A,ST1A,taxes
+tag_ST2,ST2,taxes
+tag_ST2A,ST2A,taxes
+tag_ST3,ST3,taxes
+tag_ST5,ST5,taxes
+tag_ST7,ST7,taxes
+tag_ST10,ST10,taxes
+tag_ST12,ST12,taxes
+tag_PT14,PT14,taxes
+tag_PT14A,PT14A,taxes
+tag_PT15,PT15,taxes
+tag_PT15A,PT15A,taxes
+tag_PT16,PT16,taxes
+tag_PT17,PT17,taxes
+tag_PT18,PT18,taxes
diff --git a/addons/l10n_za/data/account.account.template.csv b/addons/l10n_za/data/account.account.template.csv
new file mode 100644
index 00000000000..51d0f219092
--- /dev/null
+++ b/addons/l10n_za/data/account.account.template.csv
@@ -0,0 +1,111 @@
+id,code,name,user_type_id/id,reconcile,chart_template_id/id
+100020,100020,Stock Valuation Account,account.data_account_type_current_assets,True,default_chart_template
+100030,100030,Stock Work In Progress,account.data_account_type_current_assets,False,default_chart_template
+100040,100040,Stock Finished Goods,account.data_account_type_current_assets,False,default_chart_template
+100050,100050,Stock Delivered Control Account,account.data_account_type_current_assets,True,default_chart_template
+100060,100060,Purchase Tax Control Account,account.data_account_type_current_assets,False,default_chart_template
+100070,100070,Other Current Assets,account.data_account_type_current_assets,False,default_chart_template
+110010,110010,Debtors Control Account,account.data_account_type_receivable,True,default_chart_template
+110020,110020,Sundry Debtors,account.data_account_type_receivable,True,default_chart_template
+124010,124010,Credit Card Merchant Account,account.data_account_type_liquidity,False,default_chart_template
+126010,126010,Cash In Hand,account.data_account_type_liquidity,False,default_chart_template
+130010,130010,Prepayments,account.data_account_type_prepayments,False,default_chart_template
+140010,140010,Software,account.data_account_type_fixed_assets,False,default_chart_template
+140020,140020,Patents & Trademarks,account.data_account_type_fixed_assets,False,default_chart_template
+140030,140030,Fixtures & Fittings,account.data_account_type_fixed_assets,False,default_chart_template
+140040,140040,Land & Buildings,account.data_account_type_fixed_assets,False,default_chart_template
+140050,140050,Motor Vehicles,account.data_account_type_fixed_assets,False,default_chart_template
+140060,140060,Office Equipment (incl computer equipment),account.data_account_type_fixed_assets,False,default_chart_template
+140070,140070,Plant & Machinery,account.data_account_type_fixed_assets,False,default_chart_template
+150010,150010,Non-current assets,account.data_account_type_non_current_assets,False,default_chart_template
+200010,200010,Stock Received Control Account,account.data_account_type_current_liabilities,True,default_chart_template
+200020,200020,Sundry Creditors,account.data_account_type_current_liabilities,False,default_chart_template
+200030,200030,Other Creditors,account.data_account_type_current_liabilities,False,default_chart_template
+200040,200040,Accruals,account.data_account_type_current_liabilities,False,default_chart_template
+200050,200050,Bad debt provision,account.data_account_type_current_liabilities,False,default_chart_template
+200060,200060,Sales Tax Control Account,account.data_account_type_current_liabilities,False,default_chart_template
+200070,200070,Manual Adjustments & VAT,account.data_account_type_current_liabilities,False,default_chart_template
+200080,200080,Loans,account.data_account_type_current_liabilities,False,default_chart_template
+200090,200090,Hire Purchase,account.data_account_type_current_liabilities,False,default_chart_template
+200100,200100,Mortgages,account.data_account_type_current_liabilities,False,default_chart_template
+210010,210010,Company Credit Card,account.data_account_type_credit_card,False,default_chart_template
+220010,220010,Creditors Control Account,account.data_account_type_payable,True,default_chart_template
+220020,220020,SARS - VAT Account,account.data_account_type_payable,True,default_chart_template
+220030,220030,P.A.Y.E. & UIF,account.data_account_type_payable,True,default_chart_template
+220040,220040,Net Wages,account.data_account_type_payable,True,default_chart_template
+220050,220050,Pension Fund,account.data_account_type_payable,True,default_chart_template
+220060,220060,Corporation Tax,account.data_account_type_payable,True,default_chart_template
+300010,300010,Called up share capital,account.data_account_type_equity,False,default_chart_template
+300020,300020,Share premium account,account.data_account_type_equity,False,default_chart_template
+300030,300030,Revaluation reserve,account.data_account_type_equity,False,default_chart_template
+300040,300040,Other reserves,account.data_account_type_equity,False,default_chart_template
+300050,300050,Capital,account.data_account_type_equity,False,default_chart_template
+300060,300060,Dividends,account.data_account_type_equity,False,default_chart_template
+300070,300070,Drawings,account.data_account_type_equity,False,default_chart_template
+400010,400010,Undistributed Profits/Losses,account.data_unaffected_earnings,False,default_chart_template
+500010,500010,Sales category 1,account.data_account_type_revenue,False,default_chart_template
+500020,500020,Sales category 2,account.data_account_type_revenue,False,default_chart_template
+500030,500030,Sales category 3,account.data_account_type_revenue,False,default_chart_template
+500040,500040,Sales category 4,account.data_account_type_revenue,False,default_chart_template
+500050,500050,Bank Interest received,account.data_account_type_revenue,False,default_chart_template
+500060,500060,Investment Interest received,account.data_account_type_revenue,False,default_chart_template
+500070,500070,Profits/Losses on disposals of assets,account.data_account_type_revenue,False,default_chart_template
+500080,500080,Rental Income,account.data_account_type_revenue,False,default_chart_template
+510010,510010,Other Income,account.data_account_type_other_income,False,default_chart_template
+600010,600010,Cost of sales 1,account.data_account_type_direct_costs,False,default_chart_template
+600020,600020,Cost of sales 2,account.data_account_type_direct_costs,False,default_chart_template
+600030,600030,Cost of sales 3,account.data_account_type_direct_costs,False,default_chart_template
+600040,600040,Cost of sales 4,account.data_account_type_direct_costs,False,default_chart_template
+610010,610010,Marketing,account.data_account_type_expenses,False,default_chart_template
+610020,610020,Exhibitions and events,account.data_account_type_expenses,False,default_chart_template
+610030,610030,PR,account.data_account_type_expenses,False,default_chart_template
+610040,610040,Distribution vehicles,account.data_account_type_expenses,False,default_chart_template
+610050,610050,Distribution salaries and wages,account.data_account_type_expenses,False,default_chart_template
+610060,610060,Shipping,account.data_account_type_expenses,False,default_chart_template
+610070,610070,Directors pension,account.data_account_type_expenses,False,default_chart_template
+610080,610080,Directors remuneration,account.data_account_type_expenses,False,default_chart_template
+610090,610090,Gross Salaries,account.data_account_type_expenses,False,default_chart_template
+610100,610100,Employers SDL & UIF,account.data_account_type_expenses,False,default_chart_template
+610110,610110,Subcontractors payments,account.data_account_type_expenses,False,default_chart_template
+610120,610120,Rent and rates,account.data_account_type_expenses,False,default_chart_template
+610130,610130,Light / heat and power,account.data_account_type_expenses,False,default_chart_template
+610140,610140,Repairs and maintenance,account.data_account_type_expenses,False,default_chart_template
+610150,610150,Car hire,account.data_account_type_expenses,False,default_chart_template
+610160,610160,Car fuel,account.data_account_type_expenses,False,default_chart_template
+610170,610170,Car maintenance,account.data_account_type_expenses,False,default_chart_template
+610180,610180,Telephone,account.data_account_type_expenses,False,default_chart_template
+610190,610190,Internet & hosting,account.data_account_type_expenses,False,default_chart_template
+610200,610200,Mobiles,account.data_account_type_expenses,False,default_chart_template
+610210,610210,Stationery,account.data_account_type_expenses,False,default_chart_template
+610220,610220,Office consumables,account.data_account_type_expenses,False,default_chart_template
+610230,610230,Postage and Carriage,account.data_account_type_expenses,False,default_chart_template
+610240,610240,Books,account.data_account_type_expenses,False,default_chart_template
+610250,610250,Network costs,account.data_account_type_expenses,False,default_chart_template
+610260,610260,Software expenses,account.data_account_type_expenses,False,default_chart_template
+610270,610270,Other computer costs,account.data_account_type_expenses,False,default_chart_template
+610280,610280,Recruitment fees,account.data_account_type_expenses,False,default_chart_template
+610290,610290,Other admin expenses,account.data_account_type_expenses,False,default_chart_template
+610300,610300,Accounting,account.data_account_type_expenses,False,default_chart_template
+610310,610310,Auditing,account.data_account_type_expenses,False,default_chart_template
+610320,610320,Consultancy,account.data_account_type_expenses,False,default_chart_template
+610330,610330,Legal and professional charges,account.data_account_type_expenses,False,default_chart_template
+610340,610340,Exchange gains/losses,account.data_account_type_expenses,False,default_chart_template
+610350,610350,Other sundry expenses,account.data_account_type_expenses,False,default_chart_template
+610360,610360,Bad debts,account.data_account_type_expenses,False,default_chart_template
+610370,610370,Interest paid,account.data_account_type_expenses,False,default_chart_template
+610380,610380,Bank Charges,account.data_account_type_expenses,False,default_chart_template
+610390,610390,Donations,account.data_account_type_expenses,False,default_chart_template
+610400,610400,Entertaining,account.data_account_type_expenses,False,default_chart_template
+610410,610410,Insurance,account.data_account_type_expenses,False,default_chart_template
+610420,610420,Travel and subsistence,account.data_account_type_expenses,False,default_chart_template
+610430,610430,Corporation tax expense,account.data_account_type_expenses,False,default_chart_template
+610440,610440,Foreign Exchange Gains/Losses,account.data_account_type_expenses,False,default_chart_template
+610450,610450,Price Differences Control Account,account.data_account_type_expenses,False,default_chart_template
+610460,610460,Cash Register Gains/Losses,account.data_account_type_expenses,False,default_chart_template
+620010,620010,Software Depreciation,account.data_account_type_depreciation,False,default_chart_template
+620020,620020,Patents & Trademarks Depreciation,account.data_account_type_depreciation,False,default_chart_template
+620030,620030,Fixtures and fittings Depreciation,account.data_account_type_depreciation,False,default_chart_template
+620040,620040,Land and buildings Depreciation,account.data_account_type_depreciation,False,default_chart_template
+620050,620050,Motor vehicles Depreciation,account.data_account_type_depreciation,False,default_chart_template
+620060,620060,Office equipment (inc computer equipment) Depreciation,account.data_account_type_depreciation,False,default_chart_template
+620070,620070,Plant and machinery Depreciation,account.data_account_type_depreciation,False,default_chart_template
diff --git a/addons/l10n_za/data/account.tax.group.csv b/addons/l10n_za/data/account.tax.group.csv
new file mode 100644
index 00000000000..ec35d11456c
--- /dev/null
+++ b/addons/l10n_za/data/account.tax.group.csv
@@ -0,0 +1,3 @@
+id,name
+tax_group_0,VAT 0%
+tax_group_1,VAT 15%
diff --git a/addons/l10n_za/data/account_chart_template_configure_data.xml b/addons/l10n_za/data/account_chart_template_configure_data.xml
new file mode 100644
index 00000000000..6a3db99a6d5
--- /dev/null
+++ b/addons/l10n_za/data/account_chart_template_configure_data.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/addons/l10n_za/data/account_chart_template_data.xml b/addons/l10n_za/data/account_chart_template_data.xml
new file mode 100644
index 00000000000..652646a5d18
--- /dev/null
+++ b/addons/l10n_za/data/account_chart_template_data.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ South African Tax and Account Chart Template (by Paradigm Digital)
+ 1200
+ 1250
+ 1010
+ 6
+
+
+
+
diff --git a/addons/l10n_za/data/account_chart_template_post_data.xml b/addons/l10n_za/data/account_chart_template_post_data.xml
new file mode 100644
index 00000000000..1d1fa398173
--- /dev/null
+++ b/addons/l10n_za/data/account_chart_template_post_data.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/l10n_za/data/account_tax_template_data.xml b/addons/l10n_za/data/account_tax_template_data.xml
new file mode 100644
index 00000000000..1c588eaa16c
--- /dev/null
+++ b/addons/l10n_za/data/account_tax_template_data.xml
@@ -0,0 +1,212 @@
+
+
+
+
+ 15%
+
+ sale
+ Standard Rate
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ sale
+ Standard Rate (Capital Goods)
+ percent
+ 15
+
+
+
+
+
+
+
+ 0%
+
+ sale
+ Zero Rate
+ percent
+ 0
+
+
+
+
+
+
+
+ 0%
+
+ sale
+ Zero Rate Exports
+ percent
+ 0
+
+
+
+
+
+
+
+ 0%
+
+ sale
+ Exempt and Non-Supplies
+ percent
+ 0
+
+
+
+
+
+
+
+ 15%
+
+ sale
+ Accommodation (28+ days)
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ sale
+ Accommodation (Under 28 days)
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ sale
+ Export of Second-hand Goods/ Change in Use
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ sale
+ VAT Adjustments and Manual VAT
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ purchase
+ Standard Rate
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ purchase
+ Standard Rate (Capital Goods)
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ purchase
+ Capital Goods Imported
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ purchase
+ Goods and Services Imported
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ purchase
+ Change in Use
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ purchase
+ Bad Debts
+ percent
+ 15
+
+
+
+
+
+
+
+ 15%
+
+ purchase
+ Other Adjustments
+ percent
+ 15
+
+
+
+
+
+
+
diff --git a/addons/l10n_za/static/description/icon.png b/addons/l10n_za/static/description/icon.png
new file mode 100644
index 00000000000..1f82ab5459c
Binary files /dev/null and b/addons/l10n_za/static/description/icon.png differ
diff --git a/addons/mail/models/mail_activity.py b/addons/mail/models/mail_activity.py
index 4acce13d381..bf0b0141604 100644
--- a/addons/mail/models/mail_activity.py
+++ b/addons/mail/models/mail_activity.py
@@ -298,7 +298,7 @@ class MailActivity(models.Model):
target_record = self.env[activity.res_model].browse(activity.res_id)
if hasattr(target_record, 'company_id') and (
target_record.company_id != target_user.company_id and (
- len(target_user.company_ids) > 1)):
+ len(target_user.sudo().company_ids) > 1)):
return # in that case we skip the check, assuming it would fail because of the company
model.browse(activity.res_id).check_access_rule('read')
except exceptions.AccessError:
diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py
index e2ebb16c69e..39510206641 100644
--- a/addons/mail/models/mail_message.py
+++ b/addons/mail/models/mail_message.py
@@ -4,6 +4,7 @@
import logging
import re
+from binascii import Error as binascii_error
from operator import itemgetter
from email.utils import formataddr
from openerp.http import request
@@ -933,7 +934,7 @@ class Message(models.Model):
def _invalidate_documents(self):
""" Invalidate the cache of the documents followed by ``self``. """
for record in self:
- if record.model and record.res_id and 'message_ids' in self.env[record.model]:
+ if record.model and record.res_id and issubclass(self.pool[record.model], self.pool['mail.thread']):
self.env[record.model].invalidate_cache(fnames=[
'message_ids',
'message_unread',
@@ -968,16 +969,21 @@ class Message(models.Model):
key = match.group(2)
if not data_to_url.get(key):
name = match.group(4) if match.group(4) else 'image%s' % len(data_to_url)
- attachment = Attachments.create({
- 'name': name,
- 'datas': match.group(2),
- 'datas_fname': name,
- 'res_model': values.get('model'),
- 'res_id': values.get('res_id'),
- })
- attachment.generate_access_token()
- values['attachment_ids'].append((4, attachment.id))
- data_to_url[key] = ['/web/image/%s?access_token=%s' % (attachment.id, attachment.access_token), name]
+ try:
+ attachment = Attachments.create({
+ 'name': name,
+ 'datas': match.group(2),
+ 'datas_fname': name,
+ 'res_model': values.get('model'),
+ 'res_id': values.get('res_id'),
+ })
+ except binascii_error:
+ _logger.warning("Impossible to create an attachment out of badly formated base64 embedded image. Image has been removed.")
+ return match.group(3) # group(3) is the url ending single/double quote matched by the regexp
+ else:
+ attachment.generate_access_token()
+ values['attachment_ids'].append((4, attachment.id))
+ data_to_url[key] = ['/web/image/%s?access_token=%s' % (attachment.id, attachment.access_token), name]
return '%s%s alt="%s"' % (data_to_url[key][0], match.group(3), data_to_url[key][1])
values['body'] = _image_dataurl.sub(base64_to_boundary, tools.ustr(values['body']))
diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js
index dc23354fe7b..635d261bf3f 100644
--- a/addons/mail/static/src/js/chatter.js
+++ b/addons/mail/static/src/js/chatter.js
@@ -139,7 +139,7 @@ var Chatter = Widget.extend({
if (this.fields.activity) {
this.fields.activity.$el.detach();
}
- if (this.fields.thread) {
+ if (this.fields.thread && this.fields.thread.$el ) {
this.fields.thread.$el.detach();
}
@@ -398,7 +398,7 @@ var Chatter = Widget.extend({
self.fields.followers.$el.insertBefore(self.$('.o_chatter_button_attachment'));
}
}
- if (self.fields.thread) {
+ if (self.fields.thread && self.fields.thread.$el) {
self.fields.thread.$el.appendTo(self.$el);
}
}).always(function () {
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index 75b867d3ad5..95ae6372c41 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -372,7 +372,6 @@ class MailComposer(models.TransientModel):
- normal mode: return rendered values
/!\ for x2many field, this onchange return command instead of ids
"""
- attachment_ids = []
if template_id and composition_mode == 'mass_mail':
template = self.env['mail.template'].browse(template_id)
fields = ['subject', 'body_html', 'email_from', 'reply_to', 'mail_server_id']
@@ -388,6 +387,7 @@ class MailComposer(models.TransientModel):
values = self.generate_email_for_composer(template_id, [res_id])[res_id]
# transform attachments into attachment_ids; not attached to the document because this will
# be done further in the posting process, allowing to clean database if email not send
+ attachment_ids = []
Attachment = self.env['ir.attachment']
for attach_fname, attach_datas in values.pop('attachments', []):
data_attach = {
@@ -399,6 +399,8 @@ class MailComposer(models.TransientModel):
'type': 'binary', # override default_type from context, possibly meant for another model!
}
attachment_ids.append(Attachment.create(data_attach).id)
+ if values.get('attachment_ids', []) or attachment_ids:
+ values['attachment_ids'] = [(5,)] + values.get('attachment_ids', []) + attachment_ids
else:
default_values = self.with_context(default_composition_mode=composition_mode, default_model=model, default_res_id=res_id).default_get(['composition_mode', 'model', 'res_id', 'parent_id', 'partner_ids', 'subject', 'body', 'email_from', 'reply_to', 'attachment_ids', 'mail_server_id'])
values = dict((key, default_values[key]) for key in ['subject', 'body', 'partner_ids', 'email_from', 'reply_to', 'attachment_ids', 'mail_server_id'] if key in default_values)
@@ -411,8 +413,6 @@ class MailComposer(models.TransientModel):
# this force the complete replacement of x2many field with
# command and is compatible with onchange api.v7
values = self._convert_to_write(values)
- if attachment_ids:
- values.update(attachment_ids=[(6, 0, attachment_ids)])
return {'value': values}
diff --git a/addons/mrp/models/stock_warehouse.py b/addons/mrp/models/stock_warehouse.py
index bd4de62bdb4..788157476d0 100644
--- a/addons/mrp/models/stock_warehouse.py
+++ b/addons/mrp/models/stock_warehouse.py
@@ -249,7 +249,7 @@ class StockWarehouse(models.Model):
@api.multi
def _get_all_routes(self):
- routes = super(StockWarehouse, self).get_all_routes_for_wh()
+ routes = super(StockWarehouse, self)._get_all_routes()
routes |= self.filtered(lambda self: self.manufacture_to_resupply and self.manufacture_pull_id and self.manufacture_pull_id.route_id).mapped('manufacture_pull_id').mapped('route_id')
return routes
diff --git a/addons/payment/controllers/portal.py b/addons/payment/controllers/portal.py
index 55a4d08e4b9..8b4585b9393 100644
--- a/addons/payment/controllers/portal.py
+++ b/addons/payment/controllers/portal.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
+import psycopg2
from odoo import http, _
from odoo.http import request
@@ -40,7 +41,7 @@ class PaymentProcessing(http.Controller):
# I prefer to let the controller chose when to access to payment.transaction using sudo
return request.session.get("__payment_tx_ids__", [])
- @http.route(['/payment/process'], type="http", auth="public", website=True)
+ @http.route(['/payment/process'], type="http", auth="public", website=True, sitemap=False)
def payment_status_page(self, **kwargs):
# When the customer is redirect to this website page,
# we retrieve the payment transaction list from his session
@@ -93,11 +94,15 @@ class PaymentProcessing(http.Controller):
tx_to_process = payment_transaction_ids.filtered(lambda x: x.state == 'done' and x.is_processed is False)
try:
tx_to_process._post_process_after_done()
+ except psycopg2.OperationalError as e:
+ request.env.cr.rollback()
+ result['success'] = False
+ result['error'] = "tx_process_retry"
except Exception as e:
request.env.cr.rollback()
result['success'] = False
result['error'] = str(e)
- _logger.error("Error while processing transaction(s) %s, exception \"%s\"", tx_to_process.ids, str(e))
+ _logger.exception("Error while processing transaction(s) %s, exception \"%s\"", tx_to_process.ids, str(e))
return result
@@ -122,7 +127,7 @@ class WebsitePayment(http.Controller):
}
return request.render("payment.pay_methods", values)
- @http.route(['/website_payment/pay'], type='http', auth='public', website=True)
+ @http.route(['/website_payment/pay'], type='http', auth='public', website=True, sitemap=False)
def pay(self, reference='', order_id=None, amount=False, currency_id=None, acquirer_id=None, **kw):
env = request.env
user = env.user.sudo()
@@ -262,7 +267,7 @@ class WebsitePayment(http.Controller):
except Exception as e:
return request.redirect('/payment/process')
- @http.route(['/website_payment/confirm'], type='http', auth='public', website=True)
+ @http.route(['/website_payment/confirm'], type='http', auth='public', website=True, sitemap=False)
def confirm(self, **kw):
tx_id = int(kw.get('tx_id', 0))
if tx_id:
diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py
index 0f2ce5d222a..6847cb76461 100644
--- a/addons/payment/models/payment_acquirer.py
+++ b/addons/payment/models/payment_acquirer.py
@@ -899,7 +899,7 @@ class PaymentTransaction(models.Model):
# custom create
custom_method_name = '%s_create' % acquirer.provider
- if hasattr(acquirer, custom_method_name):
+ if hasattr(self, custom_method_name):
values.update(getattr(self, custom_method_name)(values))
if not values.get('reference'):
diff --git a/addons/payment/static/src/js/payment_processing.js b/addons/payment/static/src/js/payment_processing.js
index 4397bf134f1..5409b915526 100644
--- a/addons/payment/static/src/js/payment_processing.js
+++ b/addons/payment/static/src/js/payment_processing.js
@@ -54,6 +54,8 @@ odoo.define('payment.processing', function (require) {
}
else {
switch(data.error) {
+ case "tx_process_retry":
+ break;
case "no_tx_found":
self.displayContent("payment.no_tx_found", {});
break;
@@ -63,11 +65,7 @@ odoo.define('payment.processing', function (require) {
}
}
self.startPolling();
- }).always(function(){
- if ($.blockUI) {
- $.unblockUI();
- }
- }).fail(function(e) {
+ }).fail(function(e) {
self.displayContent("payment.rpc_error", {});
self.startPolling();
});
@@ -117,6 +115,7 @@ odoo.define('payment.processing', function (require) {
},
displayContent: function (xmlid, render_values) {
var html = Qweb.render(xmlid, render_values);
+ $.unblockUI();
this.$el.find('.o_payment_processing_content').html(html);
},
displayLoading: function () {
diff --git a/addons/payment_authorize/models/authorize_request.py b/addons/payment_authorize/models/authorize_request.py
index 79d0a7589c5..922d9df1bf1 100644
--- a/addons/payment_authorize/models/authorize_request.py
+++ b/addons/payment_authorize/models/authorize_request.py
@@ -226,6 +226,12 @@ class AuthorizeAPI():
etree.SubElement(customer, "email").text = partner.email or ''
response = self._authorize_request(root)
res = dict()
+ if not response.find('customerProfileId'):
+ _logger.warning(
+ 'Unable to create customer payment profile, data missing from transaction. Transaction_id: %s - Partner_id: %s'
+ % (transaction_id, partner)
+ )
+ return res
res['profile_id'] = response.find('customerProfileId').text
res['payment_profile_id'] = response.find('customerPaymentProfileIdList/numericString').text
root_profile = self._base_tree('getCustomerPaymentProfileRequest')
diff --git a/addons/payment_authorize/models/payment.py b/addons/payment_authorize/models/payment.py
index 5a6ed0bf8e1..946f031484d 100644
--- a/addons/payment_authorize/models/payment.py
+++ b/addons/payment_authorize/models/payment.py
@@ -228,17 +228,18 @@ class TxAuthorize(models.Model):
(self.type == 'form_save' or self.acquirer_id.save_token == 'always'):
transaction = AuthorizeAPI(self.acquirer_id)
res = transaction.create_customer_profile_from_tx(self.partner_id, self.acquirer_reference)
- token_id = self.env['payment.token'].create({
- 'authorize_profile': res.get('profile_id'),
- 'name': res.get('name'),
- 'acquirer_ref': res.get('payment_profile_id'),
- 'acquirer_id': self.acquirer_id.id,
- 'partner_id': self.partner_id.id,
- })
- self.payment_token_id = token_id
+ if res:
+ token_id = self.env['payment.token'].create({
+ 'authorize_profile': res.get('profile_id'),
+ 'name': res.get('name'),
+ 'acquirer_ref': res.get('payment_profile_id'),
+ 'acquirer_id': self.acquirer_id.id,
+ 'partner_id': self.partner_id.id,
+ })
+ self.payment_token_id = token_id
- if self.payment_token_id:
- self.payment_token_id.verified = True
+ if self.payment_token_id:
+ self.payment_token_id.verified = True
return True
elif status_code == self._authorize_pending_tx_status:
self.write({'acquirer_reference': data.get('x_trans_id')})
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index 43eb5e433b1..edae32282db 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -878,7 +878,7 @@ class PosOrder(models.Model):
# a serialnumber always has a quantity of 1 product, a lot number takes the full quantity of the order line
qty = 1.0
if stock_production_lot.product_id.tracking == 'lot':
- qty = pos_pack_lot.pos_order_line_id.qty
+ qty = abs(pos_pack_lot.pos_order_line_id.qty)
qty_done += qty
pack_lots.append({'lot_id': stock_production_lot.id, 'qty': qty})
else:
diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js
index 41952ae6cff..1fe17d449a5 100644
--- a/addons/point_of_sale/static/src/js/models.js
+++ b/addons/point_of_sale/static/src/js/models.js
@@ -1457,7 +1457,7 @@ exports.Orderline = Backbone.Model.extend({
var lots_required = 1;
if (this.product.tracking == 'serial') {
- lots_required = this.quantity;
+ lots_required = Math.abs(this.quantity);
}
return lots_required;
@@ -1913,8 +1913,11 @@ var PacklotlineCollection = Backbone.Collection.extend({
set_quantity_by_lot: function() {
if (this.order_line.product.tracking == 'serial') {
- var valid_lots = this.get_valid_lots();
- this.order_line.set_quantity(valid_lots.length);
+ var valid_lots_quantity = this.get_valid_lots().length;
+ if (this.order_line.quantity < 0){
+ valid_lots_quantity = -valid_lots_quantity;
+ }
+ this.order_line.set_quantity(valid_lots_quantity);
}
}
});
diff --git a/addons/pos_sale/report/sale_report.py b/addons/pos_sale/report/sale_report.py
index 735aaf56169..a340d0b7c4d 100644
--- a/addons/pos_sale/report/sale_report.py
+++ b/addons/pos_sale/report/sale_report.py
@@ -14,11 +14,6 @@ class SaleReport(models.Model):
('invoiced', 'Invoiced')], string='Status', readonly=True)
def _query(self, with_clause='', fields={}, groupby='', from_clause=''):
- with_clause += '''pol_tax AS (SELECT line_tax.pos_order_line_id AS pol_id,sum(tax.amount/100) AS amount
- FROM account_tax_pos_order_line_rel line_tax
- JOIN account_tax tax ON line_tax.account_tax_id = tax.id
- GROUP BY line_tax.pos_order_line_id)'''
-
res = super(SaleReport, self)._query(with_clause, fields, groupby, from_clause)
select_ = '''
@@ -29,10 +24,10 @@ class SaleReport(models.Model):
sum(l.qty * u.factor) AS qty_delivered,
CASE WHEN pos.state = 'invoiced' THEN sum(qty) ELSE 0 END AS qty_invoiced,
CASE WHEN pos.state != 'invoiced' THEN sum(qty) ELSE 0 END AS qty_to_invoice,
- (COALESCE(COALESCE((l.price_unit * l.qty * u.factor * ((1 - COALESCE(l.discount / 100, 0)) )) + (l.price_unit * l.qty * u.factor * ((1 - COALESCE(l.discount / 100, 0)) ) * pol_tax.amount), (l.price_unit * l.qty * u.factor * ((1 - COALESCE(l.discount / 100, 0)) ))), 1.0)) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS price_total,
- (COALESCE((l.qty * u.factor * l.price_unit * ((1 - COALESCE(l.discount / 100, 0)) )), 1.0)) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS price_subtotal,
- (CASE WHEN pos.state != 'invoiced' THEN ((l.price_unit * l.qty * u.factor * ((1 - COALESCE(l.discount / 100, 0)))) + (l.price_unit * l.qty * u.factor * (1 - COALESCE(l.discount / 100, 0)) * pol_tax.amount)) ELSE 0 END) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS amount_to_invoice,
- (CASE WHEN pos.state = 'invoiced' THEN ((l.price_unit * l.qty * u.factor * ((1 - COALESCE(l.discount / 100, 0)) )) + (l.price_unit * l.qty * u.factor * (1 - COALESCE(l.discount / 100, 0)) * pol_tax.amount)) ELSE 0 END) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS amount_invoiced,
+ SUM(l.price_subtotal_incl) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS price_total,
+ SUM(l.price_subtotal) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS price_subtotal,
+ (CASE WHEN pos.state != 'invoiced' THEN SUM(l.price_subtotal_incl) ELSE 0 END) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS amount_to_invoice,
+ (CASE WHEN pos.state = 'invoiced' THEN SUM(l.price_subtotal_incl) ELSE 0 END) / MIN(CASE COALESCE(pos.currency_rate, 0) WHEN 0 THEN 1.0 ELSE pos.currency_rate END) AS amount_invoiced,
count(*) AS nbr,
pos.name AS name,
pos.date_order AS date,
@@ -70,7 +65,6 @@ class SaleReport(models.Model):
pos_order_line l
join pos_order pos on (l.order_id=pos.id)
left join res_partner partner ON (pos.partner_id = partner.id OR pos.partner_id = NULL)
- left join pol_tax pol_tax on pol_tax.pol_id=l.id
left join product_product p on (l.product_id=p.id)
left join product_template t on (p.product_tmpl_id=t.id)
LEFT JOIN uom_uom u ON (u.id=t.uom_id)
@@ -98,7 +92,6 @@ class SaleReport(models.Model):
partner.country_id,
partner.industry_id,
partner.commercial_partner_id,
- pol_tax.amount,
u.factor,
config.crm_team_id
'''
diff --git a/addons/product/models/product.py b/addons/product/models/product.py
index 4ac541f526b..7b4c3bb2c9b 100644
--- a/addons/product/models/product.py
+++ b/addons/product/models/product.py
@@ -335,6 +335,7 @@ class ProductProduct(models.Model):
else:
self.partner_ref = self.display_name
+ self.image_variant = False
@api.depends('product_tmpl_id', 'attribute_value_ids')
def _compute_product_template_attribute_value_ids(self):
# Fetch and pre-map the values first for performance. It assumes there
@@ -391,6 +392,16 @@ class ProductProduct(models.Model):
product._set_standard_price(vals.get('standard_price') or 0.0)
# `_get_variant_id_for_combination` depends on existing variants
self.clear_caches()
+ self.env['product.template'].invalidate_cache(
+ fnames=[
+ 'valid_archived_variant_ids',
+ 'valid_existing_variant_ids',
+ 'product_variant_ids',
+ 'product_variant_id',
+ 'product_variant_count'
+ ],
+ ids=products.mapped('product_tmpl_id').ids
+ )
return products
@api.multi
diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py
index c6c8eb92325..4d20b5afbeb 100644
--- a/addons/product/models/product_template.py
+++ b/addons/product/models/product_template.py
@@ -135,8 +135,10 @@ class ProductTemplate(models.Model):
compute="_compute_valid_attributes", string='Valid Product Attribute Values Without No Variant Attributes', help="Technical compute")
valid_product_attribute_wnva_ids = fields.Many2many('product.attribute',
compute="_compute_valid_attributes", string='Valid Product Attributes Without No Variant Attributes', help="Technical compute")
+ # valid_archived_variant_ids deprecated
valid_archived_variant_ids = fields.Many2many('product.product',
- compute="_compute_valid_attributes", string='Valid Archived Variants', help="Technical compute")
+ compute="_compute_valid_archived_variant_ids", string='Valid Archived Variants', help="Technical compute")
+ # valid_existing_variant_ids deprecated
valid_existing_variant_ids = fields.Many2many('product.product',
compute="_compute_valid_existing_variant_ids", string='Valid Existing Variants', help="Technical compute")
@@ -601,8 +603,6 @@ class ProductTemplate(models.Model):
For what is considered an archived variant, see `_has_valid_attributes`.
"""
- archived_variants = self.env['product.product'].search([('product_tmpl_id', 'in', self.ids), ('active', '=', False)])
-
# prefetch
self.mapped('attribute_line_ids.value_ids.id')
self.mapped('attribute_line_ids.attribute_id.create_variant')
@@ -617,6 +617,12 @@ class ProductTemplate(models.Model):
record.valid_product_attribute_ids = record.valid_product_template_attribute_line_ids.mapped('attribute_id')
record.valid_product_attribute_wnva_ids = record.valid_product_template_attribute_line_wnva_ids.mapped('attribute_id')
+ @api.multi
+ def _compute_valid_archived_variant_ids(self):
+ """This compute is done outside of `_compute_valid_attributes` because
+ it is often not needed, and it can be very bad on performance."""
+ archived_variants = self.env['product.product'].search([('product_tmpl_id', 'in', self.ids), ('active', '=', False)])
+ for record in self:
valid_value_ids = record.valid_product_attribute_value_wnva_ids
valid_attribute_ids = record.valid_product_attribute_wnva_ids
@@ -708,23 +714,23 @@ class ProductTemplate(models.Model):
:return: dict of exclusions
- exclusions: from this product itself
+ - parent_combination: ids of the given parent_combination
- parent_exclusions: from the parent_combination
- - archived_combinations: variants that are archived
- - existing_combinations: variants that are existing (as opposed to
- deleted, because deleted need to be considered impossible if
- there are no dynamic attributes).
- - has_dynamic_attributes: whether there is a dynamic attribute
- - no_variant_product_template_attribute_value_ids: values that are
- no_variant
+ - archived_combinations: deprecated
+ - existing_combinations: deprecated
+ - has_dynamic_attributes: deprecated
+ - no_variant_product_template_attribute_value_ids: deprecated
"""
self.ensure_one()
+ parent_combination = parent_combination or self.env['product.template.attribute.value']
return {
'exclusions': self._get_own_attribute_exclusions(),
'parent_exclusions': self._get_parent_attribute_exclusions(parent_combination),
- 'archived_combinations': self._get_archived_combinations(),
+ 'parent_combination': parent_combination.ids,
+ 'archived_combinations': [],
'has_dynamic_attributes': self.has_dynamic_attributes(),
- 'existing_combinations': self._get_existing_combinations(),
- 'no_variant_product_template_attribute_value_ids': self._get_no_variant_product_template_attribute_values(),
+ 'existing_combinations': [],
+ 'no_variant_product_template_attribute_value_ids': [],
}
@api.multi
@@ -777,29 +783,21 @@ class ProductTemplate(models.Model):
@api.multi
def _get_archived_combinations(self):
+ """Deprecated"""
self.ensure_one()
- """Get archived combinations.
-
- Array, each element is an array with ids of an archived combination.
- """
return [archived_variant.product_template_attribute_value_ids.ids
for archived_variant in self.valid_archived_variant_ids]
@api.multi
def _get_existing_combinations(self):
+ """Deprecated"""
self.ensure_one()
- """Get existing combinations.
-
- Needed because when not using dynamic attributes, the combination is
- not ok if it doesn't exist (= if the variant has been deleted).
-
- Array, each element is an array with ids of an existing combination.
- """
return [variant.product_template_attribute_value_ids.ids
for variant in self.valid_existing_variant_ids]
@api.multi
def _get_no_variant_product_template_attribute_values(self):
+ """Deprecated"""
self.ensure_one()
product_template_attribute_values = self._get_valid_product_template_attribute_lines().mapped('product_template_value_ids')
return product_template_attribute_values.filtered(
@@ -845,9 +843,16 @@ class ProductTemplate(models.Model):
# combination has different values than the ones configured on the template
return False
- if not self.has_dynamic_attributes() and not self._get_variant_for_combination(combination):
- # the variant has been deleted
- return False
+ variant = self._get_variant_for_combination(combination)
+
+ if self.has_dynamic_attributes():
+ if variant and not variant.active:
+ # dynamic and the variant has been archived
+ return False
+ else:
+ if not variant or not variant.active:
+ # not dynamic, the variant has been archived or deleted
+ return False
exclusions = self._get_own_attribute_exclusions()
if exclusions:
@@ -864,11 +869,6 @@ class ProductTemplate(models.Model):
if exclusion in combination.ids:
return False
- filtered_combination = combination._without_no_variant_attributes()
- archived_combinations = self._get_archived_combinations()
- if archived_combinations and filtered_combination.ids in archived_combinations:
- return False
-
return True
@api.multi
@@ -902,7 +902,7 @@ class ProductTemplate(models.Model):
for pav in attribute_values:
domain = expression.AND([[('attribute_value_ids', 'in', pav.id)], domain])
- res = self.env['product.product'].with_context(active_test=False).search(domain)
+ res = self.env['product.product'].with_context(active_test=False).search(domain, order='active DESC')
# The domain above is checking for the `product.attribute.value`, but we
# need to make sure it's the same `product.template.attribute.value`.
diff --git a/addons/product/tests/test_product_attribute_value_config.py b/addons/product/tests/test_product_attribute_value_config.py
index e966adbdbdc..497f6fbeba3 100644
--- a/addons/product/tests/test_product_attribute_value_config.py
+++ b/addons/product/tests/test_product_attribute_value_config.py
@@ -312,6 +312,21 @@ class TestProductAttributeValueConfig(TestProductAttributeValueSetup):
variant.unlink()
self.assertFalse(self.computer._is_combination_possible(computer_ssd_256 + computer_ram_8 + computer_hdd_1))
+ # CASE: if multiple variants exist for the same combination and at least
+ # one of them is not archived, the combination is possible
+ values = self.ssd_256 + self.ram_8 + self.hdd_1
+ self.env['product.product'].create({
+ 'product_tmpl_id': self.computer.id,
+ 'attribute_value_ids': [(6, 0, values.ids)],
+ 'active': False,
+ })
+ self.env['product.product'].create({
+ 'product_tmpl_id': self.computer.id,
+ 'attribute_value_ids': [(6, 0, values.ids)],
+ 'active': True,
+ })
+ self.assertTrue(self.computer._is_combination_possible(computer_ssd_256 + computer_ram_8 + computer_hdd_1))
+
def test_get_first_possible_combination(self):
computer_ssd_256 = self._get_product_template_attribute_value(self.ssd_256)
computer_ssd_512 = self._get_product_template_attribute_value(self.ssd_512)
diff --git a/addons/product/tests/test_variants.py b/addons/product/tests/test_variants.py
index 9381868b623..cc929171aef 100644
--- a/addons/product/tests/test_variants.py
+++ b/addons/product/tests/test_variants.py
@@ -1,6 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import base64
+import io
+from PIL import Image
+
from . import common
from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase
@@ -461,3 +465,72 @@ class TestVariantsManyAttributes(common.TestAttributesCommon):
self.assertEqual(len(toto.attribute_line_ids.mapped('attribute_id')), 10)
self.assertEqual(len(toto.attribute_line_ids.mapped('value_ids')), 100)
self.assertEqual(len(toto.product_variant_ids), 0)
+
+class TestVariantsImages(common.TestProductCommon):
+
+ def setUp(self):
+ res = super(TestVariantsImages, self).setUp()
+
+ self.colors = {'red': '#FF0000', 'green': '#00FF00', 'blue': '#0000FF'}
+ self.images = {}
+
+ product_attribute = self.env['product.attribute'].create({'name': 'Color'})
+
+ self.template = self.env['product.template'].create({
+ 'name': 'template',
+ })
+
+ for color in self.colors:
+ attr = self.env['product.attribute.value'].create({
+ 'name': color,
+ 'attribute_id': product_attribute.id,
+ })
+
+ f = io.BytesIO()
+ Image.new('RGB', (800, 500), self.colors[color]).save(f, 'PNG')
+ f.seek(0)
+ self.images.update({color: base64.b64encode(f.read())})
+
+ self.env['product.product'].create({
+ 'image_variant': self.images[color],
+ 'attribute_value_ids': [(6, 0, [attr.id])],
+ 'product_tmpl_id': self.template.id,
+ })
+ # the first one has no image, no color
+ self.variants = self.template.product_variant_ids.sorted('id')
+
+ return res
+
+ def test_variant_images(self):
+ """Check that on variant, the image used is the image_variant if set,
+ and defaults to the template image otherwise.
+ """
+ f = io.BytesIO()
+ Image.new('RGB', (800, 500), '#000000').save(f, 'PNG')
+ f.seek(0)
+ image_black = base64.b64encode(f.read())
+
+ images = self.variants.mapped('image_variant')
+ self.assertEqual(len(set(images)), 4)
+
+ variant_no_image = self.variants[0]
+ self.assertFalse(variant_no_image.image)
+ self.template.image = image_black
+
+ # the first has no image variant, all the others do
+ self.assertFalse(variant_no_image.image_variant)
+ self.assertTrue(all(images[1:]))
+
+ # template image is the same as this one, since it has no image variant
+ self.assertEqual(variant_no_image.image, self.template.image)
+ # having changed the template image should not have changed these
+ self.assertEqual(images[1:], self.variants.mapped('image')[1:])
+
+
+ def test_update_images_with_archived_variants(self):
+ """Update images after variants have been archived"""
+ self.variants[1:].write({'active': False})
+ self.variants[0].image = self.images['red']
+ self.assertEqual(self.template.image, self.images['red'])
+ self.assertEqual(self.variants[0].image_variant, False)
+ self.assertEqual(self.variants[0].image, self.images['red'])
diff --git a/addons/product_email_template/models/product.py b/addons/product_email_template/models/product.py
index 9ef844e8f46..e043717e6e8 100644
--- a/addons/product_email_template/models/product.py
+++ b/addons/product_email_template/models/product.py
@@ -11,4 +11,6 @@ class ProductTemplate(models.Model):
_inherit = "product.template"
email_template_id = fields.Many2one('mail.template', string='Product Email Template',
- help='Send a product-specific email once the invoice is paid.')
+ help='When validating an invoice, an email will be sent to the customer '
+ 'based on this template. The customer will receive an email for each '
+ 'product linked to an email template.')
diff --git a/addons/product_email_template/views/product_views.xml b/addons/product_email_template/views/product_views.xml
index 393ab6d3ec9..9afb300f64f 100644
--- a/addons/product_email_template/views/product_views.xml
+++ b/addons/product_email_template/views/product_views.xml
@@ -10,7 +10,7 @@
0
- 0:
+ if (discount > 0 and new_list_price > 0) or (discount < 0 and new_list_price < 0):
self.discount = discount
def _is_delivery(self):
diff --git a/addons/sale/static/src/js/product_configurator_mixin.js b/addons/sale/static/src/js/product_configurator_mixin.js
index 0d49a4f245e..bb72b4b428a 100644
--- a/addons/sale/static/src/js/product_configurator_mixin.js
+++ b/addons/sale/static/src/js/product_configurator_mixin.js
@@ -1,6 +1,7 @@
odoo.define('sale.ProductConfiguratorMixin', function (require) {
'use strict';
+var concurrency = require('web.concurrency');
var core = require('web.core');
var utils = require('web.utils');
var ajax = require('web.ajax');
@@ -8,11 +9,11 @@ var _t = core._t;
var ProductConfiguratorMixin = {
events: {
- 'click .css_attribute_color input': '_onChangeColorAttribute',
+ 'change .css_attribute_color input': '_onChangeColorAttribute',
'change .main_product:not(.in_cart) input.js_quantity': 'onChangeAddQuantity',
- 'click button.js_add_cart_json': 'onClickAddCartJSON',
'change [data-attribute_exclusions]': 'onChangeVariant'
},
+ // isSelectedVariantAllowed deprecated
isSelectedVariantAllowed: true,
//--------------------------------------------------------------------------
@@ -20,23 +21,11 @@ var ProductConfiguratorMixin = {
//--------------------------------------------------------------------------
/**
- * When a product is added or when the quantity is changed,
- * we need to refresh the total price row
- * TODO awa: add a container context to avoid global selectors ?
+ * This method is only useful in the configurator modal.
+ *
+ * @deprecated
*/
- computePriceTotal: function () {
- if ($('.js_price_total').length){
- var price = 0;
- $('.js_product.in_cart').each(function (){
- var quantity = parseInt($('input[name="add_qty"]').first().val());
- price += parseFloat($(this).find('.js_raw_price').html()) * quantity;
- });
-
- $('.js_price_total .oe_currency_value').html(
- this._priceToStr(parseFloat(price))
- );
- }
- },
+ computePriceTotal: function () {},
/**
* When a variant is changed, this will check:
@@ -49,6 +38,20 @@ var ProductConfiguratorMixin = {
* @param {MouseEvent} ev
*/
onChangeVariant: function (ev) {
+ var $parent = $(ev.target).closest('.js_product');
+ if (!$parent.data('uniqueId')) {
+ $parent.data('uniqueId', _.uniqueId());
+ }
+ this._throttledGetCombinationInfo($parent.data('uniqueId'))(ev);
+ },
+ /**
+ * @see onChangeVariant
+ *
+ * @private
+ * @param {Event} ev
+ * @returns {Deferred}
+ */
+ _getCombinationInfo: function (ev) {
var self = this;
var $component;
@@ -64,16 +67,21 @@ var ProductConfiguratorMixin = {
var qty = $component.find('input[name="add_qty"]').val();
var $parent = $(ev.target).closest('.js_product');
+
var combination = this.getSelectedVariantValues($parent);
+ var parentCombination = $parent.find('ul[data-attribute_exclusions]').data('attribute_exclusions').parent_combination;
+
+ var productTemplateId = parseInt($parent.find('.product_template_id').val());
self._checkExclusions($parent, combination);
- ajax.jsonRpc(this._getUri('/product_configurator/get_combination_info'), 'call', {
- product_template_id: parseInt($parent.find('.product_template_id').val()),
- product_id: this._getProductId($parent),
- combination: combination,
- add_qty: parseInt(qty),
- pricelist_id: this.pricelistId || false,
+ return ajax.jsonRpc(this._getUri('/product_configurator/get_combination_info'), 'call', {
+ 'product_template_id': productTemplateId,
+ 'product_id': this._getProductId($parent),
+ 'combination': combination,
+ 'add_qty': parseInt(qty),
+ 'pricelist_id': this.pricelistId || false,
+ 'parent_combination': parentCombination,
}).then(function (combinationData) {
self._onChangeCombination(ev, $parent, combinationData);
});
@@ -149,10 +157,13 @@ var ProductConfiguratorMixin = {
var $input = $link.closest('.input-group').find("input");
var min = parseFloat($input.data("min") || 0);
var max = parseFloat($input.data("max") || Infinity);
- var quantity = ($link.has(".fa-minus").length ? -1 : 1) + parseFloat($input.val() || 0, 10);
+ var previousQty = parseFloat($input.val() || 0, 10);
+ var quantity = ($link.has(".fa-minus").length ? -1 : 1) + previousQty;
var newQty = quantity > min ? (quantity < max ? quantity : max) : min;
- $input.val(newQty).trigger('change');
+ if (newQty !== previousQty) {
+ $input.val(newQty).trigger('change');
+ }
return false;
},
@@ -343,23 +354,6 @@ var ProductConfiguratorMixin = {
* @param {Array} combination the selected combination of product attribute values
*/
_checkExclusions: function ($parent, combination) {
-
- function areCombinationsEqual(c1, c2) {
- return c1.length === c2.length && _.every(c1, function (ptav) {
- return c2.indexOf(ptav) > -1;
- });
- }
-
- function isCombinationInList(c1, list) {
- return _.some(list, function (c2) {
- return areCombinationsEqual(c1, c2);
- });
- }
-
- function isPtavInCombination(ptav, combination) {
- return combination.indexOf(ptav) > -1;
- }
-
var self = this;
var combinationData = $parent
.find('ul[data-attribute_exclusions]')
@@ -367,17 +361,6 @@ var ProductConfiguratorMixin = {
$parent.find('option, input, label').removeClass('css_not_available');
- var disable = false;
-
- // compatibility 12.0
- var filteredCombination = combination;
- if (combinationData.no_variant_product_template_attribute_value_ids !== undefined) {
- var no_variants = combinationData.no_variant_product_template_attribute_value_ids;
- filteredCombination = _.filter(combination, function (ptav) {
- return !isPtavInCombination(ptav, no_variants);
- });
- }
-
// exclusion rules: array of ptav
// for each of them, contains array with the other ptav they exclude
if (combinationData.exclusions) {
@@ -386,11 +369,6 @@ var ProductConfiguratorMixin = {
if (combinationData.exclusions.hasOwnProperty(current_ptav)) {
// for each exclusion of the current attribute:
_.each(combinationData.exclusions[current_ptav], function (excluded_ptav) {
- // disable if it excludes any other attribute already in the combination
- if (isPtavInCombination(excluded_ptav, combination)) {
- disable = true;
- }
-
// disable the excluded input (even when not already selected)
// to give a visual feedback before click
self._disableInput($parent, excluded_ptav);
@@ -401,37 +379,11 @@ var ProductConfiguratorMixin = {
// parent exclusions (tell which attributes are excluded from parent)
_.each(combinationData.parent_exclusions, function (ptav) {
- if (isPtavInCombination(ptav, combination)) {
- disable = true;
- }
// disable the excluded input (even when not already selected)
// to give a visual feedback before click
self._disableInput($parent, ptav);
});
-
- // archived variants
- if (isCombinationInList(filteredCombination, combinationData.archived_combinations)) {
- disable = true;
- }
-
- // if not using dynamic attributes, exclude variants that are deleted
- if (filteredCombination.length && // compatibility 12.0 list view of variants
- combinationData.has_dynamic_attributes === false &&
- combinationData.existing_combinations !== undefined &&
- !isCombinationInList(filteredCombination, combinationData.existing_combinations)
- ) {
- disable = true;
- }
-
- this.isSelectedVariantAllowed = !disable;
- $parent.toggleClass('css_not_available', disable);
- $parent.find("#add_to_cart").toggleClass('disabled', disable);
- $parent
- .parents('.modal')
- .find('.o_sale_product_configurator_add')
- .toggleClass('disabled', disable);
},
-
/**
* Extracted to a method to be extendable by other modules
*
@@ -440,7 +392,6 @@ var ProductConfiguratorMixin = {
_getProductId: function ($parent) {
return parseInt($parent.find('.product_id').val());
},
-
/**
* Will disable the input/option that refers to the passed attributeValueId.
* This is used for showing the user that some combinations are not available.
@@ -455,7 +406,6 @@ var ProductConfiguratorMixin = {
$input.addClass('css_not_available');
$input.closest('label').addClass('css_not_available');
},
-
/**
* @see onChangeVariant
*
@@ -469,6 +419,15 @@ var ProductConfiguratorMixin = {
var $price = $parent.find(".oe_price:first .oe_currency_value");
var $default_price = $parent.find(".oe_default_price:first .oe_currency_value");
var $optional_price = $parent.find(".oe_optional:first .oe_currency_value");
+
+ var isCombinationPossible = this.isSelectedVariantAllowed;
+
+ if (combination.is_combination_possible !== undefined) {
+ isCombinationPossible = combination.is_combination_possible;
+ }
+
+ this._toggleDisable($parent, isCombinationPossible);
+
$price.html(self._priceToStr(combination.price));
$default_price.html(self._priceToStr(combination.list_price));
@@ -505,7 +464,8 @@ var ProductConfiguratorMixin = {
$parent.closest(rootComponentSelectors.join(', ')),
combination.product_id,
combination.product_template_id,
- combination.carousel
+ combination.carousel,
+ isCombinationPossible
);
}
@@ -525,7 +485,6 @@ var ProductConfiguratorMixin = {
.first()
.html(combination.price);
- this.computePriceTotal();
this.handleCustomValues($(ev.target));
},
@@ -546,7 +505,46 @@ var ProductConfiguratorMixin = {
formatted[0] = utils.insert_thousand_seps(formatted[0]);
return formatted.join(l10n.decimal_point);
},
-
+ /**
+ * Returns a throttled `_getCombinationInfo` with a leading and a trailing
+ * call, which is memoized per `uniqueId`, and for which previous results
+ * are dropped.
+ *
+ * The uniqueId is needed because on the configurator modal there might be
+ * multiple elements triggering the rpc at the same time, and we need each
+ * individual product rpc to be executed, but only once per individual
+ * product.
+ *
+ * The leading execution is to keep good reactivity on the first call, for
+ * a better user experience. The trailing is because ultimately only the
+ * information about the last selected combination is useful. All
+ * intermediary rpc can be ignored and are therefore best not done at all.
+ *
+ * The DropMisordered is to make sure slower rpc are ignored if the result
+ * of a newer rpc has already been received.
+ *
+ * @private
+ * @param {string} uniqueId
+ * @returns {function}
+ */
+ _throttledGetCombinationInfo: _.memoize(function (uniqueId) {
+ var dropMisordered = new concurrency.DropMisordered();
+ var _getCombinationInfo = _.throttle(this._getCombinationInfo.bind(this), 500);
+ return function (ev) {
+ return dropMisordered.add(_getCombinationInfo(ev));
+ };
+ }),
+ /**
+ * Toggles the disabled class depending on the $parent element
+ * and the possibility of the current combination.
+ *
+ * @private
+ * @param {$.Element} $parent
+ * @param {boolean} isCombinationPossible
+ */
+ _toggleDisable: function ($parent, isCombinationPossible) {
+ $parent.toggleClass('css_not_available', !isCombinationPossible);
+ },
/**
* Updates the product image.
* This will use the productId if available or will fallback to the productTemplateId.
diff --git a/addons/sale/static/src/js/product_configurator_modal.js b/addons/sale/static/src/js/product_configurator_modal.js
index 7cd61d6baec..0d6908a807b 100644
--- a/addons/sale/static/src/js/product_configurator_modal.js
+++ b/addons/sale/static/src/js/product_configurator_modal.js
@@ -12,20 +12,12 @@ var optionalProductsMap = {};
var OptionalProductsModal = Dialog.extend(ServicesMixin, ProductConfiguratorMixin, {
events: _.extend({}, Dialog.prototype.events, ProductConfiguratorMixin.events, {
'click a.js_add, a.js_remove': '_onAddOrRemoveOption',
- 'change .in_cart.main_product input.js_quantity': '_onChangeQuantity'
+ 'click button.js_add_cart_json': 'onClickAddCartJSON',
+ 'change .in_cart.main_product input.js_quantity': '_onChangeQuantity',
}),
/**
* Initializes the optional products modal
*
- * If the "isWebsite" param is true, will also disable the following events:
- * - change [data-attribute_exclusions]
- * - click button.js_add_cart_json
- *
- * This has to be done because those events are already registered at the "website_sale"
- * component level.
- * This modal is part of the form that has these events registered and we
- * want to avoid duplicates.
- *
* @override
* @param {$.Element} parent The parent container
* @param {Object} params
@@ -67,10 +59,6 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, ProductConfiguratorMixi
this.pricelistId = params.pricelistId;
this.isWebsite = params.isWebsite;
this.dialogClass = 'oe_optional_products_modal' + (params.isWebsite ? ' oe_website_sale' : '');
-
- if (this.isWebsite) {
- delete this.events['click button.js_add_cart_json'];
- }
this._productImageField = 'image_medium';
},
/**
@@ -156,6 +144,25 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, ProductConfiguratorMixi
// Public
//--------------------------------------------------------------------------
+ /**
+ * Computes and updates the total price, useful when a product is added or
+ * when the quantity is changed.
+ * TODO awa: add a container context to avoid global selectors ?
+ */
+ computePriceTotal: function () {
+ if ($('.js_price_total').length) {
+ var price = 0;
+ $('.js_product.in_cart').each(function () {
+ var quantity = parseInt($('input[name="add_qty"]').first().val());
+ price += parseFloat($(this).find('.js_raw_price').html()) * quantity;
+ });
+
+ $('.js_price_total .oe_currency_value').html(
+ this._priceToStr(parseFloat(price))
+ );
+ }
+ ProductConfiguratorMixin.computePriceTotal.apply(this, arguments);
+ },
/**
* Returns the list of selected products.
* The root product is added on top of the list.
@@ -450,6 +457,8 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, ProductConfiguratorMixi
.text(combination.display_name);
ProductConfiguratorMixin._onChangeCombination.apply(this, arguments);
+
+ this.computePriceTotal();
},
/**
* When the quantity of the root product is updated, we need to update
diff --git a/addons/sale/static/src/js/product_configurator_renderer.js b/addons/sale/static/src/js/product_configurator_renderer.js
index 899656c8065..d2cebf8794e 100644
--- a/addons/sale/static/src/js/product_configurator_renderer.js
+++ b/addons/sale/static/src/js/product_configurator_renderer.js
@@ -5,6 +5,10 @@ var FormRenderer = require('web.FormRenderer');
var ProductConfiguratorMixin = require('sale.ProductConfiguratorMixin');
var ProductConfiguratorFormRenderer = FormRenderer.extend(ProductConfiguratorMixin ,{
+
+ events: _.extend({}, FormRenderer.prototype.events, ProductConfiguratorMixin.events, {
+ 'click button.js_add_cart_json': 'onClickAddCartJSON',
+ }),
/**
* @override
*/
@@ -44,7 +48,22 @@ var ProductConfiguratorFormRenderer = FormRenderer.extend(ProductConfiguratorMix
$configuratorHtml.appendTo($configuratorContainer);
this.triggerVariantChange($configuratorContainer);
- }
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Toggles the add button depending on the possibility of the current
+ * combination.
+ *
+ * @override
+ */
+ _toggleDisable: function ($parent, isCombinationPossible) {
+ ProductConfiguratorMixin._toggleDisable.apply(this, arguments);
+ $parent.parents('.modal').find('.o_sale_product_configurator_add').toggleClass('disabled', !isCombinationPossible);
+ },
});
return ProductConfiguratorFormRenderer;
diff --git a/addons/sale/views/res_config_settings_views.xml b/addons/sale/views/res_config_settings_views.xml
index bc1366e5d5c..f7c2fd973fa 100644
--- a/addons/sale/views/res_config_settings_views.xml
+++ b/addons/sale/views/res_config_settings_views.xml
@@ -50,7 +50,7 @@
- Send a product-specific email once the invoice is paid
+ Send a product-specific email once the invoice is validated
diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml
index d6313076e6c..62fd461d080 100644
--- a/addons/sale/views/sale_views.xml
+++ b/addons/sale/views/sale_views.xml
@@ -491,6 +491,7 @@
+
diff --git a/addons/sale_management/static/tests/product_configurator_test_ui.js b/addons/sale_management/static/tests/product_configurator_test_ui.js
index 03f5a0a8bbc..0c98a46c166 100644
--- a/addons/sale_management/static/tests/product_configurator_test_ui.js
+++ b/addons/sale_management/static/tests/product_configurator_test_ui.js
@@ -30,16 +30,7 @@ tour.register('sale_product_configurator_tour', {
run: 'click'
}, {
trigger: '.configurator_container span:contains("Steel")',
- run: function () {
- $('input.product_id').change(function () {
- var request_count = 0;
- if ($('.o_sale_product_configurator_add').attr('request_count')) {
- request_count = parseInt($('.o_sale_product_configurator_add').attr('request_count'));
- }
- request_count++;
- $('.o_sale_product_configurator_add').attr('request_count', request_count);
- });
- }
+ run: function () {},
}, {
trigger: '.configurator_container span:contains("Aluminium")',
run: 'click'
@@ -49,10 +40,9 @@ tour.register('sale_product_configurator_tour', {
}, {
trigger: 'input[data-value_name="Black"]'
}, {
- trigger: 'input[data-value_name="White"]'
+ trigger: '.o_sale_product_configurator_add.disabled'
}, {
- trigger: '.o_sale_product_configurator_add[request_count="3"]',
- run: function (){} // used to sync with "get_combination_info" completion
+ trigger: 'input[data-value_name="White"]'
}, {
trigger: '.o_sale_product_configurator_add:not(.disabled)'
}, {
diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py
index ec068f21a5b..a01080ef7b1 100644
--- a/addons/sale_stock/models/sale_order.py
+++ b/addons/sale_stock/models/sale_order.py
@@ -96,6 +96,21 @@ class SaleOrder(models.Model):
if self.warehouse_id.company_id:
self.company_id = self.warehouse_id.company_id.id
+ @api.onchange('partner_shipping_id')
+ def _onchange_partner_shipping_id(self):
+ res = {}
+ pickings = self.picking_ids.filtered(
+ lambda p: p.state not in ['done', 'cancel'] and p.partner_id != self.partner_shipping_id
+ )
+ if pickings:
+ res['warning'] = {
+ 'title': _('Warning!'),
+ 'message': _(
+ 'Do not forget to change the partner on the following delivery orders: %s'
+ ) % (','.join(pickings.mapped('name')))
+ }
+ return res
+
@api.multi
def action_view_delivery(self):
'''
diff --git a/addons/sms/models/mail_thread.py b/addons/sms/models/mail_thread.py
index 1fb62a72de4..a439487e4e9 100644
--- a/addons/sms/models/mail_thread.py
+++ b/addons/sms/models/mail_thread.py
@@ -39,8 +39,8 @@ class MailThread(models.AbstractModel):
if not partners:
partners = self._get_default_sms_recipients()
- # Collect numbers, we will consider the message to be sent if at least one number can be found
- numbers = list(set([i.mobile for i in partners if i.mobile]))
+ # Collect numbers, we will consider the message to be sent if at least one number can be found
+ numbers = list(set([i.mobile for i in partners if i.mobile]))
if numbers:
try:
diff --git a/addons/sms/wizard/send_sms.py b/addons/sms/wizard/send_sms.py
index 786a5e2af76..986fd1c9239 100644
--- a/addons/sms/wizard/send_sms.py
+++ b/addons/sms/wizard/send_sms.py
@@ -45,8 +45,8 @@ class SendSMS(models.TransientModel):
return number
if not phonenumbers.is_possible_number(phone_nbr) or not phonenumbers.is_valid_number(phone_nbr):
return number
- phone_fmt = phonenumbers.PhoneNumberFormat.INTERNATIONAL
- return phonenumbers.format_number(phone_nbr, phone_fmt).replace(' ', '')
+ phone_fmt = phonenumbers.PhoneNumberFormat.E164
+ return phonenumbers.format_number(phone_nbr, phone_fmt)
else:
return number
@@ -62,12 +62,11 @@ class SendSMS(models.TransientModel):
@api.model
def default_get(self, fields):
result = super(SendSMS, self).default_get(fields)
-
active_model = self.env.context.get('active_model')
- model = self.env[active_model]
- records = self._get_records(model)
- if getattr(records, '_get_default_sms_recipients') and not self.env.context.get('default_recipients'):
+ if not self.env.context.get('default_recipients') and active_model and hasattr(self.env[active_model], '_get_default_sms_recipients'):
+ model = self.env[active_model]
+ records = self._get_records(model)
partners = records._get_default_sms_recipients()
phone_numbers = []
no_phone_partners = []
@@ -84,12 +83,12 @@ class SendSMS(models.TransientModel):
return result
def action_send_sms(self):
- numbers = self.recipients.split(',')
+ numbers = [number.strip() for number in self.recipients.split(',') if number.strip()]
active_model = self.env.context.get('active_model')
- model = self.env[active_model]
- records = self._get_records(model)
- if getattr(records, 'message_post_send_sms'):
+ if active_model and hasattr(self.env[active_model], 'message_post_send_sms'):
+ model = self.env[active_model]
+ records = self._get_records(model)
records.message_post_send_sms(self.message, numbers=numbers)
else:
self.env['sms.api']._send_sms(numbers, self.message)
diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py
index 099d04d982e..6e31e58f3d0 100644
--- a/addons/stock/models/product.py
+++ b/addons/stock/models/product.py
@@ -487,6 +487,10 @@ class ProductTemplate(models.Model):
def _is_cost_method_standard(self):
return True
+ @api.depends(
+ 'product_variant_ids',
+ 'product_variant_ids.stock_quant_ids',
+ )
def _compute_quantities(self):
res = self._compute_quantities_dict()
for template in self:
diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index 1224a5f63b5..975dee846bd 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -468,6 +468,7 @@ class StockMove(models.Model):
else:
view = self.env.ref('stock.view_stock_move_nosuggest_operations')
+ picking_type_id = self.picking_type_id or self.picking_id.picking_type_id
return {
'name': _('Detailed Operations'),
'type': 'ir.actions.act_window',
@@ -480,8 +481,8 @@ class StockMove(models.Model):
'res_id': self.id,
'context': dict(
self.env.context,
- show_lots_m2o=self.has_tracking != 'none' and (self.picking_type_id.use_existing_lots or self.state == 'done' or self.origin_returned_move_id.id), # able to create lots, whatever the value of ` use_create_lots`.
- show_lots_text=self.has_tracking != 'none' and self.picking_type_id.use_create_lots and not self.picking_type_id.use_existing_lots and self.state != 'done' and not self.origin_returned_move_id.id,
+ show_lots_m2o=self.has_tracking != 'none' and (picking_type_id.use_existing_lots or self.state == 'done' or self.origin_returned_move_id.id), # able to create lots, whatever the value of ` use_create_lots`.
+ show_lots_text=self.has_tracking != 'none' and picking_type_id.use_create_lots and not picking_type_id.use_existing_lots and self.state != 'done' and not self.origin_returned_move_id.id,
show_source_location=self.location_id.child_ids and self.picking_type_id.code != 'incoming',
show_destination_location=self.location_dest_id.child_ids and self.picking_type_id.code != 'outgoing',
show_package=not self.location_id.usage == 'supplier',
diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py
index cf1dd8e031e..c80f9efe6e6 100644
--- a/addons/stock/models/stock_warehouse.py
+++ b/addons/stock/models/stock_warehouse.py
@@ -892,7 +892,6 @@ class Warehouse(models.Model):
@api.returns('self')
def _get_all_routes(self):
- # TDE FIXME: check overrides
routes = self.mapped('route_ids') | self.mapped('mto_pull_id').mapped('route_id')
routes |= self.env["stock.location.route"].search([('supplied_wh_id', 'in', self.ids)])
return routes
diff --git a/addons/stock/wizard/stock_picking_return_views.xml b/addons/stock/wizard/stock_picking_return_views.xml
index 3bbba44a176..37b8af9a1e9 100644
--- a/addons/stock/wizard/stock_picking_return_views.xml
+++ b/addons/stock/wizard/stock_picking_return_views.xml
@@ -27,7 +27,7 @@
-
+
'+
''+
- '
'+
+ '
Foo '+
'
'+
'this is foo'+
'
'+
@@ -79,9 +79,11 @@ QUnit.module('base_settings_tests', {
assert.hasAttrValue(form.$('.selected'), 'data-key',"crm","crm setting selected");
assert.isVisible(form.$(".settings .app_settings_block"), "project settings show");
form.$('.searchInput').val('b').trigger('keyup');
- assert.strictEqual($('.highlighter').html(),"B","b word hilited");
+ assert.strictEqual(form.$('.highlighter').html(), "B", "b word highlighted");
form.$('.searchInput').val('bx').trigger('keyup');
assert.isVisible(form.$('.notFound'), "record not found message shown");
+ form.$('.searchInput').val('f').trigger('keyup');
+ assert.strictEqual(form.$('span.o_form_label .highlighter').html(), "F", "F word highlighted");
form.destroy();
});
diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py
index f49aea62b1f..562949215f8 100644
--- a/odoo/addons/test_new_api/tests/test_new_fields.py
+++ b/odoo/addons/test_new_api/tests/test_new_fields.py
@@ -251,11 +251,12 @@ class TestFields(common.TransactionCase):
self.assertEqual(c.display_name, 'B / C')
self.assertEqual(d.display_name, 'B / C / D')
- b.name = 'X'
+ # rename several records to trigger several recomputations at once
+ (d + c + b).write({'name': 'X'})
self.assertEqual(a.display_name, 'A')
self.assertEqual(b.display_name, 'X')
- self.assertEqual(c.display_name, 'X / C')
- self.assertEqual(d.display_name, 'X / C / D')
+ self.assertEqual(c.display_name, 'X / X')
+ self.assertEqual(d.display_name, 'X / X / X')
# delete b; both c and d are deleted in cascade; c should also be marked
# to recompute, but recomputation should not fail...
diff --git a/odoo/fields.py b/odoo/fields.py
index 0a80e52ead3..5529288d177 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -116,8 +116,8 @@ class Field(MetaField('DummyField', (object,), {})):
:param required: whether the value of the field is required (boolean, by
default ``False``)
- :param index: whether the field is indexed in database (boolean, by
- default ``False``)
+ :param index: whether the field is indexed in database. Note: no effect
+ on non-stored and virtual fields. (boolean, by default ``False``)
:param default: the default value for the field; this is either a static
value, or a function taking a recordset and returning a value; use
@@ -640,6 +640,7 @@ class Field(MetaField('DummyField', (object,), {})):
_related_help = property(attrgetter('help'))
_related_groups = property(attrgetter('groups'))
_related_group_operator = property(attrgetter('group_operator'))
+ _related_context_dependent = property(attrgetter('context_dependent'))
@property
def base_field(self):
@@ -1084,6 +1085,8 @@ class Field(MetaField('DummyField', (object,), {})):
recs = record._recompute_check(self)
if recs:
# recompute the value (only in cache)
+ if self.recursive:
+ recs = record
self.compute_value(recs)
# HACK: if result is in the wrong cache, copy values
if recs.env != env:
diff --git a/odoo/models.py b/odoo/models.py
index 0a0d8147549..2d29bc72834 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -4017,8 +4017,7 @@ Fields:
# The quotes surrounding `ir_translation` are important as well.
unique_translation_subselect = """
(SELECT res_id, value FROM "ir_translation"
- WHERE name=%s AND lang=%s AND value!=''
- ORDER BY res_id, id DESC)
+ WHERE type='model' AND name=%s AND lang=%s AND value!='')
"""
alias, alias_statement = query.add_join(
(table_alias, unique_translation_subselect, 'id', 'res_id', field),
@@ -5445,6 +5444,19 @@ Fields:
if subnames else record[name]
)
+ def has_changed(self, name):
+ """ Return whether a field on record has changed. """
+ record = self['
']
+ subnames = self[''][name]
+ if not subnames:
+ return self[name] != record[name]
+ else:
+ return len(self[name]) != len(record[name]) or any(
+ line_snapshot.has_changed(subname)
+ for line_snapshot in self[name]
+ for subname in subnames
+ )
+
def diff(self, other):
""" Return the values in ``self`` that differ from ``other``.
Requires record cache invalidation for correct output!
@@ -5522,25 +5534,24 @@ Fields:
result = {'warnings': OrderedSet()}
- # process names in order (or the keys of values if no name given)
- while todo:
- name = todo.pop(0)
- if name in done:
- continue
- done.add(name)
-
- with env.do_in_onchange():
+ # process names in order
+ with env.do_in_onchange():
+ while todo:
# apply field-specific onchange methods
- if field_onchange.get(name):
- record._onchange_eval(name, field_onchange[name], result)
+ for name in todo:
+ if field_onchange.get(name):
+ record._onchange_eval(name, field_onchange[name], result)
+ done.add(name)
- # make a snapshot (this forces evaluation of computed fields)
- snapshot1 = Snapshot(record, nametree)
+ # determine which fields to process for the next pass
+ todo = [
+ name
+ for name in nametree
+ if name not in done and snapshot0.has_changed(name)
+ ]
- # determine which fields have been modified
- for name in nametree:
- if snapshot1[name] != snapshot0[name]:
- todo.append(name)
+ # make the snapshot with the final values of record
+ snapshot1 = Snapshot(record, nametree)
# determine values that have changed by comparing snapshots
self.invalidate_cache()
diff --git a/odoo/tests/common.py b/odoo/tests/common.py
index 941f7b10384..1c4f87d75ac 100644
--- a/odoo/tests/common.py
+++ b/odoo/tests/common.py
@@ -1409,7 +1409,8 @@ class Form(object):
if not any(spec[f] for f in fields):
return
- result = self._model.onchange(self._onchange_values(), fields, spec)
+ record = self._model.browse(self._values.get('id'))
+ result = record.onchange(self._onchange_values(), fields, spec)
if result.get('warning'):
_logger.getChild('onchange').warn("%(title)s %(message)s" % result.get('warning'))
values = result.get('value', {})
diff --git a/odoo/tools/image.py b/odoo/tools/image.py
index 137f84d1ab4..67d677160a7 100644
--- a/odoo/tools/image.py
+++ b/odoo/tools/image.py
@@ -152,14 +152,9 @@ def image_save_for_web(image, fp=None, format=None):
opt = dict(format=image.format or format)
if image.format == 'PNG':
opt.update(optimize=True)
- alpha = False
- if image.mode in ('RGBA', 'LA') or (image.mode == 'P' and 'transparency' in image.info):
- alpha = image.convert('RGBA').split()[-1]
if image.mode != 'P':
# Floyd Steinberg dithering by default
image = image.convert('RGBA').convert('P', palette=Image.WEB, colors=256)
- if alpha:
- image.putalpha(alpha)
elif image.format == 'JPEG':
opt.update(optimize=True, quality=80)
if fp:
diff --git a/odoo/tools/translate.py b/odoo/tools/translate.py
index 16ce6af0bfd..c00f967857a 100644
--- a/odoo/tools/translate.py
+++ b/odoo/tools/translate.py
@@ -964,8 +964,11 @@ def trans_generate(lang, modules, cr):
extra_comments = extra_comments or []
if not module: return
src_file = open(fabsolutepath, 'rb')
+ options = {}
+ if extract_method == 'python':
+ options['encoding'] = 'UTF-8'
try:
- for extracted in extract.extract(extract_method, src_file, keywords=extract_keywords):
+ for extracted in extract.extract(extract_method, src_file, keywords=extract_keywords, options=options):
# Babel 0.9.6 yields lineno, message, comments
# Babel 1.3 yields lineno, message, comments, context
lineno, message, comments = extracted[:3]