[MERGE] forward port branch 10.0 up to 00e169c05a
This commit is contained in:
@@ -483,9 +483,9 @@ class AccountInvoice(models.Model):
|
||||
else:
|
||||
account_id = pay_account.id
|
||||
payment_term_id = p.property_supplier_payment_term_id.id
|
||||
addr = self.partner_id.address_get(['delivery'])
|
||||
fiscal_position = self.env['account.fiscal.position'].get_fiscal_position(self.partner_id.id, delivery_id=addr['delivery'])
|
||||
|
||||
delivery_partner_id = self.get_delivery_partner_id()
|
||||
fiscal_position = self.env['account.fiscal.position'].get_fiscal_position(self.partner_id.id, delivery_id=delivery_partner_id)
|
||||
|
||||
# If partner has no warning, check its company
|
||||
if p.invoice_warn == 'no-message' and p.parent_id:
|
||||
@@ -513,6 +513,10 @@ class AccountInvoice(models.Model):
|
||||
return {'domain': {'partner_bank_id': [('id', 'in', bank_ids.ids)]}}
|
||||
return {}
|
||||
|
||||
@api.multi
|
||||
def get_delivery_partner_id(self):
|
||||
self.ensure_one()
|
||||
return self.partner_id.address_get(['delivery'])['delivery']
|
||||
|
||||
@api.onchange('journal_id')
|
||||
def _onchange_journal_id(self):
|
||||
@@ -1030,7 +1034,7 @@ class AccountInvoice(models.Model):
|
||||
|
||||
values['invoice_line_ids'] = self._refund_cleanup_lines(invoice.invoice_line_ids)
|
||||
|
||||
tax_lines = filter(lambda l: l.manual, invoice.tax_line_ids)
|
||||
tax_lines = invoice.tax_line_ids
|
||||
values['tax_line_ids'] = self._refund_cleanup_lines(tax_lines)
|
||||
|
||||
if journal_id:
|
||||
|
||||
@@ -304,11 +304,13 @@ class account_journal(models.Model):
|
||||
'default_type': invoice_type,
|
||||
'type': invoice_type
|
||||
})
|
||||
|
||||
[action] = self.env.ref('account.%s' % action_name).read()
|
||||
action['context'] = ctx
|
||||
action['domain'] = self._context.get('use_domain', [])
|
||||
action['views'] = False
|
||||
action['view_id'] = False
|
||||
if action_name in ['action_bank_statement_tree', 'action_view_bank_statement_tree']:
|
||||
action['views'] = False
|
||||
action['view_id'] = False
|
||||
return action
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -8,6 +8,7 @@ from odoo.exceptions import RedirectWarning, UserError, ValidationError
|
||||
from odoo.tools.misc import formatLang
|
||||
from odoo.tools import float_is_zero, float_compare
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
import odoo.addons.decimal_precision as dp
|
||||
from lxml import etree
|
||||
|
||||
#----------------------------------------------------------
|
||||
@@ -370,7 +371,7 @@ class AccountMoveLine(models.Model):
|
||||
self.counterpart = ",".join(counterpart)
|
||||
|
||||
name = fields.Char(required=True, string="Label")
|
||||
quantity = fields.Float(digits=(16, 2),
|
||||
quantity = fields.Float(digits=dp.get_precision('Product Unit of Measure'),
|
||||
help="The optional quantity expressed by this line, eg: number of product sold. The quantity is not a legal requirement but is very useful for some reports.")
|
||||
product_uom_id = fields.Many2one('product.uom', string='Unit of Measure')
|
||||
product_id = fields.Many2one('product.product', string='Product')
|
||||
@@ -763,7 +764,7 @@ class AccountMoveLine(models.Model):
|
||||
"""
|
||||
for datum in data:
|
||||
if len(datum['mv_line_ids']) >= 1 or len(datum['mv_line_ids']) + len(datum['new_mv_line_dicts']) >= 2:
|
||||
self.process_reconciliation(datum['mv_line_ids'], datum['new_mv_line_dicts'])
|
||||
self.env['account.move.line'].browse(datum['mv_line_ids']).process_reconciliation(datum['new_mv_line_dicts'])
|
||||
|
||||
if datum['type'] == 'partner':
|
||||
partners = self.env['res.partner'].browse(datum['id'])
|
||||
@@ -1426,9 +1427,9 @@ class AccountPartialReconcile(models.Model):
|
||||
for rec in self:
|
||||
if not rec.company_id.currency_exchange_journal_id:
|
||||
raise UserError(_("You should configure the 'Exchange Rate Journal' in the accounting settings, to manage automatically the booking of accounting entries related to differences between exchange rates."))
|
||||
if not self.company_id.income_currency_exchange_account_id.id:
|
||||
if not rec.company_id.income_currency_exchange_account_id.id:
|
||||
raise UserError(_("You should configure the 'Gain Exchange Rate Account' in the accounting settings, to manage automatically the booking of accounting entries related to differences between exchange rates."))
|
||||
if not self.company_id.expense_currency_exchange_account_id.id:
|
||||
if not rec.company_id.expense_currency_exchange_account_id.id:
|
||||
raise UserError(_("You should configure the 'Loss Exchange Rate Account' in the accounting settings, to manage automatically the booking of accounting entries related to differences between exchange rates."))
|
||||
move_vals = {'journal_id': rec.company_id.currency_exchange_journal_id.id}
|
||||
|
||||
@@ -1488,6 +1489,8 @@ class AccountPartialReconcile(models.Model):
|
||||
#amount is the current cash_basis amount minus the one before the reconciliation
|
||||
amount = line.balance * percentage_after - line.balance * percentage_before
|
||||
rounded_amt = line.company_id.currency_id.round(amount)
|
||||
if float_is_zero(rounded_amt, precision_rounding=line.company_id.currency_id.rounding):
|
||||
continue
|
||||
if line.tax_line_id and line.tax_line_id.use_cash_basis:
|
||||
if not newly_created_move:
|
||||
newly_created_move = self._create_tax_basis_move()
|
||||
|
||||
@@ -11,6 +11,7 @@ class ReportPartnerLedger(models.AbstractModel):
|
||||
|
||||
def _lines(self, data, partner):
|
||||
full_account = []
|
||||
currency = self.env['res.currency']
|
||||
query_get_data = self.env['account.move.line'].with_context(data['form'].get('used_context', {}))._query_get()
|
||||
reconcile_clause = "" if data['form']['reconciled'] else ' AND "account_move_line".reconciled = false '
|
||||
params = [partner.id, tuple(data['computed']['move_state']), tuple(data['computed']['account_ids'])] + query_get_data[2]
|
||||
@@ -40,6 +41,7 @@ class ReportPartnerLedger(models.AbstractModel):
|
||||
)
|
||||
sum += r['debit'] - r['credit']
|
||||
r['progress'] = sum
|
||||
r['currency_id'] = currency.browse(r.get('currency_id'))
|
||||
full_account.append(r)
|
||||
return full_account
|
||||
|
||||
|
||||
@@ -301,10 +301,10 @@
|
||||
<td class="oe_form_group_cell o_form_group_cell" colspan="1" width="99%">
|
||||
<span class="oe_form_field o_form_field oe_form_field_radio o_form_field_radio oe_vertical o_vertical oe_form_required o_form_required">
|
||||
<span>
|
||||
<t t-if="display_mode_customers" t-call="show_mode_selector_option"><t t-set="value">customers</t><t t-set="title">Customers</t></t>
|
||||
<t t-if="display_mode_suppliers" t-call="show_mode_selector_option"><t t-set="value">suppliers</t><t t-set="title">Vendors</t></t>
|
||||
<t t-if="display_mode_others" t-call="show_mode_selector_option"><t t-set="value">others</t><t t-set="title">OtherAccounts</t></t>
|
||||
<t t-call="show_mode_selector_option"><t t-set="value">all</t><t t-set="title">All</t></t>
|
||||
<t t-if="display_mode_customers" t-call="show_mode_selector_option"><t t-set="value" t-value="'customers'"/><t t-set="title">Customers</t></t>
|
||||
<t t-if="display_mode_suppliers" t-call="show_mode_selector_option"><t t-set="value" t-value="'suppliers'"/><t t-set="title">Vendors</t></t>
|
||||
<t t-if="display_mode_others" t-call="show_mode_selector_option"><t t-set="value" t-value="'others'"/><t t-set="title">OtherAccounts</t></t>
|
||||
<t t-call="show_mode_selector_option"><t t-set="value" t-value="'all'"/><t t-set="title">All</t></t>
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
@@ -638,7 +638,7 @@
|
||||
<field name="act_window_id" ref="action_bank_statement_tree"/>
|
||||
</record>
|
||||
<record model="ir.actions.act_window.view" id="action_bank_statement_form_bank">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_bank_statement_form"/>
|
||||
<field name="act_window_id" ref="action_bank_statement_tree"/>
|
||||
|
||||
@@ -126,18 +126,23 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" t-if="len(o.tax_line_ids) > 1">
|
||||
<!-- DO NOT REMOVE THIS TABLE. MANDATORY IN SOME COUNTRIES -->
|
||||
<div class="row" t-if="len(o.tax_line_ids) > 0">
|
||||
<div class="col-xs-6">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tax</th>
|
||||
<th class="text-right">Base</th>
|
||||
<th class="text-right">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="o.tax_line_ids" t-as="t">
|
||||
<td><span t-field="t.tax_id.description"/></td>
|
||||
<td class="text-right">
|
||||
<span t-field="t.base" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="t.amount" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
</td>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<th>Debit</th>
|
||||
<th>Credit</th>
|
||||
<th>Balance</th>
|
||||
<th t-if="amount_currency">Currency</th>
|
||||
<th t-if="data['form']['amount_currency']">Currency</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -77,8 +77,10 @@
|
||||
<td class="text-right">
|
||||
<span t-esc="line['progress']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
<td t-if="amount_currency">
|
||||
<span t-esc="line['amount_currency']" t-options="{'widget': 'monetary', 'display_currency': line['currency_id']}"/>
|
||||
<td class="text-right" t-if="data['form']['amount_currency']">
|
||||
<t t-if="line['currency_id']">
|
||||
<span t-esc="line['amount_currency']" t-options="{'widget': 'monetary', 'display_currency': line['currency_id']}"/>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -58,7 +58,6 @@ class AccountInvoiceRefund(models.TransientModel):
|
||||
date = form.date or False
|
||||
description = form.description or inv.name
|
||||
refund = inv.refund(form.date_invoice, date, description, inv.journal_id.id)
|
||||
refund.compute_taxes()
|
||||
|
||||
created_inv.append(refund.id)
|
||||
if mode in ('cancel', 'modify'):
|
||||
|
||||
@@ -471,15 +471,14 @@ class AccountAssetDepreciationLine(models.Model):
|
||||
@api.multi
|
||||
def create_move(self, post_move=True):
|
||||
created_moves = self.env['account.move']
|
||||
prec = self.env['decimal.precision'].precision_get('Account')
|
||||
for line in self:
|
||||
category_id = line.asset_id.category_id
|
||||
depreciation_date = self.env.context.get('depreciation_date') or line.depreciation_date or fields.Date.context_today(self)
|
||||
company_currency = line.asset_id.company_id.currency_id
|
||||
current_currency = line.asset_id.currency_id
|
||||
amount = current_currency.compute(line.amount, company_currency)
|
||||
sign = (category_id.journal_id.type == 'purchase' or category_id.journal_id.type == 'sale' and 1) or -1
|
||||
asset_name = line.asset_id.name + ' (%s/%s)' % (line.sequence, len(line.asset_id.depreciation_line_ids))
|
||||
prec = self.env['decimal.precision'].precision_get('Account')
|
||||
move_line_1 = {
|
||||
'name': asset_name,
|
||||
'account_id': category_id.account_depreciation_id.id,
|
||||
@@ -489,7 +488,7 @@ class AccountAssetDepreciationLine(models.Model):
|
||||
'partner_id': line.asset_id.partner_id.id,
|
||||
'analytic_account_id': category_id.account_analytic_id.id if category_id.type == 'sale' else False,
|
||||
'currency_id': company_currency != current_currency and current_currency.id or False,
|
||||
'amount_currency': company_currency != current_currency and - sign * line.amount or 0.0,
|
||||
'amount_currency': company_currency != current_currency and - 1.0 * line.amount or 0.0,
|
||||
}
|
||||
move_line_2 = {
|
||||
'name': asset_name,
|
||||
@@ -500,7 +499,7 @@ class AccountAssetDepreciationLine(models.Model):
|
||||
'partner_id': line.asset_id.partner_id.id,
|
||||
'analytic_account_id': category_id.account_analytic_id.id if category_id.type == 'purchase' else False,
|
||||
'currency_id': company_currency != current_currency and current_currency.id or False,
|
||||
'amount_currency': company_currency != current_currency and sign * line.amount or 0.0,
|
||||
'amount_currency': company_currency != current_currency and line.amount or 0.0,
|
||||
}
|
||||
move_vals = {
|
||||
'ref': line.asset_id.code,
|
||||
|
||||
@@ -61,6 +61,9 @@ widgets.SidebarFilter.include({
|
||||
},
|
||||
initialize_m2o: function() {
|
||||
this.dfm = new form_common.DefaultFieldManager(this);
|
||||
if (!this.view.useContacts) {
|
||||
return;
|
||||
}
|
||||
this.dfm.extend_field_desc({
|
||||
partner_id: {
|
||||
relation: "res.partner",
|
||||
|
||||
@@ -70,7 +70,7 @@ class EventEvent(models.Model):
|
||||
'interval_unit': 'days',
|
||||
'interval_type': 'before_event',
|
||||
'template_id': self.env.ref('event.event_reminder')
|
||||
})]
|
||||
})] if self.user_has_groups('event.group_email_scheduling') else []
|
||||
|
||||
# Seats and computation
|
||||
seats_max = fields.Integer(
|
||||
|
||||
@@ -49,7 +49,7 @@ class EventMailScheduler(models.Model):
|
||||
if self.interval_type in ['before_event', 'after_event']:
|
||||
self.done = self.mail_sent
|
||||
else:
|
||||
self.done = len(self.mail_registration_ids) == len(self.event_id.registration_ids) and all(filter(lambda line: line.mail_sent, self.mail_registration_ids))
|
||||
self.done = len(self.mail_registration_ids) == len(self.event_id.registration_ids) and all(line.mail_sent for line in self.mail_registration_ids)
|
||||
|
||||
@api.one
|
||||
@api.depends('event_id.state', 'event_id.date_begin', 'interval_type', 'interval_unit', 'interval_nbr')
|
||||
|
||||
@@ -434,8 +434,8 @@
|
||||
<field name="interval_type"/>
|
||||
<field name="scheduled_date"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Registration Mails">
|
||||
<notebook groups="base.group_no_one">
|
||||
<page string="Registration Mails" >
|
||||
<field name="mail_registration_ids">
|
||||
<tree string="Registration mail" editable="bottom">
|
||||
<field name="registration_id"/>
|
||||
|
||||
@@ -213,11 +213,11 @@ class FleetVehicle(models.Model):
|
||||
LogContract = self.env['fleet.vehicle.log.contract']
|
||||
Cost = self.env['fleet.vehicle.cost']
|
||||
for record in self:
|
||||
record.odometer_count = Odometer.search_count([('vehicle_id', '=', self.id)])
|
||||
record.fuel_logs_count = LogFuel.search_count([('vehicle_id', '=', self.id)])
|
||||
record.service_count = LogService.search_count([('vehicle_id', '=', self.id)])
|
||||
record.contract_count = LogContract.search_count([('vehicle_id', '=', self.id)])
|
||||
record.cost_count = Cost.search_count([('vehicle_id', '=', self.id), ('parent_id', '=', False)])
|
||||
record.odometer_count = Odometer.search_count([('vehicle_id', '=', record.id)])
|
||||
record.fuel_logs_count = LogFuel.search_count([('vehicle_id', '=', record.id)])
|
||||
record.service_count = LogService.search_count([('vehicle_id', '=', record.id)])
|
||||
record.contract_count = LogContract.search_count([('vehicle_id', '=', record.id)])
|
||||
record.cost_count = Cost.search_count([('vehicle_id', '=', record.id), ('parent_id', '=', False)])
|
||||
|
||||
@api.depends('log_contracts')
|
||||
def _compute_contract_reminder(self):
|
||||
|
||||
@@ -246,33 +246,40 @@ class HrExpense(models.Model):
|
||||
move.post()
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def _prepare_move_line_value(self):
|
||||
self.ensure_one()
|
||||
if self.account_id:
|
||||
account = self.account_id
|
||||
elif self.product_id:
|
||||
account = self.product_id.product_tmpl_id._get_product_accounts()['expense']
|
||||
if not account:
|
||||
raise UserError(
|
||||
_("No Expense account found for the product %s (or for its category), please configure one.") % (self.product_id.name))
|
||||
else:
|
||||
account = self.env['ir.property'].with_context(force_company=self.company_id.id).get('property_account_expense_categ_id', 'product.category')
|
||||
if not account:
|
||||
raise UserError(
|
||||
_('Please configure Default Expense account for Product expense: `property_account_expense_categ_id`.'))
|
||||
aml_name = self.employee_id.name + ': ' + self.name.split('\n')[0][:64]
|
||||
move_line = {
|
||||
'type': 'src',
|
||||
'name': aml_name,
|
||||
'price_unit': self.unit_amount,
|
||||
'quantity': self.quantity,
|
||||
'price': self.total_amount,
|
||||
'account_id': account.id,
|
||||
'product_id': self.product_id.id,
|
||||
'uom_id': self.product_uom_id.id,
|
||||
'analytic_account_id': self.analytic_account_id.id,
|
||||
}
|
||||
return move_line
|
||||
|
||||
@api.multi
|
||||
def _move_line_get(self):
|
||||
account_move = []
|
||||
for expense in self:
|
||||
if expense.account_id:
|
||||
account = expense.account_id
|
||||
elif expense.product_id:
|
||||
account = expense.product_id.product_tmpl_id._get_product_accounts()['expense']
|
||||
if not account:
|
||||
raise UserError(_("No Expense account found for the product %s (or for its category), please configure one.") % (expense.product_id.name))
|
||||
else:
|
||||
account = self.env['ir.property'].with_context(force_company=expense.company_id.id).get('property_account_expense_categ_id', 'product.category')
|
||||
if not account:
|
||||
raise UserError(_('Please configure Default Expense account for Product expense: `property_account_expense_categ_id`.'))
|
||||
|
||||
aml_name = expense.employee_id.name + ': ' + expense.name.split('\n')[0][:64]
|
||||
move_line = {
|
||||
'type': 'src',
|
||||
'name': aml_name,
|
||||
'price_unit': expense.unit_amount,
|
||||
'quantity': expense.quantity,
|
||||
'price': expense.total_amount,
|
||||
'account_id': account.id,
|
||||
'product_id': expense.product_id.id,
|
||||
'uom_id': expense.product_uom_id.id,
|
||||
'analytic_account_id': expense.analytic_account_id.id,
|
||||
}
|
||||
move_line = expense._prepare_move_line_value()
|
||||
account_move.append(move_line)
|
||||
|
||||
# Calculate tax lines and adjust base line
|
||||
|
||||
@@ -10,7 +10,7 @@ class HrTimesheetSheet(models.Model):
|
||||
_inherit = "hr_timesheet_sheet.sheet"
|
||||
|
||||
attendances_ids = fields.One2many('hr.attendance', 'sheet_id', 'Attendances')
|
||||
total_attendance = fields.Integer(string='Total Attendance', compute='_compute_total')
|
||||
total_attendance = fields.Float(string='Total Attendance', compute='_compute_total')
|
||||
total_timesheet = fields.Float(string='Total Timesheet', compute="_compute_total")
|
||||
total_difference = fields.Float(string='Difference', compute="_compute_total")
|
||||
period_ids = fields.One2many('hr_timesheet_sheet.sheet.day', 'sheet_id', string='Period', readonly=True)
|
||||
|
||||
@@ -779,7 +779,7 @@
|
||||
<record id="chart2500" model="account.account.template">
|
||||
<field name="code">2500</field>
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_receivable"/>
|
||||
<field name="user_type_id" ref="account.data_account_type_non_current_assets"/>
|
||||
<field name="name">Vorsteuer</field>
|
||||
<field name="chart_template_id" ref="austria_chart_template"/>
|
||||
</record>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Indian - Schedule VI Accounting',
|
||||
'version': '2.0',
|
||||
'description': """
|
||||
Indian Accounting: Chart of Account.
|
||||
====================================
|
||||
|
||||
Indian accounting chart and localization.
|
||||
|
||||
Schedule VI chart of account for indian accounting.
|
||||
|
||||
Note: The Schedule VI has been revised by MCA and is applicable for all Balance Sheet made after
|
||||
31st March, 2011. The Format has done away with earlier two options of format of Balance
|
||||
Sheet, now only Vertical format has been permitted Which is Supported By OpenERP.
|
||||
""",
|
||||
'category': 'Localization',
|
||||
'depends': [
|
||||
'account',
|
||||
],
|
||||
'demo': [],
|
||||
'data': [
|
||||
'l10n_in_schedule6_chart.xml',
|
||||
'l10n_in_schedule6_tax_template.xml',
|
||||
'account_chart_template.yml',
|
||||
],
|
||||
'auto_install': False,
|
||||
'installable': True,
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
-
|
||||
!python {model: account.chart.template, id: indian_chart_template_schedule6}: |
|
||||
self[0].try_loading_for_current_company()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,842 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Account Tags -->
|
||||
<record id="st_tag_in_01" model="account.account.tag">
|
||||
<field name="name">Tax to Pay</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_02" model="account.account.tag">
|
||||
<field name="name">C Forms</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_03" model="account.account.tag">
|
||||
<field name="name">C Forms Tax Collected on Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_04" model="account.account.tag">
|
||||
<field name="name">C Forms Tax Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_05" model="account.account.tag">
|
||||
<field name="name">Excise Duty</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_06" model="account.account.tag">
|
||||
<field name="name">Excise Duty 12.36%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_07" model="account.account.tag">
|
||||
<field name="name">Excise Duty Collected on Sales</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_08" model="account.account.tag">
|
||||
<field name="name">Education Cess 2% Excise Duty Collected on Sales</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_09" model="account.account.tag">
|
||||
<field name="name">Higher Education Cess 1% Excise Duty Collected on Sales</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_10" model="account.account.tag">
|
||||
<field name="name">Excise Duty Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_11" model="account.account.tag">
|
||||
<field name="name">Education Cess 2% Excise Duty Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_12" model="account.account.tag">
|
||||
<field name="name">Higher Education Cess 1% Excise Duty Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_13" model="account.account.tag">
|
||||
<field name="name">Sales Tax</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_14" model="account.account.tag">
|
||||
<field name="name">Sales Tax 4%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_15" model="account.account.tag">
|
||||
<field name="name">Sales Tax 4% Collected on Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_16" model="account.account.tag">
|
||||
<field name="name">Sales Tax 4% Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_17" model="account.account.tag">
|
||||
<field name="name">Sales Tax 12%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_18" model="account.account.tag">
|
||||
<field name="name">Sales Tax 12% Collected on Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_19" model="account.account.tag">
|
||||
<field name="name">Sales Tax 12% Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_20" model="account.account.tag">
|
||||
<field name="name">Sales Tax 15%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_21" model="account.account.tag">
|
||||
<field name="name">Sales Tax 15% Collected on Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_22" model="account.account.tag">
|
||||
<field name="name">Sales Tax 15% Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_23" model="account.account.tag">
|
||||
<field name="name">Service Tax</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_24" model="account.account.tag">
|
||||
<field name="name">Service Tax 12.36%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_25" model="account.account.tag">
|
||||
<field name="name">Service Tax Collected on Sales of Services</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_26" model="account.account.tag">
|
||||
<field name="name">Education Cess 2% Service Tax Collected on Sales of Services</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_27" model="account.account.tag">
|
||||
<field name="name">Higher Education Cess 1% Service Tax Collected on Sales of Services</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_28" model="account.account.tag">
|
||||
<field name="name">Service Tax Paid on Purchase of Services</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_29" model="account.account.tag">
|
||||
<field name="name">Education Cess 2% Service Tax Paid on Purchase of Services</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_30" model="account.account.tag">
|
||||
<field name="name">Higher Education Cess 1% Service Tax Paid on Purchase of Services</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_31" model="account.account.tag">
|
||||
<field name="name">VAT</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_32" model="account.account.tag">
|
||||
<field name="name">VAT 4%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_33" model="account.account.tag">
|
||||
<field name="name">VAT 4% Collected on Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_34" model="account.account.tag">
|
||||
<field name="name">VAT 4% Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_35" model="account.account.tag">
|
||||
<field name="name">VAT 8%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_36" model="account.account.tag">
|
||||
<field name="name">VAT 8% Collected on Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_37" model="account.account.tag">
|
||||
<field name="name">VAT 8% Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_38" model="account.account.tag">
|
||||
<field name="name">VAT 10%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_39" model="account.account.tag">
|
||||
<field name="name">VAT 10% Collected on Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_40" model="account.account.tag">
|
||||
<field name="name">VAT 10% Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_41" model="account.account.tag">
|
||||
<field name="name">VAT 12.5%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_42" model="account.account.tag">
|
||||
<field name="name">VAT 12.5% Collected on Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_43" model="account.account.tag">
|
||||
<field name="name">VAT 12.5% Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_44" model="account.account.tag">
|
||||
<field name="name">Additional VAT 1%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_45" model="account.account.tag">
|
||||
<field name="name">Additional VAT 1% Collected on Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_46" model="account.account.tag">
|
||||
<field name="name">Additional VAT 1% Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_47" model="account.account.tag">
|
||||
<field name="name">Additional VAT 2.5%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_48" model="account.account.tag">
|
||||
<field name="name">Additional VAT 2.5% Collected on Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_49" model="account.account.tag">
|
||||
<field name="name">Additional VAT 2.5% Paid on Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_50" model="account.account.tag">
|
||||
<field name="name">Tax Bases</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_51" model="account.account.tag">
|
||||
<field name="name">Base C Forms</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_52" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_53" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_54" model="account.account.tag">
|
||||
<field name="name">Base Excise Duty</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_55" model="account.account.tag">
|
||||
<field name="name">Base Excise Duty 12.36%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_56" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_57" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_58" model="account.account.tag">
|
||||
<field name="name">Base Sales Tax</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_59" model="account.account.tag">
|
||||
<field name="name">Base Sales Tax 4%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_60" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_61" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_62" model="account.account.tag">
|
||||
<field name="name">Base Sales Tax 12%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_63" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_64" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_65" model="account.account.tag">
|
||||
<field name="name">Base Sales Tax 15%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_66" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_67" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_68" model="account.account.tag">
|
||||
<field name="name">Base Service Tax</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_69" model="account.account.tag">
|
||||
<field name="name">Base Service Tax 12.36%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_70" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_71" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_72" model="account.account.tag">
|
||||
<field name="name">Base VAT</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_73" model="account.account.tag">
|
||||
<field name="name">Base VAT 4%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_74" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_75" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_76" model="account.account.tag">
|
||||
<field name="name">Base VAT 8%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_77" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_78" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_79" model="account.account.tag">
|
||||
<field name="name">Base VAT 10%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_80" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_81" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_82" model="account.account.tag">
|
||||
<field name="name">Base VAT 12.5%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_83" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_84" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_85" model="account.account.tag">
|
||||
<field name="name">Base Additional VAT 1%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_86" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_87" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_88" model="account.account.tag">
|
||||
<field name="name">Base Additional VAT 2.5%</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_89" model="account.account.tag">
|
||||
<field name="name">Base of Sale</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
|
||||
<record id="st_tag_in_90" model="account.account.tag">
|
||||
<field name="name">Base of Purchase</field>
|
||||
<field name="applicability">taxes</field>
|
||||
</record>
|
||||
<!-- Account Tax Templates -->
|
||||
|
||||
<record id="input_tax_15" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Sales Tax @ 15%</field>
|
||||
<field name="description">(schedule6)Input Sales Tax @ 15%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">15</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c114214"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('st_tag_in_21'), ref('st_tag_in_66')])]"/>
|
||||
</record>
|
||||
<record id="input_tax_12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Sales Tax @ 12%</field>
|
||||
<field name="description">(schedule6)Input Sales Tax @ 12%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">12</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c114214"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('st_tag_in_18'), ref('st_tag_in_63')])]"/>
|
||||
</record>
|
||||
<record id="input_tax_4" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Sales Tax @ 4%</field>
|
||||
<field name="description">(schedule6)Input Sales Tax @ 4%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">4</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c114214"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('st_tag_in_15'), ref('st_tag_in_60')])]"/>
|
||||
</record>
|
||||
<record id="add_input_vat_1" model="account.tax.template">
|
||||
<field name="name">(schedule6)Additional Input VAT @ 1%</field>
|
||||
<field name="description">(schedule6)Additional Input VAT @ 1%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">1</field>
|
||||
<field name="sequence">15</field>
|
||||
<field name="account_id" ref="c114215"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_86'), ref('st_tag_in_45')])]"/>
|
||||
</record>
|
||||
<record id="input_vat_12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input VAT @ 12.5%</field>
|
||||
<field name="description">(schedule6)Input VAT @ 12.5%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">12.5</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c114215"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_83'), ref('st_tag_in_42')])]"/>
|
||||
</record>
|
||||
<record id="add_input_vat_2" model="account.tax.template">
|
||||
<field name="name">(schedule6)Additional Input VAT @ 2.5%</field>
|
||||
<field name="description">(schedule6)Additional Input VAT @ 2.5%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">2.5</field>
|
||||
<field name="sequence">15</field>
|
||||
<field name="account_id" ref="c114215"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_89'), ref('st_tag_in_48')])]"/>
|
||||
</record>
|
||||
<record id="sale_vat_8" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input VAT @ 8%</field>
|
||||
<field name="description">(schedule6)Input VAT @ 8%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">8</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c114215"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_77'), ref('st_tag_in_36')])]"/>
|
||||
</record>
|
||||
<record id="sale_vat_10" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input VAT @ 10%</field>
|
||||
<field name="description">(schedule6)Input VAT @ 10%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">10</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c114215"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_39'), ref('st_tag_in_80')])]"/>
|
||||
</record>
|
||||
<record id="sale_tax_12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Tax 12%</field>
|
||||
<field name="description">(schedule6)Input Tax 12%</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">12</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="account_id" ref="c1142111"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_07')])]"/>
|
||||
</record>
|
||||
<record id="sale_edu_cess_Excise_Duty_2" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Education Cess @ 2% On Excise Duty</field>
|
||||
<field name="description">(schedule6)Input Education Cess @ 2% On Excise Duty</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">0.24</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="account_id" ref="c1142112"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_08')])]"/>
|
||||
</record>
|
||||
<record id="sale_hedu_cess_Excise_Duty_1" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Higher Education Cess @ 1% On Excise Duty</field>
|
||||
<field name="description">(schedule6)Input Higher Education Cess @ 1% On Excise Duty</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">0.1224</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="account_id" ref="c1142113"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_09')])]"/>
|
||||
</record>
|
||||
<record id="sale_Excise_Duty_12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Excise Duty @ 12.36%</field>
|
||||
<field name="description">(schedule6)Input Excise Duty @ 12.36%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">group</field>
|
||||
<field name="amount">0</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="account_id" ref="c1142111"/>
|
||||
<field name="children_tax_ids" eval="[(6, 0, [ref('sale_tax_12'),ref('sale_edu_cess_Excise_Duty_2'),ref('sale_hedu_cess_Excise_Duty_1')])]"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_56')])]"/>
|
||||
</record>
|
||||
<record id="sale_service_tax12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Service Tax 12%</field>
|
||||
<field name="description">(schedule6)Input Service Tax 12%</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">12</field>
|
||||
<field name="sequence">13</field>
|
||||
<field name="account_id" ref="c1142121"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('st_tag_in_25')])]"/>
|
||||
</record>
|
||||
<record id="sale_edu_cess_service_tax_2" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Education Cess @ 2% On Service Tax</field>
|
||||
<field name="description">(schedule6)Input Education Cess @ 2% On Service Tax</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">0.24</field>
|
||||
<field name="sequence">14</field>
|
||||
<field name="account_id" ref="c1142122"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_26')])]"/>
|
||||
</record>
|
||||
<record id="sale_high_edu_service_tax_1" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Higher Education Cess @ 1% On Service Tax</field>
|
||||
<field name="description">(schedule6)Input Higher Education Cess @ 1% On Service Tax</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">0.1224</field>
|
||||
<field name="sequence">15</field>
|
||||
<field name="account_id" ref="c1142123"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_27')])]"/>
|
||||
</record>
|
||||
<record id="sale_service_tax_12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input Service Tax @ 12.36%</field>
|
||||
<field name="description">(schedule6)Input Service Tax @ 12.36%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">group</field>
|
||||
<field name="amount">12</field>
|
||||
<field name="include_base_amount">True</field>
|
||||
<field name="sequence">8</field>
|
||||
<field name="account_id" ref="c1142121"/>
|
||||
<field name="children_tax_ids" eval="[(6, 0, [ref('sale_service_tax12'),ref('sale_edu_cess_service_tax_2'),ref('sale_high_edu_service_tax_1')])]"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_70')])]"/>
|
||||
</record>
|
||||
<record id="input_vat_4" model="account.tax.template">
|
||||
<field name="name">(schedule6)Input VAT @ 4%</field>
|
||||
<field name="description">(schedule6)Input VAT @ 4%</field>
|
||||
<field name="type_tax_use">sale</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">4</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c114215"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('st_tag_in_33'), ref('st_tag_in_74')])]"/>
|
||||
</record>
|
||||
|
||||
<!--output purchase tax-->
|
||||
<record id="output_tax_15" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Purchase Tax @ 15%</field>
|
||||
<field name="description">(schedule6)Output Purchase Tax @ 15%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">15</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c121610"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('st_tag_in_22'), ref('st_tag_in_67')])]"/>
|
||||
</record>
|
||||
<record id="output_tax_12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Purchase Tax @ 12%</field>
|
||||
<field name="description">(schedule6)Output Purchase Tax @ 12%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">12</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c121610"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [ref('st_tag_in_19'), ref('st_tag_in_64')])]"/>
|
||||
</record>
|
||||
<record id="output_tax_4" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Purchase Tax @ 4%</field>
|
||||
<field name="description">(schedule6)Output Purchase Tax @ 4%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">4</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c121610"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_61'), ref('st_tag_in_16')])]"/>
|
||||
</record>
|
||||
<record id="add_output_vat_1" model="account.tax.template">
|
||||
<field name="name">(schedule6)Additional Output VAT @ 1%</field>
|
||||
<field name="description">(schedule6)Additional Output VAT @ 1%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">1</field>
|
||||
<field name="sequence">15</field>
|
||||
<field name="account_id" ref="c121611"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_87'), ref('st_tag_in_46')])]"/>
|
||||
</record>
|
||||
<record id="output_vat_12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output VAT @ 12.5%</field>
|
||||
<field name="description">(schedule6)Output VAT @ 12.5%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">12.5</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c121611"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_43'), ref('st_tag_in_84')])]"/>
|
||||
</record>
|
||||
<record id="add_output_vat_2" model="account.tax.template">
|
||||
<field name="name">(schedule6)Additional Output VAT @ 2.5%</field>
|
||||
<field name="description">(schedule6)Additional Output VAT @ 2.5%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">2.5</field>
|
||||
<field name="sequence">15</field>
|
||||
<field name="account_id" ref="c121611"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_90'), ref('st_tag_in_49')])]"/>
|
||||
</record>
|
||||
<record id="purchase_vat_8" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output VAT @ 8%</field>
|
||||
<field name="description">(schedule6)Output VAT @ 8%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">8</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c121611"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_78'), ref('st_tag_in_37')])]"/>
|
||||
</record>
|
||||
<record id="purchase_vat_10" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output VAT @ 10%</field>
|
||||
<field name="description">(schedule6)Output VAT @ 10%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">10</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c121611"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_81'), ref('st_tag_in_40')])]"/>
|
||||
</record>
|
||||
<record id="purchase_edu_cess_12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Tax 12%</field>
|
||||
<field name="description">(schedule6)Output Tax 12%</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">12</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="account_id" ref="c1216121"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_10')])]"/>
|
||||
</record>
|
||||
<record id="purchase_edu_cess_Excise_Duty_2" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Education Cess @ 2% Excise Duty</field>
|
||||
<field name="description">(schedule6)Output Education Cess @ 2% Excise Duty</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">0.24</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="account_id" ref="c1216122"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_11')])]"/>
|
||||
</record>
|
||||
<record id="purchase_hedu_cess_Excise_Duty_1" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Higher Education Cess @ 1% Excise Duty</field>
|
||||
<field name="description">(schedule6)Output Higher Education Cess @ 1% Excise Duty</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">0.1224</field>
|
||||
<field name="sequence">4</field>
|
||||
<field name="account_id" ref="c1216123"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_12')])]"/>
|
||||
</record>
|
||||
<record id="purchase_Excise_Duty_12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Excise Duty @ 12.36%</field>
|
||||
<field name="description">(schedule6)Output Excise Duty @ 12.36%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="amount_type">group</field>
|
||||
<field name="amount">12</field>
|
||||
<field name="include_base_amount">True</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="account_id" ref="c1216121"/>
|
||||
<field name="children_tax_ids" eval="[(6, 0, [ref('purchase_edu_cess_12'),ref('purchase_edu_cess_Excise_Duty_2'),ref('purchase_hedu_cess_Excise_Duty_1')])]"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_57')])]"/>
|
||||
</record>
|
||||
<record id="purchase_edu_service_tax12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Service Tax 12%</field>
|
||||
<field name="description">(schedule6)Output Service Tax 12%</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">12</field>
|
||||
<field name="sequence">9</field>
|
||||
<field name="account_id" ref="c1216131"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_28')])]"/>
|
||||
</record>
|
||||
<record id="purchase_edu_cess_service_tax_2" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Education Cess @ 2% On Service Tax</field>
|
||||
<field name="description">(schedule6)Output Education Cess @ 2% On Service Tax</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">0.24</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c1216132"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_29')])]"/>
|
||||
</record>
|
||||
<record id="purchase_high_edu_service_tax_1" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Higher Education Cess @ 1% On Service Tax</field>
|
||||
<field name="description">(schedule6)Output Higher Education Cess @ 1% On Service Tax</field>
|
||||
<field name="type_tax_use">none</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">0.1224</field>
|
||||
<field name="sequence">11</field>
|
||||
<field name="account_id" ref="c1216133"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_30')])]"/>
|
||||
</record>
|
||||
<record id="st_purchase_service_tax_12" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output Service Tax @ 12.36%</field>
|
||||
<field name="description">(schedule6)Output Service Tax @ 12.36%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="include_base_amount">True</field>
|
||||
<field name="amount_type">group</field>
|
||||
<field name="amount">12</field>
|
||||
<field name="sequence">8</field>
|
||||
<field name="account_id" ref="c1216131"/>
|
||||
<field name="children_tax_ids" eval="[(6, 0, [ref('purchase_edu_service_tax12'),ref('purchase_edu_cess_service_tax_2'),ref('purchase_high_edu_service_tax_1')])]"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_71')])]"/>
|
||||
</record>
|
||||
<record id="output_vat_4" model="account.tax.template">
|
||||
<field name="name">(schedule6)Output VAT @ 4%</field>
|
||||
<field name="description">(schedule6)Output VAT @ 4%</field>
|
||||
<field name="type_tax_use">purchase</field>
|
||||
<field name="amount_type">percent</field>
|
||||
<field name="amount">4</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="account_id" ref="c121611"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('st_tag_in_34'), ref('st_tag_in_75')])]"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
@@ -1690,9 +1690,10 @@ class MailThread(models.AbstractModel):
|
||||
return self._message_post_process_attachments(attachments, attachment_ids, {'model': attach_model, 'res_id': attach_res_id})
|
||||
|
||||
def _message_post_process_attachments(self, attachments, attachment_ids, message_data):
|
||||
IrAttachment, parameter_attachments = self.env['ir.attachment'], self.env['ir.attachment']
|
||||
IrAttachment = self.env['ir.attachment']
|
||||
m2m_attachment_ids = []
|
||||
cid_mapping = {}
|
||||
fname_mapping = {}
|
||||
if attachment_ids:
|
||||
filtered_attachment_ids = self.env['ir.attachment'].sudo().search([
|
||||
('res_model', '=', 'mail.compose.message'),
|
||||
@@ -1708,9 +1709,7 @@ class MailThread(models.AbstractModel):
|
||||
name, content = attachment
|
||||
elif len(attachment) == 3:
|
||||
name, content, info = attachment
|
||||
if info and info.get('cid'):
|
||||
cid = info['cid']
|
||||
cid_mapping[cid] = name
|
||||
cid = info and info.get('cid')
|
||||
else:
|
||||
continue
|
||||
if isinstance(content, unicode):
|
||||
@@ -1718,13 +1717,16 @@ class MailThread(models.AbstractModel):
|
||||
data_attach = {
|
||||
'name': name,
|
||||
'datas': base64.b64encode(str(content)),
|
||||
'datas_fname': cid or name,
|
||||
'datas_fname': name,
|
||||
'description': name,
|
||||
'res_model': message_data['model'],
|
||||
'res_id': message_data['res_id'],
|
||||
}
|
||||
parameter_attachments |= IrAttachment.create(data_attach)
|
||||
m2m_attachment_ids += [(4, attach.id) for attach in parameter_attachments]
|
||||
new_attachment = IrAttachment.create(data_attach)
|
||||
m2m_attachment_ids.append((4, new_attachment.id))
|
||||
if cid:
|
||||
cid_mapping[cid] = new_attachment
|
||||
fname_mapping[name] = new_attachment
|
||||
|
||||
if cid_mapping and message_data.get('body'):
|
||||
root = lxml.html.fromstring(tools.ustr(message_data['body']))
|
||||
@@ -1732,12 +1734,11 @@ class MailThread(models.AbstractModel):
|
||||
for node in root.iter('img'):
|
||||
if node.get('src', '').startswith('cid:'):
|
||||
cid = node.get('src').split('cid:')[1]
|
||||
fname = cid_mapping.get(cid, node.get('data-filename', ''))
|
||||
attachment = parameter_attachments.filtered(lambda attachment: attachment.datas_fname == cid)
|
||||
attachment = cid_mapping.get(cid)
|
||||
if not attachment:
|
||||
attachment = parameter_attachments.filtered(lambda attachment: attachment.datas_fname == fname)
|
||||
attachment = fname_mapping.get(node.get('data-filename'), '')
|
||||
if attachment:
|
||||
node.set('src', '/web/image/%s' % attachment.ids[0])
|
||||
node.set('src', '/web/image/%s' % attachment.id)
|
||||
postprocessed = True
|
||||
if postprocessed:
|
||||
body = lxml.html.tostring(root, pretty_print=False, encoding='UTF-8')
|
||||
|
||||
@@ -312,7 +312,7 @@
|
||||
<field name="owner_user_id" string="Owner"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="maintenance_team_id"/>
|
||||
<field name="maintenance_team_id" attrs="{'required': [('period', '!=', 0)]}"/>
|
||||
<field name="technician_user_id"/>
|
||||
<field name="assign_date" groups="base.group_no_one"/>
|
||||
<field name="scrap_date" groups="base.group_no_one"/>
|
||||
|
||||
@@ -7,8 +7,7 @@ import werkzeug.urls
|
||||
|
||||
from odoo import api, fields, models, tools
|
||||
|
||||
|
||||
URL_REGEX = r'(\bhref=[\'"]([^\'"]+)[\'"])'
|
||||
from openerp.addons.link_tracker.models.link_tracker import URL_REGEX
|
||||
|
||||
|
||||
class MailMail(models.Model):
|
||||
|
||||
@@ -44,7 +44,12 @@ class ProductProduct(models.Model):
|
||||
read_group_res = self.env['mrp.bom'].read_group([('product_id', 'in', self.ids)], ['product_id'], ['product_id'])
|
||||
mapped_data = dict([(data['product_id'][0], data['product_id_count']) for data in read_group_res])
|
||||
for product in self:
|
||||
product.bom_count = mapped_data.get(product.id, 0)
|
||||
if product.product_tmpl_id.product_variant_count == 1:
|
||||
bom_count = mapped_data.get(product.id, product.product_tmpl_id.bom_count)
|
||||
else:
|
||||
bom_count = mapped_data.get(product.id, 0)
|
||||
product.bom_count = bom_count
|
||||
|
||||
|
||||
def _compute_mo_count(self):
|
||||
read_group_res = self.env['mrp.production'].read_group([('product_id', 'in', self.ids)], ['product_id'], ['product_id'])
|
||||
|
||||
@@ -33,7 +33,7 @@ class StockMoveLots(models.Model):
|
||||
def _check_lot_id(self):
|
||||
if self.move_id.product_id.tracking == 'serial':
|
||||
lots = set([])
|
||||
for move_lot in self.move_id.move_lot_ids.filtered(lambda r: not r.lot_produced_id):
|
||||
for move_lot in self.move_id.active_move_lot_ids.filtered(lambda r: not r.lot_produced_id and r.lot_id):
|
||||
if move_lot.lot_id in lots:
|
||||
raise exceptions.UserError(_('You cannot use the same serial number in two different lines.'))
|
||||
if float_compare(move_lot.quantity_done, 1.0, precision_rounding=move_lot.product_id.uom_id.rounding) == 1:
|
||||
|
||||
@@ -490,7 +490,10 @@ class RepairLine(models.Model):
|
||||
if partner and self.product_id:
|
||||
self.tax_id = partner.property_account_position_id.map_tax(self.product_id.taxes_id, self.product_id, partner).ids
|
||||
if self.product_id:
|
||||
self.name = self.product_id.display_name
|
||||
if partner:
|
||||
self.name = self.product_id.with_context(lang=partner.lang).display_name
|
||||
else:
|
||||
self.name = self.product_id.display_name
|
||||
self.product_uom = self.product_id.uom_id.id
|
||||
|
||||
warning = False
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
string="Quotation / Order"
|
||||
id="action_report_mrp_repair_order"
|
||||
model="mrp.repair"
|
||||
name="mrp_repair.report_mrprepairorder"
|
||||
name="mrp_repair.report_mrprepairorder2"
|
||||
file="mrp_repair.report_mrprepairorder"
|
||||
report_type="qweb-pdf"
|
||||
/>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="report_mrprepairorder">
|
||||
<t t-call="report.html_container">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-call="report.external_layout">
|
||||
<t t-set="o" t-value="o.with_context({'lang': o.partner_id.lang})" />
|
||||
<div class="page">
|
||||
<div class="oe_structure"/>
|
||||
|
||||
@@ -153,6 +153,13 @@
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="report_mrprepairorder2">
|
||||
<t t-call="report.html_container">
|
||||
<t t-foreach="docs" t-as="doc">
|
||||
<t t-call="mrp_repair.report_mrprepairorder" t-lang="doc.partner_id.lang"/>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</data>
|
||||
|
||||
@@ -150,7 +150,7 @@ class AuthorizeAPI():
|
||||
etree.SubElement(root, "transId").text = transaction_id
|
||||
customer = etree.SubElement(root, "customer")
|
||||
etree.SubElement(customer, "merchantCustomerId").text = 'ODOO-%s-%s' % (partner.id, uuid4().hex[:8])
|
||||
etree.SubElement(customer, "email").text = partner.email
|
||||
etree.SubElement(customer, "email").text = partner.email or ''
|
||||
response = self._authorize_request(root)
|
||||
res = dict()
|
||||
res['profile_id'] = response.find('customerProfileId').text
|
||||
|
||||
@@ -32,16 +32,22 @@ class PaypalController(http.Controller):
|
||||
|
||||
:param response str: text response, structured in the following way:
|
||||
STATUS\nkey1=value1\nkey2=value2...\n
|
||||
or STATUS\nError message...\n
|
||||
:rtype tuple(str, dict)
|
||||
:return: tuple containing the STATUS str and the key/value pairs
|
||||
parsed as a dict
|
||||
"""
|
||||
lines = filter(None, response.split('\n'))
|
||||
status = lines.pop(0)
|
||||
pdt_post = dict(line.split('=', 1) for line in lines)
|
||||
# html unescape
|
||||
for post in pdt_post:
|
||||
pdt_post[post] = urllib.unquote_plus(pdt_post[post]).decode('utf8')
|
||||
|
||||
pdt_post = {}
|
||||
for line in lines:
|
||||
split = line.split('=', 1)
|
||||
if len(split) == 2:
|
||||
pdt_post[split[0]] = urllib.unquote_plus(split[1]).decode('utf8')
|
||||
else:
|
||||
_logger.warning('Paypal: error processing pdt response: %s', line)
|
||||
|
||||
return status, pdt_post
|
||||
|
||||
def paypal_validate_data(self, **post):
|
||||
|
||||
@@ -14,7 +14,10 @@ class PosController(http.Controller):
|
||||
@http.route('/pos/web', type='http', auth='user')
|
||||
def pos_web(self, debug=False, **k):
|
||||
# if user not logged in, log him in
|
||||
pos_sessions = request.env['pos.session'].search([('state', '=', 'opened'), ('user_id', '=', request.session.uid)])
|
||||
pos_sessions = request.env['pos.session'].search([
|
||||
('state', '=', 'opened'),
|
||||
('user_id', '=', request.session.uid),
|
||||
('name', 'not like', '(RESCUE FOR')])
|
||||
if not pos_sessions:
|
||||
return werkzeug.utils.redirect('/web#action=point_of_sale.action_client_pos_menu')
|
||||
pos_sessions.login()
|
||||
|
||||
@@ -31,7 +31,7 @@ class PosConfig(models.Model):
|
||||
|
||||
def _default_sale_journal(self):
|
||||
journal = self.env.ref('point_of_sale.pos_sale_journal', raise_if_not_found=False)
|
||||
if journal and journal.company_id == self.env.user.company_id:
|
||||
if journal and journal.sudo().company_id == self.env.user.company_id:
|
||||
return journal
|
||||
return self._default_invoice_journal()
|
||||
|
||||
@@ -137,9 +137,12 @@ class PosConfig(models.Model):
|
||||
@api.depends('session_ids')
|
||||
def _compute_current_session(self):
|
||||
for pos_config in self:
|
||||
session = pos_config.session_ids.filtered(lambda r: r.user_id.id == self.env.uid and not r.state == 'closed')
|
||||
pos_config.current_session_id = session
|
||||
pos_config.current_session_state = session.state
|
||||
session = pos_config.session_ids.filtered(lambda r: r.user_id.id == self.env.uid and \
|
||||
not r.state == 'closed' and \
|
||||
'(RESCUE FOR' not in r.name)
|
||||
# sessions ordered by id desc
|
||||
pos_config.current_session_id = session and session[0].id or False
|
||||
pos_config.current_session_state = session and session[0].state or False
|
||||
|
||||
@api.depends('session_ids')
|
||||
def _compute_last_session(self):
|
||||
@@ -159,7 +162,8 @@ class PosConfig(models.Model):
|
||||
@api.depends('session_ids')
|
||||
def _compute_current_session_user(self):
|
||||
for pos_config in self:
|
||||
pos_config.pos_session_username = pos_config.session_ids.filtered(lambda s: s.state == 'opened').user_id.name
|
||||
session = pos_config.session_ids.filtered(lambda s: s.state == 'opened' and '(RESCUE FOR' not in s.name)
|
||||
pos_config.pos_session_username = session and session[0].user_id.name or False
|
||||
|
||||
@api.constrains('company_id', 'stock_location_id')
|
||||
def _check_company_location(self):
|
||||
@@ -207,7 +211,7 @@ class PosConfig(models.Model):
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
IrSequence = self.env['ir.sequence']
|
||||
IrSequence = self.env['ir.sequence'].sudo()
|
||||
val = {
|
||||
'name': _('POS Order %s') % values['name'],
|
||||
'padding': 4,
|
||||
|
||||
@@ -66,11 +66,18 @@ class PosOrder(models.Model):
|
||||
closed_session.id,
|
||||
order['name'],
|
||||
order['amount_total'])
|
||||
rescue_session = PosSession.search([
|
||||
('name', 'like', '(RESCUE FOR %(session)s)' % {'session': closed_session.name}),
|
||||
('state', 'not in', ('closed', 'closing_control')),
|
||||
], limit=1)
|
||||
if rescue_session:
|
||||
_logger.warning('reusing recovery session %s for saving order %s', rescue_session.name, order['name'])
|
||||
return rescue_session
|
||||
|
||||
_logger.warning('attempting to create recovery session for saving order %s', order['name'])
|
||||
new_session = PosSession.create({
|
||||
'config_id': closed_session.config_id.id,
|
||||
'name': _('(RESCUE FOR %(session)s)') % {'session': closed_session.name},
|
||||
'rescue': True, # avoid conflict with live sessions
|
||||
'name': '(RESCUE FOR %(session)s)' % {'session': closed_session.name},
|
||||
})
|
||||
# bypass opening_control (necessary when using cash control)
|
||||
new_session.action_pos_session_open()
|
||||
@@ -334,7 +341,12 @@ class PosOrder(models.Model):
|
||||
name = fields.Char(string='Order Ref', required=True, readonly=True, copy=False, default='/')
|
||||
company_id = fields.Many2one('res.company', string='Company', required=True, readonly=True, default=lambda self: self.env.user.company_id)
|
||||
date_order = fields.Datetime(string='Order Date', readonly=True, index=True, default=fields.Datetime.now)
|
||||
user_id = fields.Many2one('res.users', string='Salesman', help="Person who uses the cash register. It can be a reliever, a student or an interim employee.", default=lambda self: self.env.uid)
|
||||
user_id = fields.Many2one(
|
||||
comodel_name='res.users', string='Salesman',
|
||||
help="Person who uses the cash register. It can be a reliever, a student or an interim employee.",
|
||||
default=lambda self: self.env.uid,
|
||||
states={'done': [('readonly', True)], 'invoiced': [('readonly', True)]},
|
||||
)
|
||||
amount_tax = fields.Float(compute='_compute_amount_all', string='Taxes', digits=0)
|
||||
amount_total = fields.Float(compute='_compute_amount_all', string='Total', digits=0)
|
||||
amount_paid = fields.Float(compute='_compute_amount_all', string='Paid', states={'draft': [('readonly', False)]}, readonly=True, digits=0)
|
||||
@@ -359,12 +371,22 @@ class PosOrder(models.Model):
|
||||
account_move = fields.Many2one('account.move', string='Journal Entry', readonly=True, copy=False)
|
||||
picking_id = fields.Many2one('stock.picking', string='Picking', readonly=True, copy=False)
|
||||
picking_type_id = fields.Many2one('stock.picking.type', related='session_id.config_id.picking_type_id', string="Picking Type")
|
||||
location_id = fields.Many2one('stock.location', related='session_id.config_id.stock_location_id', string="Location", store=True)
|
||||
location_id = fields.Many2one(
|
||||
comodel_name='stock.location',
|
||||
related='session_id.config_id.stock_location_id',
|
||||
string="Location", store=True,
|
||||
readonly=True,
|
||||
)
|
||||
note = fields.Text(string='Internal Notes')
|
||||
nb_print = fields.Integer(string='Number of Print', readonly=True, copy=False, default=0)
|
||||
pos_reference = fields.Char(string='Receipt Ref', readonly=True, copy=False)
|
||||
sale_journal = fields.Many2one('account.journal', related='session_id.config_id.journal_id', string='Sales Journal', store=True, readonly=True)
|
||||
fiscal_position_id = fields.Many2one('account.fiscal.position', string='Fiscal Position', default=lambda self: self._default_session().config_id.default_fiscal_position_id)
|
||||
fiscal_position_id = fields.Many2one(
|
||||
comodel_name='account.fiscal.position', string='Fiscal Position',
|
||||
default=lambda self: self._default_session().config_id.default_fiscal_position_id,
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]},
|
||||
)
|
||||
|
||||
@api.depends('statement_ids', 'lines.price_subtotal_incl', 'lines.discount')
|
||||
def _compute_amount_all(self):
|
||||
@@ -540,6 +562,8 @@ class PosOrder(models.Model):
|
||||
Move = self.env['stock.move']
|
||||
StockWarehouse = self.env['stock.warehouse']
|
||||
for order in self:
|
||||
if not order.lines.filtered(lambda l: l.product_id.type in ['product', 'consu']):
|
||||
continue
|
||||
address = order.partner_id.address_get(['delivery']) or {}
|
||||
picking_type = order.picking_type_id
|
||||
return_pick_type = order.picking_type_id.return_picking_type_id or order.picking_type_id
|
||||
@@ -569,7 +593,7 @@ class PosOrder(models.Model):
|
||||
'location_id': location_id,
|
||||
'location_dest_id': destination_id,
|
||||
}
|
||||
pos_qty = any([x.qty >= 0 for x in order.lines])
|
||||
pos_qty = any([x.qty > 0 for x in order.lines])
|
||||
if pos_qty:
|
||||
order_picking = Picking.create(picking_vals.copy())
|
||||
order_picking.message_post(body=message)
|
||||
@@ -607,8 +631,8 @@ class PosOrder(models.Model):
|
||||
|
||||
# when the pos.config has no picking_type_id set only the moves will be created
|
||||
if moves and not return_picking and not order_picking:
|
||||
moves.action_confirm()
|
||||
moves.force_assign()
|
||||
moves.action_assign()
|
||||
moves.filtered(lambda m: m.state in ['confirmed', 'waiting']).force_assign()
|
||||
moves.filtered(lambda m: m.product_id.tracking == 'none').action_done()
|
||||
|
||||
return True
|
||||
@@ -616,7 +640,7 @@ class PosOrder(models.Model):
|
||||
def _force_picking_done(self, picking):
|
||||
"""Force picking in order to be set as done."""
|
||||
self.ensure_one()
|
||||
picking.action_confirm()
|
||||
picking.action_assign()
|
||||
picking.force_assign()
|
||||
self.set_pack_operation_lot(picking)
|
||||
if not any([(x.product_id.tracking != 'none') for x in picking.pack_operation_ids]):
|
||||
|
||||
@@ -128,12 +128,20 @@ class PosSession(models.Model):
|
||||
@api.constrains('user_id', 'state')
|
||||
def _check_unicity(self):
|
||||
# open if there is no session in 'opening_control', 'opened', 'closing_control' for one user
|
||||
if self.search_count([('state', 'not in', ('closed', 'closing_control')), ('user_id', '=', self.user_id.id)]) > 1:
|
||||
if self.search_count([
|
||||
('state', 'not in', ('closed', 'closing_control')),
|
||||
('user_id', '=', self.user_id.id),
|
||||
('name', 'not like', 'RESCUE FOR'),
|
||||
]) > 1:
|
||||
raise ValidationError(_("You cannot create two active sessions with the same responsible!"))
|
||||
|
||||
@api.constrains('config_id')
|
||||
def _check_pos_config(self):
|
||||
if self.search_count([('state', '!=', 'closed'), ('config_id', '=', self.config_id.id)]) > 1:
|
||||
if self.search_count([
|
||||
('state', '!=', 'closed'),
|
||||
('config_id', '=', self.config_id.id),
|
||||
('name', 'not like', 'RESCUE FOR'),
|
||||
]) > 1:
|
||||
raise ValidationError(_("You cannot create two active sessions related to the same point of sale!"))
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -11,6 +11,7 @@ var CrashManager = require('web.CrashManager');
|
||||
|
||||
|
||||
var _t = core._t;
|
||||
var _lt = core._lt;
|
||||
var QWeb = core.qweb;
|
||||
|
||||
/* -------- The Order Selector -------- */
|
||||
@@ -704,7 +705,7 @@ var Chrome = PosBaseWidget.extend({
|
||||
'widget': HeaderButtonWidget,
|
||||
'append': '.pos-rightheader',
|
||||
'args': {
|
||||
label: _t('Close'),
|
||||
label: _lt('Close'),
|
||||
action: function(){
|
||||
var self = this;
|
||||
if (!this.confirmed) {
|
||||
|
||||
@@ -1318,7 +1318,9 @@ exports.Orderline = Backbone.Model.extend({
|
||||
this.trigger('change',this);
|
||||
},
|
||||
get_unit_price: function(){
|
||||
return round_di(this.price || 0, this.pos.dp['Product Price'])
|
||||
var digits = this.pos.dp['Product Price'];
|
||||
// round and truncate to mimic _sybmbol_set behavior
|
||||
return parseFloat(round_di(this.price || 0, digits).toFixed(digits));
|
||||
},
|
||||
get_unit_display_price: function(){
|
||||
if (this.pos.config.iface_tax_included) {
|
||||
|
||||
@@ -1355,6 +1355,16 @@ var ClientListScreenWidget = ScreenWidget.extend({
|
||||
contents.append($(QWeb.render('ClientDetailsEdit',{widget:this,partner:partner})));
|
||||
this.toggle_save_button();
|
||||
|
||||
// Browsers attempt to scroll invisible input elements
|
||||
// into view (eg. when hidden behind keyboard). They don't
|
||||
// seem to take into account that some elements are not
|
||||
// scrollable.
|
||||
contents.find('input').blur(function() {
|
||||
setTimeout(function() {
|
||||
self.$('.window').scrollTop(0);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
contents.find('.image-uploader').on('change',function(event){
|
||||
self.load_image_file(event.target.files[0],function(res){
|
||||
if (res) {
|
||||
@@ -1404,7 +1414,9 @@ var ReceiptScreenWidget = ScreenWidget.extend({
|
||||
|
||||
this.render_change();
|
||||
this.render_receipt();
|
||||
|
||||
this.handle_auto_print();
|
||||
},
|
||||
handle_auto_print: function() {
|
||||
if (this.should_auto_print()) {
|
||||
this.print();
|
||||
if (this.should_close_immediately()){
|
||||
@@ -1413,7 +1425,6 @@ var ReceiptScreenWidget = ScreenWidget.extend({
|
||||
} else {
|
||||
this.lock_screen(false);
|
||||
}
|
||||
|
||||
},
|
||||
should_auto_print: function() {
|
||||
return this.pos.config.iface_print_auto && !this.pos.get_order()._printed;
|
||||
|
||||
@@ -10,12 +10,16 @@ class TestPointOfSaleCommon(common.TransactionCase):
|
||||
self.AccountBankStatementLine = self.env['account.bank.statement.line']
|
||||
self.PosMakePayment = self.env['pos.make.payment']
|
||||
self.PosOrder = self.env['pos.order']
|
||||
self.PosSession = self.env['pos.session']
|
||||
self.company_id = self.ref('base.main_company')
|
||||
self.product3 = self.env.ref('product.product_product_3')
|
||||
self.product4 = self.env.ref('product.product_product_4')
|
||||
self.partner1 = self.env.ref('base.res_partner_1')
|
||||
self.partner4 = self.env.ref('base.res_partner_4')
|
||||
self.pos_config = self.env.ref('point_of_sale.pos_config_main')
|
||||
self.carotte = self.env.ref('point_of_sale.carotte')
|
||||
self.courgette = self.env.ref('point_of_sale.courgette')
|
||||
self.onions = self.env.ref('point_of_sale.Onions')
|
||||
|
||||
# create a new session
|
||||
self.pos_order_session0 = self.env['pos.session'].create({
|
||||
|
||||
@@ -4,8 +4,8 @@ import time
|
||||
import os
|
||||
|
||||
import odoo
|
||||
from odoo import tools, report as odoo_report
|
||||
from odoo.tools import float_compare, test_reports
|
||||
from odoo import fields, tools, report as odoo_report
|
||||
from odoo.tools import float_compare, mute_logger, test_reports
|
||||
|
||||
from odoo.addons.point_of_sale.tests.common import TestPointOfSaleCommon
|
||||
|
||||
@@ -411,3 +411,138 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
|
||||
# I confirm the bank statement using Confirm button
|
||||
|
||||
self.AccountBankStatement.button_confirm_bank()
|
||||
|
||||
def test_create_from_ui(self):
|
||||
"""
|
||||
Simulation of sales coming from the interface, even after closing the session
|
||||
"""
|
||||
# I click on create a new session button
|
||||
self.pos_config.open_session_cb()
|
||||
|
||||
current_session = self.pos_config.current_session_id
|
||||
num_starting_orders = len(current_session.order_ids)
|
||||
|
||||
carrot_order = {'data':
|
||||
{'amount_paid': 0.9,
|
||||
'amount_return': 0,
|
||||
'amount_tax': 0,
|
||||
'amount_total': 0.9,
|
||||
'creation_date': fields.Datetime.now(),
|
||||
'fiscal_position_id': False,
|
||||
'lines': [[0,
|
||||
0,
|
||||
{'discount': 0,
|
||||
'id': 42,
|
||||
'pack_lot_ids': [],
|
||||
'price_unit': 0.9,
|
||||
'product_id': self.carotte.id,
|
||||
'qty': 1,
|
||||
'tax_ids': []}]],
|
||||
'name': 'Order 00042-003-0014',
|
||||
'partner_id': False,
|
||||
'pos_session_id': current_session.id,
|
||||
'sequence_number': 2,
|
||||
'statement_ids': [[0,
|
||||
0,
|
||||
{'account_id': self.env.user.partner_id.property_account_receivable_id.id,
|
||||
'amount': 0.9,
|
||||
'journal_id': self.pos_config.journal_ids[0].id,
|
||||
'name': fields.Datetime.now(),
|
||||
'statement_id': current_session.statement_ids[0].id}]],
|
||||
'uid': '00042-003-0014',
|
||||
'user_id': self.env.uid},
|
||||
'id': '00042-003-0014',
|
||||
'to_invoice': False}
|
||||
|
||||
zucchini_order = {'data':
|
||||
{'amount_paid': 1.2,
|
||||
'amount_return': 0,
|
||||
'amount_tax': 0,
|
||||
'amount_total': 1.1,
|
||||
'creation_date': fields.Datetime.now(),
|
||||
'fiscal_position_id': False,
|
||||
'lines': [[0,
|
||||
0,
|
||||
{'discount': 0,
|
||||
'id': 3,
|
||||
'pack_lot_ids': [],
|
||||
'price_unit': 1.2,
|
||||
'product_id': self.courgette.id,
|
||||
'qty': 1,
|
||||
'tax_ids': []}]],
|
||||
'name': 'Order 00043-003-0014',
|
||||
'partner_id': False,
|
||||
'pos_session_id': current_session.id,
|
||||
'sequence_number': self.pos_config.journal_id.id,
|
||||
'statement_ids': [[0,
|
||||
0,
|
||||
{'account_id': self.env.user.partner_id.property_account_receivable_id.id,
|
||||
'amount': 1.2,
|
||||
'journal_id': self.pos_config.journal_ids[0].id,
|
||||
'name': fields.Datetime.now(),
|
||||
'statement_id': current_session.statement_ids[0].id}]],
|
||||
'uid': '00043-003-0014',
|
||||
'user_id': self.env.uid},
|
||||
'id': '00043-003-0014',
|
||||
'to_invoice': False}
|
||||
|
||||
onions_order = {'data':
|
||||
{'amount_paid': 1.28,
|
||||
'amount_return': 0,
|
||||
'amount_tax': 0,
|
||||
'amount_total': 1.28,
|
||||
'creation_date': fields.Datetime.now(),
|
||||
'fiscal_position_id': False,
|
||||
'lines': [[0,
|
||||
0,
|
||||
{'discount': 0,
|
||||
'id': 3,
|
||||
'pack_lot_ids': [],
|
||||
'price_unit': 1.28,
|
||||
'product_id': self.onions.id,
|
||||
'qty': 1,
|
||||
'tax_ids': [[6, False, self.onions.taxes_id.ids]]}]],
|
||||
'name': 'Order 00044-003-0014',
|
||||
'partner_id': False,
|
||||
'pos_session_id': current_session.id,
|
||||
'sequence_number': self.pos_config.journal_id.id,
|
||||
'statement_ids': [[0,
|
||||
0,
|
||||
{'account_id': self.env.user.partner_id.property_account_receivable_id.id,
|
||||
'amount': 1.28,
|
||||
'journal_id': self.pos_config.journal_ids[0].id,
|
||||
'name': fields.Datetime.now(),
|
||||
'statement_id': current_session.statement_ids[0].id}]],
|
||||
'uid': '00044-003-0014',
|
||||
'user_id': self.env.uid},
|
||||
'id': '00044-003-0014',
|
||||
'to_invoice': False}
|
||||
|
||||
|
||||
# I create an order on an open session
|
||||
self.PosOrder.create_from_ui([carrot_order])
|
||||
self.assertEqual(num_starting_orders + 1, len(current_session.order_ids), "Submitted order not encoded")
|
||||
|
||||
# I resubmit the same order
|
||||
self.PosOrder.create_from_ui([carrot_order])
|
||||
self.assertEqual(num_starting_orders + 1, len(current_session.order_ids), "Resubmitted order was not skipped")
|
||||
|
||||
# I close the session
|
||||
current_session.action_pos_session_closing_control()
|
||||
self.assertEqual(current_session.state, 'closed', "Session was not properly closed")
|
||||
self.assertFalse(self.pos_config.current_session_id, "Current session not properly recomputed")
|
||||
|
||||
# I keep selling after the session is closed
|
||||
with mute_logger('odoo.addons.point_of_sale.models.pos_order'):
|
||||
self.PosOrder.create_from_ui([zucchini_order, onions_order])
|
||||
rescue_session = self.PosSession.search([
|
||||
('config_id', '=', self.pos_config.id),
|
||||
('state', '=', 'opened'),
|
||||
])
|
||||
self.assertEqual(len(rescue_session), 1, "One (and only one) rescue session should be created for orphan orders")
|
||||
self.assertIn("(RESCUE FOR %s)" % current_session.name, rescue_session.name, "Rescue session is not linked to the previous one")
|
||||
self.assertEqual(len(rescue_session.order_ids), 2, "Rescue session does not contain both orders")
|
||||
|
||||
# I close the rescue session
|
||||
rescue_session.action_pos_session_closing_control()
|
||||
self.assertEqual(rescue_session.state, 'closed', "Rescue session was not properly closed")
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
<field name="user_id" />
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="User" domain="[]" context="{'group_by' : 'user_id'}" />
|
||||
<filter string="Point of Sale" domain="[]" context="{'group_by': 'user_id'}" />
|
||||
<filter string="Point of Sale" domain="[]" context="{'group_by': 'config_id'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
||||
@@ -550,13 +550,34 @@ class PurchaseOrderLine(models.Model):
|
||||
line = super(PurchaseOrderLine, self).create(values)
|
||||
if line.order_id.state == 'purchase':
|
||||
line.order_id._create_picking()
|
||||
msg = _("Extra line with %s ") % (line.product_id.display_name,)
|
||||
line.order_id.message_post(body=msg)
|
||||
return line
|
||||
|
||||
@api.multi
|
||||
def write(self, values):
|
||||
orders = False
|
||||
if 'product_qty' in values:
|
||||
changed_lines = self.filtered(lambda x: x.order_id.state == 'purchase')
|
||||
if changed_lines:
|
||||
orders = changed_lines.mapped('order_id')
|
||||
for order in orders:
|
||||
order_lines = changed_lines.filtered(lambda x: x.order_id == order)
|
||||
msg = ""
|
||||
if any([values['product_qty'] < x.product_qty for x in order_lines]):
|
||||
msg += "<b>" + _('The ordered quantity has been decreased. Do not forget to take it into account on your bills and receipts.') + '</b><br/>'
|
||||
msg += "<ul>"
|
||||
for line in order_lines:
|
||||
msg += "<li> %s:" % (line.product_id.display_name,)
|
||||
msg += "<br/>" + _("Ordered Quantity") + ": %s -> %s <br/>" % (line.product_qty, float(values['product_qty']),)
|
||||
if line.product_id.type in ('product', 'consu'):
|
||||
msg += _("Received Quantity") + ": %s <br/>" % (line.qty_received,)
|
||||
msg += _("Billed Quantity") + ": %s <br/></li>" % (line.qty_invoiced,)
|
||||
msg += "</ul>"
|
||||
order.message_post(body=msg)
|
||||
result = super(PurchaseOrderLine, self).write(values)
|
||||
orders = self.filtered(lambda x: x.order_id.state == 'purchase').mapped('order_id')
|
||||
orders._create_picking()
|
||||
if orders:
|
||||
orders._create_picking()
|
||||
return result
|
||||
|
||||
name = fields.Text(string='Description', required=True)
|
||||
@@ -950,6 +971,12 @@ class ProcurementOrder(models.Model):
|
||||
'group_id': group
|
||||
}
|
||||
|
||||
def _make_po_select_supplier(self, suppliers):
|
||||
""" Method intended to be overridden by customized modules to implement any logic in the
|
||||
selection of supplier.
|
||||
"""
|
||||
return suppliers[0]
|
||||
|
||||
@api.multi
|
||||
def make_po(self):
|
||||
cache = {}
|
||||
@@ -959,7 +986,7 @@ class ProcurementOrder(models.Model):
|
||||
if not suppliers:
|
||||
procurement.message_post(body=_('No vendor associated to product %s. Please set one to fix this procurement.') % (procurement.product_id.name))
|
||||
continue
|
||||
supplier = suppliers[0]
|
||||
supplier = procurement._make_po_select_supplier(suppliers)
|
||||
partner = supplier.name
|
||||
|
||||
gpo = procurement.rule_id.group_propagation_option
|
||||
|
||||
@@ -461,8 +461,11 @@ class Report(models.Model):
|
||||
out, err = process.communicate()
|
||||
|
||||
if process.returncode not in [0, 1]:
|
||||
raise UserError(_('Wkhtmltopdf failed (error code: %s). '
|
||||
'Message: %s') % (str(process.returncode), err))
|
||||
if process.returncode == -11:
|
||||
message = _('Wkhtmltopdf failed (error code: %s). Memory limit too low or maximum file number of subprocess reached. Message : %s')
|
||||
else:
|
||||
message = _('Wkhtmltopdf failed (error code: %s). Message: %s')
|
||||
raise UserError(message % (str(process.returncode), err[-1000:]))
|
||||
|
||||
# Save the pdf in attachment if marked
|
||||
if reporthtml[0] is not False and save_in_attachment.get(reporthtml[0]):
|
||||
|
||||
@@ -84,6 +84,10 @@ class AccountInvoice(models.Model):
|
||||
|
||||
return report_pages
|
||||
|
||||
@api.multi
|
||||
def get_delivery_partner_id(self):
|
||||
self.ensure_one()
|
||||
return self.partner_shipping_id.id or super(AccountInvoice, self).get_delivery_partner_id()
|
||||
|
||||
class AccountInvoiceLine(models.Model):
|
||||
_inherit = 'account.invoice.line'
|
||||
|
||||
@@ -61,7 +61,9 @@ class SaleOrder(models.Model):
|
||||
for inv in invoice_ids:
|
||||
refund_ids += refund_ids.search([('type', '=', 'out_refund'), ('origin', '=', inv.number), ('origin', '!=', False), ('journal_id', '=', inv.journal_id.id)])
|
||||
|
||||
line_invoice_status = [line.invoice_status for line in order.order_line]
|
||||
# Ignore the status of the deposit product
|
||||
deposit_product_id = self.env['sale.advance.payment.inv']._default_product_id()
|
||||
line_invoice_status = [line.invoice_status for line in order.order_line if line.product_id != deposit_product_id]
|
||||
|
||||
if order.state not in ('sale', 'done'):
|
||||
invoice_status = 'no'
|
||||
@@ -507,11 +509,11 @@ class SaleOrder(models.Model):
|
||||
group = tax.tax_group_id
|
||||
res.setdefault(group, 0.0)
|
||||
amount = tax.compute_all(line.price_reduce + base_tax, quantity=line.product_uom_qty,
|
||||
product=line.product_id, partner=line.order_partner_id)['taxes'][0]['amount']
|
||||
product=line.product_id, partner=self.partner_shipping_id)['taxes'][0]['amount']
|
||||
res[group] += amount
|
||||
if tax.include_base_amount:
|
||||
base_tax += tax.compute_all(line.price_reduce + base_tax, quantity=1, product=line.product_id,
|
||||
partner=line.order_partner_id)['taxes'][0]['amount']
|
||||
partner=self.partner_shipping_id)['taxes'][0]['amount']
|
||||
res = sorted(res.items(), key=lambda l: l[0].sequence)
|
||||
res = map(lambda l: (l[0].name, l[1]), res)
|
||||
return res
|
||||
@@ -558,7 +560,7 @@ class SaleOrderLine(models.Model):
|
||||
"""
|
||||
for line in self:
|
||||
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||
taxes = line.tax_id.compute_all(price, line.order_id.currency_id, line.product_uom_qty, product=line.product_id, partner=line.order_id.partner_id)
|
||||
taxes = line.tax_id.compute_all(price, line.order_id.currency_id, line.product_uom_qty, product=line.product_id, partner=line.order_id.partner_shipping_id)
|
||||
line.update({
|
||||
'price_tax': sum(t.get('amount', 0.0) for t in taxes.get('taxes', [])),
|
||||
'price_total': taxes['total_included'],
|
||||
@@ -624,7 +626,7 @@ class SaleOrderLine(models.Model):
|
||||
fpos = line.order_id.fiscal_position_id or line.order_id.partner_id.property_account_position_id
|
||||
# If company_id is set, always filter taxes by the company
|
||||
taxes = line.product_id.taxes_id.filtered(lambda r: not line.company_id or r.company_id == line.company_id)
|
||||
line.tax_id = fpos.map_tax(taxes, line.product_id, line.order_id.partner_id) if fpos else taxes
|
||||
line.tax_id = fpos.map_tax(taxes, line.product_id, line.order_id.partner_shipping_id) if fpos else taxes
|
||||
|
||||
@api.multi
|
||||
def _prepare_order_line_procurement(self, group_id=False):
|
||||
@@ -688,16 +690,37 @@ class SaleOrderLine(models.Model):
|
||||
line = super(SaleOrderLine, self).create(values)
|
||||
if line.state == 'sale':
|
||||
line._action_procurement_create()
|
||||
msg = _("Extra line with %s ") % (line.product_id.display_name,)
|
||||
line.order_id.message_post(body=msg)
|
||||
|
||||
return line
|
||||
|
||||
@api.multi
|
||||
def write(self, values):
|
||||
lines = False
|
||||
changed_lines = False
|
||||
if 'product_uom_qty' in values:
|
||||
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
|
||||
lines = self.filtered(
|
||||
lambda r: r.state == 'sale' and float_compare(r.product_uom_qty, values['product_uom_qty'], precision_digits=precision) == -1)
|
||||
changed_lines = self.filtered(
|
||||
lambda r: r.state == 'sale' and float_compare(r.product_uom_qty, values['product_uom_qty'], precision_digits=precision) != 0)
|
||||
if changed_lines:
|
||||
orders = self.mapped('order_id')
|
||||
for order in orders:
|
||||
order_lines = changed_lines.filtered(lambda x: x.order_id == order)
|
||||
msg = ""
|
||||
if any([values['product_uom_qty'] < x.product_uom_qty for x in order_lines]):
|
||||
msg += "<b>" + _('The ordered quantity has been decreased. Do not forget to take it into account on your invoices and delivery orders.') + '</b>'
|
||||
msg += "<ul>"
|
||||
for line in order_lines:
|
||||
msg += "<li> %s:" % (line.product_id.display_name,)
|
||||
msg += "<br/>" + _("Ordered Quantity") + ": %s -> %s <br/>" % (line.product_uom_qty, float(values['product_uom_qty']),)
|
||||
if line.product_id.type in ('consu', 'product'):
|
||||
msg += _("Delivered Quantity") + ": %s <br/>" % (line.qty_delivered,)
|
||||
msg += _("Invoiced Quantity") + ": %s <br/>" % (line.qty_invoiced,)
|
||||
msg += "</ul>"
|
||||
order.message_post(body=msg)
|
||||
result = super(SaleOrderLine, self).write(values)
|
||||
if lines:
|
||||
lines._action_procurement_create()
|
||||
@@ -821,10 +844,16 @@ class SaleOrderLine(models.Model):
|
||||
@api.multi
|
||||
def _get_display_price(self, product):
|
||||
# TO DO: move me in master/saas-16 on sale.order
|
||||
if self.order_id.pricelist_id.discount_policy == 'without_discount':
|
||||
if self.order_id.pricelist_id.discount_policy == 'with_discount':
|
||||
return product.with_context(pricelist=self.order_id.pricelist_id.id).price
|
||||
price, rule_id = self.order_id.pricelist_id.get_product_price_rule(self.product_id, self.product_uom_qty or 1.0, self.order_id.partner_id)
|
||||
pricelist_item = self.env['product.pricelist.item'].browse(rule_id)
|
||||
if (pricelist_item.base == 'pricelist' and pricelist_item.base_pricelist_id.discount_policy == 'with_discount'):
|
||||
price, rule_id = pricelist_item.base_pricelist_id.get_product_price_rule(self.product_id, self.product_uom_qty or 1.0, self.order_id.partner_id)
|
||||
return price
|
||||
else:
|
||||
from_currency = self.order_id.company_id.currency_id
|
||||
return from_currency.compute(product.lst_price, self.order_id.pricelist_id.currency_id)
|
||||
return product.with_context(pricelist=self.order_id.pricelist_id.id).price
|
||||
|
||||
@api.multi
|
||||
@api.onchange('product_id')
|
||||
@@ -964,7 +993,7 @@ class SaleOrderLine(models.Model):
|
||||
new_list_price, currency_id = self.with_context(context_partner)._get_real_price_currency(self.product_id, rule_id, self.product_uom_qty, self.product_uom, self.order_id.pricelist_id.id)
|
||||
new_list_price = self.env['account.tax']._fix_tax_included_price(new_list_price, self.product_id.taxes_id, self.tax_id)
|
||||
|
||||
if price != 0 and new_list_price != 0:
|
||||
if new_list_price != 0:
|
||||
if self.product_id.company_id and self.order_id.pricelist_id.currency_id != self.product_id.company_id.currency_id:
|
||||
# new_list_price is in company's currency while price in pricelist currency
|
||||
new_list_price = self.env['res.currency'].browse(currency_id).with_context(context_partner).compute(new_list_price, self.order_id.pricelist_id.currency_id)
|
||||
|
||||
@@ -12,7 +12,19 @@ class SaleOrderLine(models.Model):
|
||||
lines = {}
|
||||
if not domain:
|
||||
# To filter on analyic lines linked to an expense
|
||||
domain = [('so_line', 'in', self.ids), ('amount', '<=', 0.0)]
|
||||
expense_type_id = self.env.ref('account.data_account_type_expenses', raise_if_not_found=False)
|
||||
expense_type_id = expense_type_id and expense_type_id.id
|
||||
domain = [
|
||||
('so_line', 'in', self.ids),
|
||||
'|',
|
||||
('amount', '<', 0),
|
||||
'&',
|
||||
('amount', '=', 0),
|
||||
'|',
|
||||
('move_id', '=', False),
|
||||
('move_id.account_id.user_type_id', '=', expense_type_id)
|
||||
]
|
||||
|
||||
data = self.env['account.analytic.line'].read_group(
|
||||
domain,
|
||||
['so_line', 'unit_amount', 'product_uom_id'], ['product_uom_id', 'so_line'], lazy=False
|
||||
|
||||
@@ -142,5 +142,18 @@ class SaleOrderLine(models.Model):
|
||||
def _compute_analytic(self, domain=None):
|
||||
if not domain:
|
||||
# To filter on analyic lines linked to an expense
|
||||
domain = [('so_line', 'in', self.ids), '|', ('amount', '<=', 0.0), ('project_id', '!=', False)]
|
||||
expense_type_id = self.env.ref('account.data_account_type_expenses', raise_if_not_found=False)
|
||||
expense_type_id = expense_type_id and expense_type_id.id
|
||||
domain = [
|
||||
('so_line', 'in', self.ids),
|
||||
'|',
|
||||
'|',
|
||||
('amount', '<', 0.0),
|
||||
('project_id', '!=', False),
|
||||
'&',
|
||||
('amount', '=', 0),
|
||||
'|',
|
||||
('move_id', '=', False),
|
||||
('move_id.account_id.user_type_id', '=', expense_type_id)
|
||||
]
|
||||
return super(SaleOrderLine, self)._compute_analytic(domain=domain)
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
<record id="team_sales_department" model="crm.team">
|
||||
<field name="name">Sales</field>
|
||||
<field name="member_ids" eval="[(4, ref('base.user_root'))]"/>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="salesteam_website_sales" model="crm.team">
|
||||
<field name="name">Website</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
<field name="active" eval="False"/>
|
||||
<field name="team_type" eval="'website'"/>
|
||||
</record>
|
||||
|
||||
@@ -24,9 +24,11 @@ class CrmTeam(models.Model):
|
||||
def _get_default_team_id(self, user_id=None):
|
||||
if not user_id:
|
||||
user_id = self.env.uid
|
||||
team_id = self.env['crm.team'].sudo().search(
|
||||
['|', ('user_id', '=', user_id), ('member_ids', '=', user_id)],
|
||||
limit=1)
|
||||
company_id = self.sudo(user_id).company_id.id
|
||||
team_id = self.env['crm.team'].sudo().search([
|
||||
'|', ('user_id', '=', user_id), ('member_ids', '=', user_id),
|
||||
'|', ('company_id', '=', False), ('company_id', 'child_of', [company_id])
|
||||
], limit=1)
|
||||
if not team_id and 'default_team_id' in self.env.context:
|
||||
team_id = self.env['crm.team'].browse(self.env.context.get('default_team_id'))
|
||||
if not team_id:
|
||||
|
||||
@@ -277,23 +277,27 @@ class ProcurementOrder(models.Model):
|
||||
@api.model
|
||||
def _procure_orderpoint_confirm(self, use_new_cursor=False, company_id=False):
|
||||
""" Create procurements based on orderpoints.
|
||||
:param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement.
|
||||
:param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing
|
||||
1000 orderpoints.
|
||||
This is appropriate for batch jobs only.
|
||||
"""
|
||||
if use_new_cursor:
|
||||
cr = registry(self._cr.dbname).cursor()
|
||||
self = self.with_env(self.env(cr=cr))
|
||||
|
||||
OrderPoint = self.env['stock.warehouse.orderpoint']
|
||||
Procurement = self.env['procurement.order']
|
||||
ProcurementAutorundefer = Procurement.with_context(procurement_autorun_defer=True)
|
||||
procurement_list = []
|
||||
|
||||
orderpoints_noprefetch = OrderPoint.with_context(prefetch_fields=False).search(
|
||||
company_id and [('company_id', '=', company_id)] or [],
|
||||
order=self._procurement_from_orderpoint_get_order())
|
||||
domain = [('company_id', '=', company_id)] if company_id else []
|
||||
domain += [('product_id.active', '=', True)]
|
||||
orderpoints_noprefetch = OrderPoint.with_context(prefetch_fields=False).search(domain,
|
||||
order=self._procurement_from_orderpoint_get_order()).ids
|
||||
while orderpoints_noprefetch:
|
||||
orderpoints = OrderPoint.browse(orderpoints_noprefetch[:1000].ids)
|
||||
if use_new_cursor:
|
||||
cr = registry(self._cr.dbname).cursor()
|
||||
self = self.with_env(self.env(cr=cr))
|
||||
OrderPoint = self.env['stock.warehouse.orderpoint']
|
||||
Procurement = self.env['procurement.order']
|
||||
ProcurementAutorundefer = Procurement.with_context(procurement_autorun_defer=True)
|
||||
procurement_list = []
|
||||
|
||||
orderpoints = OrderPoint.browse(orderpoints_noprefetch[:1000])
|
||||
orderpoints_noprefetch = orderpoints_noprefetch[1000:]
|
||||
|
||||
# Calculate groups that can be executed together
|
||||
@@ -343,7 +347,7 @@ class ProcurementOrder(models.Model):
|
||||
|
||||
except OperationalError:
|
||||
if use_new_cursor:
|
||||
orderpoints_noprefetch += orderpoint.id
|
||||
orderpoints_noprefetch += [orderpoint.id]
|
||||
cr.rollback()
|
||||
continue
|
||||
else:
|
||||
@@ -367,8 +371,6 @@ class ProcurementOrder(models.Model):
|
||||
|
||||
if use_new_cursor:
|
||||
cr.commit()
|
||||
cr.close()
|
||||
|
||||
if use_new_cursor:
|
||||
cr.commit()
|
||||
cr.close()
|
||||
return {}
|
||||
|
||||
@@ -349,7 +349,7 @@ class StockMove(models.Model):
|
||||
raise UserError(_('Cannot unreserve a done move'))
|
||||
self.quants_unreserve()
|
||||
if not self.env.context.get('no_state_change'):
|
||||
waiting = self.filtered(lambda move: move.get_ancestors())
|
||||
waiting = self.filtered(lambda move: move.procure_method == 'make_to_order' or move.get_ancestors())
|
||||
waiting.write({'state': 'waiting'})
|
||||
(self - waiting).write({'state': 'confirmed'})
|
||||
|
||||
@@ -411,15 +411,8 @@ class StockMove(models.Model):
|
||||
type (moves should already have them identical). Otherwise, create a new
|
||||
picking to assign them to. """
|
||||
Picking = self.env['stock.picking']
|
||||
|
||||
# If this method is called in batch by a write on a one2many and
|
||||
# at some point had to create a picking, some next iterations could
|
||||
# try to find back the created picking. As we look for it by searching
|
||||
# on some computed fields, we have to force a recompute, else the
|
||||
# record won't be found.
|
||||
self.recompute()
|
||||
|
||||
for move in self:
|
||||
recompute = False
|
||||
picking = Picking.search([
|
||||
('group_id', '=', move.group_id.id),
|
||||
('location_id', '=', move.location_id.id),
|
||||
@@ -428,8 +421,17 @@ class StockMove(models.Model):
|
||||
('printed', '=', False),
|
||||
('state', 'in', ['draft', 'confirmed', 'waiting', 'partially_available', 'assigned'])], limit=1)
|
||||
if not picking:
|
||||
recompute = True
|
||||
picking = Picking.create(move._get_new_picking_values())
|
||||
move.write({'picking_id': picking.id})
|
||||
|
||||
# If this method is called in batch by a write on a one2many and
|
||||
# at some point had to create a picking, some next iterations could
|
||||
# try to find back the created picking. As we look for it by searching
|
||||
# on some computed fields, we have to force a recompute, else the
|
||||
# record won't be found.
|
||||
if recompute:
|
||||
move.recompute()
|
||||
return True
|
||||
_picking_assign = assign_picking
|
||||
|
||||
@@ -690,7 +692,7 @@ class StockMove(models.Model):
|
||||
if len(reserved_quant_ids) == 0 and move.partially_available:
|
||||
vals['partially_available'] = False
|
||||
if move.state == 'assigned':
|
||||
if move.find_move_ancestors():
|
||||
if move.procure_method == 'make_to_order' or move.find_move_ancestors():
|
||||
vals['state'] = 'waiting'
|
||||
else:
|
||||
vals['state'] = 'confirmed'
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
<div t-field="o.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True}'/>
|
||||
</div>
|
||||
<div t-if="not (o.move_lines and o.move_lines[0].partner_id) and o.partner_id" name="partner_header">
|
||||
t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>
|
||||
|
||||
@@ -1726,4 +1726,69 @@ class TestStockFlow(TestStockCommon):
|
||||
|
||||
# force assign on the delivery order, it should be assigned
|
||||
picking_out.force_assign()
|
||||
self.assertEquals(picking_out.state, "assigned")
|
||||
self.assertEquals(picking_out.state, "assigned")
|
||||
|
||||
def test_74_move_state_waiting_mto(self):
|
||||
""" This test will check that when a move is unreserved, it state change to 'waiting' if
|
||||
it has ancestors or is has a 'procure_method' equal to 'make_to_order' else the state
|
||||
changes to 'confirmed'.
|
||||
"""
|
||||
picking_out = self.PickingObj.create({
|
||||
'partner_id': self.partner_agrolite_id,
|
||||
'picking_type_id': self.picking_type_out,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
move_mto_alone = self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 2,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': picking_out.id,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location,
|
||||
'procure_method':'make_to_order'})
|
||||
move_with_ancestors = self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 2,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': picking_out.id,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
the_ancestor = self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 2,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': picking_out.id,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location,
|
||||
'move_dest_id': move_with_ancestors.id})
|
||||
other_move = self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 2,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': picking_out.id,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
|
||||
move_mto_alone.action_confirm()
|
||||
move_with_ancestors.action_confirm()
|
||||
other_move.action_confirm()
|
||||
|
||||
move_mto_alone.do_unreserve()
|
||||
move_with_ancestors.do_unreserve()
|
||||
other_move.do_unreserve()
|
||||
|
||||
self.assertEquals(move_mto_alone.state, "waiting")
|
||||
self.assertEquals(move_with_ancestors.state, "waiting")
|
||||
self.assertEquals(other_move.state, "confirmed")
|
||||
|
||||
move_mto_alone.recalculate_move_state()
|
||||
move_with_ancestors.recalculate_move_state()
|
||||
other_move.recalculate_move_state()
|
||||
|
||||
self.assertEquals(move_mto_alone.state, "waiting")
|
||||
self.assertEquals(move_with_ancestors.state, "waiting")
|
||||
self.assertEquals(other_move.state, "confirmed")
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
<field name="model">stock.warehouse</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Warehouse">
|
||||
<field name="name"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
</search>
|
||||
</field>
|
||||
|
||||
@@ -188,7 +188,7 @@ class StockMove(models.Model):
|
||||
tmpl_dict = defaultdict(lambda: 0.0)
|
||||
# adapt standard price on incomming moves if the product cost_method is 'average'
|
||||
std_price_update = {}
|
||||
for move in self.filtered(lambda move: move.location_id.usage == 'supplier' and move.product_id.cost_method == 'average'):
|
||||
for move in self.filtered(lambda move: move.location_id.usage in ('supplier', 'production') and move.product_id.cost_method == 'average'):
|
||||
product_tot_qty_available = move.product_id.qty_available + tmpl_dict[move.product_id.id]
|
||||
|
||||
# if the incoming move is for a purchase order with foreign currency, need to call this to get the same value that the quant will use.
|
||||
|
||||
@@ -129,6 +129,7 @@ class LandedCost(models.Model):
|
||||
if quant.location_id.usage != 'internal':
|
||||
qty_out += quant.qty
|
||||
line._create_accounting_entries(move, qty_out)
|
||||
move.assert_balanced()
|
||||
cost.write({'state': 'done', 'account_move_id': move.id})
|
||||
move.post()
|
||||
return True
|
||||
@@ -326,7 +327,7 @@ class AdjustmentLines(models.Model):
|
||||
Generate the account.move.line values to track the landed cost.
|
||||
Afterwards, for the goods that are already out of stock, we should create the out moves
|
||||
"""
|
||||
AccountMoveLine = self.env['account.move.line'].with_context(check_move_validity=False)
|
||||
AccountMoveLine = self.env['account.move.line'].with_context(check_move_validity=False, recompute=False)
|
||||
|
||||
base_line = {
|
||||
'name': self.name,
|
||||
@@ -389,5 +390,4 @@ class AdjustmentLines(models.Model):
|
||||
AccountMoveLine.create(debit_line)
|
||||
AccountMoveLine.create(credit_line)
|
||||
|
||||
move.assert_balanced()
|
||||
return True
|
||||
|
||||
@@ -651,6 +651,8 @@ class Database(http.Controller):
|
||||
@http.route('/web/database/create', type='http', auth="none", methods=['POST'], csrf=False)
|
||||
def create(self, master_pwd, name, lang, password, **post):
|
||||
try:
|
||||
if not re.match('^[a-zA-Z][a-zA-Z0-9_]+$', name):
|
||||
raise Exception(_('Invalid database name. Only alphanumerical characters and underscore are allowed.'))
|
||||
# country code could be = "False" which is actually True in python
|
||||
country_code = post.get('country_code') or False
|
||||
dispatch_rpc('db', 'create_database', [master_pwd, name, bool(post.get('demo')), lang, password, post['login'], country_code])
|
||||
@@ -663,6 +665,8 @@ class Database(http.Controller):
|
||||
@http.route('/web/database/duplicate', type='http', auth="none", methods=['POST'], csrf=False)
|
||||
def duplicate(self, master_pwd, name, new_name):
|
||||
try:
|
||||
if not re.match('^[a-zA-Z][a-zA-Z0-9_]+$', new_name):
|
||||
raise Exception(_('Invalid database name. Only alphanumerical characters and underscore are allowed.'))
|
||||
dispatch_rpc('db', 'duplicate_database', [master_pwd, name, new_name])
|
||||
return http.local_redirect('/web/database/manager')
|
||||
except Exception, e:
|
||||
|
||||
@@ -207,9 +207,15 @@ var DataExport = Dialog.extend({
|
||||
this.records = {};
|
||||
this.dataset = dataset;
|
||||
this.exports = new data.DataSetSearch(this, 'ir.exports', this.dataset.get_context());
|
||||
|
||||
|
||||
this.row_index = 0;
|
||||
this.row_index_level = 0;
|
||||
|
||||
// The default for the ".modal_content" element is "max-height: 100%;"
|
||||
// but we want it to always expand to "height: 100%;" for this modal.
|
||||
// This can be achieved thanks to LESS modification without touching
|
||||
// the ".modal-content" rules... but not with Internet explorer (11).
|
||||
this.$modal.find(".modal-content").css("height", "100%");
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
@@ -261,7 +267,7 @@ var DataExport = Dialog.extend({
|
||||
|
||||
function do_setup_export_formats(formats) {
|
||||
var $fmts = self.$('.o_export_format');
|
||||
|
||||
|
||||
_.each(formats, function(format, i) {
|
||||
var $radio = $('<input/>', {type: 'radio', value: format.tag, name: 'o_export_format_name'});
|
||||
var $label = $('<label/>', {html: format.label});
|
||||
@@ -362,7 +368,7 @@ var DataExport = Dialog.extend({
|
||||
},
|
||||
on_show_data: function(records, expansion) {
|
||||
var self = this;
|
||||
|
||||
|
||||
if(expansion) {
|
||||
this.$('.o_export_tree_item[data-id="' + expansion + '"]')
|
||||
.addClass('open')
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.o_web_client .o_export {
|
||||
|
||||
.o-flex(0, 1, 1000px);
|
||||
.o-flex(1, 1, auto);
|
||||
.o-flex-display();
|
||||
.o-flex-flow(column, nowrap);
|
||||
min-height: 0;
|
||||
|
||||
> p, > .row {
|
||||
.o-flex(0, 0, auto);
|
||||
@@ -28,8 +28,9 @@
|
||||
}
|
||||
|
||||
.o_export_panel {
|
||||
.o-flex(1, 0, 0);
|
||||
.o-flex(1, 1, auto);
|
||||
position: relative;
|
||||
min-height: 0;
|
||||
|
||||
> div {
|
||||
height: 100%;
|
||||
@@ -59,9 +60,10 @@
|
||||
.o_left_field_panel, .o_fields_list {
|
||||
border: 1px solid @odoo-brand-lightsecondary;
|
||||
overflow: auto;
|
||||
.o-flex(1, 0, 0);
|
||||
.o-flex(1, 1, auto);
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
|
||||
.o_export_tree_item {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@@ -72,7 +74,7 @@
|
||||
background-color: @odoo-brand-primary;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.o_expand_parent {
|
||||
.o-position-absolute(@top: 4px, @left: 5px);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<label for="name" class="control-label">Database Name</label>
|
||||
<input id="name" type="text" name="name" class="form-control" required="required" autocomplete="off"/>
|
||||
<input id="name" type="text" name="name" class="form-control" required="required" autocomplete="off" pattern="^[a-zA-Z][a-zA-Z0-9_]+$" title="Only alphanumerical characters and underscore are allowed"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,7 +207,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name" class="control-label">Database Name</label>
|
||||
<input id="name" type="text" name="name" class="form-control" required="required"/>
|
||||
<input id="name" type="text" name="name" class="form-control" required="required" pattern="^[a-zA-Z][a-zA-Z0-9_]+$" title="Only alphanumerical characters and underscore are allowed"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="copy">This database might have been moved or copied.</label>
|
||||
@@ -280,7 +280,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new_name" class="control-label">New Name</label>
|
||||
<input id="new_name" type="text" name="new_name" class="form-control" required="required"/>
|
||||
<input id="new_name" type="text" name="new_name" class="form-control" required="required" pattern="^[a-zA-Z][a-zA-Z0-9_]+$" title="Only alphanumerical characters and underscore are allowed"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -292,7 +292,15 @@ eventHandler.modules.popover.update = function ($popover, oStyle, isAirMode) {
|
||||
$imagePopover.find('.o_image_alt').text( (alt || "").replace(/"/g, '"') ).parent().toggle(oStyle.image.tagName === "IMG");
|
||||
$imagePopover.show();
|
||||
|
||||
range.createFromNode(dom.firstChild(oStyle.image)).select();
|
||||
// for video tag (non-void) we select the range over the tag,
|
||||
// for other media types we get the first descendant leaf element
|
||||
var target_node = oStyle.image;
|
||||
if (!oStyle.image.className.match(/(^|\s)media_iframe_video(\s|$)/i)) {
|
||||
target_node = dom.firstChild(target_node);
|
||||
}
|
||||
range.createFromNode(target_node).select();
|
||||
// save range on the editor so it is not lost if restored
|
||||
eventHandler.modules.editor.saveRange(dom.makeLayoutInfo(target_node).editable());
|
||||
} else {
|
||||
$(".note-control-selection").hide();
|
||||
}
|
||||
|
||||
@@ -637,9 +637,12 @@ var fontIconsDialog = Widget.extend({
|
||||
},
|
||||
renderElement: function () { // extract list of font (like awesome) from the cheatsheet.
|
||||
this.iconsParser = fontIcons;
|
||||
this.icons = _.flatten(_.map(fontIcons, function (data) {
|
||||
this.icons = _.flatten(_.map(fontIcons, function (data) { // TODO maybe useless now
|
||||
return data.icons;
|
||||
}));
|
||||
this.alias = _.flatten(_.map(fontIcons, function (data) {
|
||||
return data.alias;
|
||||
}));
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
search: function (needle) {
|
||||
@@ -741,11 +744,8 @@ var fontIconsDialog = Widget.extend({
|
||||
case '': continue;
|
||||
default:
|
||||
$(".font-icons-icon").removeClass("o_selected").filter("[data-alias*=',"+cls+",']").addClass("o_selected");
|
||||
for (var k=0; k<this.icons.length; k++) {
|
||||
if (this.icons.indexOf(cls) !== -1) {
|
||||
this.$('#fa-icon').val(cls);
|
||||
break;
|
||||
}
|
||||
if (this.alias.indexOf(cls) !== -1) {
|
||||
this.$('#fa-icon').val(cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +307,9 @@ var KanbanView = View.extend({
|
||||
return this.fields.active;
|
||||
},
|
||||
_is_quick_create_enabled: function() {
|
||||
if(!_.contains(['char', 'boolean', 'many2one'], this.fields[this.group_by_field].type)){
|
||||
return false;
|
||||
}
|
||||
if (!this.quick_creatable || !this.is_action_enabled('create'))
|
||||
return false;
|
||||
if (this.fields_view.arch.attrs.quick_create !== undefined)
|
||||
|
||||
@@ -294,7 +294,8 @@ return core.Class.extend({
|
||||
this.update();
|
||||
}).bind(this));
|
||||
|
||||
window.location.href = session.debug ? $.param.querystring(tour.url, {debug: session.debug}) : tour.url;
|
||||
var url = session.debug ? $.param.querystring(tour.url, {debug: session.debug}) : tour.url;
|
||||
window.location.href = window.location.origin + url;
|
||||
} else {
|
||||
this.update();
|
||||
}
|
||||
|
||||
@@ -349,6 +349,7 @@ var MenuEntryDialog = widget.LinkDialog.extend({
|
||||
|
||||
return {
|
||||
'TopBar': TopBarContent,
|
||||
'EditMenuDialog': EditMenuDialog,
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -77,6 +77,14 @@ class SaleOrder(models.Model):
|
||||
total += line.price_subtotal + line.price_unit * ((line.discount or 0.0) / 100.0) * line.product_uom_qty # why is there a discount in a field named amount_undiscounted ??
|
||||
self.amount_undiscounted = total
|
||||
|
||||
@api.onchange('partner_id')
|
||||
def onchange_update_description_lang(self):
|
||||
if not self.template_id:
|
||||
return
|
||||
else:
|
||||
template = self.template_id.with_context(lang=self.partner_id.lang)
|
||||
self.website_description = template.website_description
|
||||
|
||||
@api.onchange('template_id')
|
||||
def onchange_template_id(self):
|
||||
if not self.template_id:
|
||||
|
||||
@@ -149,10 +149,19 @@ class SaleOrder(models.Model):
|
||||
else:
|
||||
# update line
|
||||
values = self._website_product_id_change(self.id, product_id, qty=quantity)
|
||||
|
||||
if self.pricelist_id.discount_policy == 'with_discount' and not self.env.context.get('fixed_price'):
|
||||
order = self.sudo().browse(self.id)
|
||||
product_context = dict(self.env.context)
|
||||
product_context.setdefault('lang', order.partner_id.lang)
|
||||
product_context.update({
|
||||
'partner': order.partner_id.id,
|
||||
'quantity': quantity,
|
||||
'date': order.date_order,
|
||||
'pricelist': order.pricelist_id.id,
|
||||
})
|
||||
product = self.env['product.product'].with_context(product_context).browse(product_id)
|
||||
values['price_unit'] = self.env['account.tax']._fix_tax_included_price(
|
||||
order_line._get_display_price(order_line.product_id),
|
||||
order_line._get_display_price(product),
|
||||
order_line.product_id.taxes_id,
|
||||
order_line.tax_id
|
||||
)
|
||||
|
||||
@@ -446,6 +446,10 @@ label.css_attribute_color.css_not_available {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.oe_website_sale input.quantity {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* ---- Publish managment and options ---- */
|
||||
.oe_overlay_options .dropdown ul[name="size"] table {
|
||||
margin-left: 20px;
|
||||
|
||||
@@ -385,6 +385,9 @@ label.css_attribute_color.css_not_available
|
||||
min-width: 48px
|
||||
text-align: center
|
||||
|
||||
.oe_website_sale input.quantity
|
||||
padding: 0
|
||||
|
||||
/* ---- Publish managment and options ---- */
|
||||
|
||||
.oe_overlay_options
|
||||
|
||||
@@ -74,6 +74,7 @@ odoo.define('website_sale.website_sale', function (require) {
|
||||
var ajax = require('web.ajax');
|
||||
var utils = require('web.utils');
|
||||
var core = require('web.core');
|
||||
var config = require('web.config');
|
||||
var _t = core._t;
|
||||
|
||||
if(!$('.oe_website_sale').length) {
|
||||
@@ -295,8 +296,10 @@ odoo.define('website_sale.website_sale', function (require) {
|
||||
var $ul = $(ev.target).closest('.js_add_cart_variants');
|
||||
var $parent = $ul.closest('.js_product');
|
||||
var $product_id = $parent.find('input.product_id').first();
|
||||
var $price = $parent.find(".oe_price:first .oe_currency_value");
|
||||
var $default_price = $parent.find(".oe_default_price:first .oe_currency_value");
|
||||
var $price = $parent.find(".oe_price:first .oe_currency_value")
|
||||
.add($('#product_confirmation').find(".oe_price"));
|
||||
var $default_price = $parent.find(".oe_default_price:first .oe_currency_value")
|
||||
.add($('#product_confirmation').find(".oe_default_price:first .oe_currency_value"));
|
||||
var $optional_price = $parent.find(".oe_optional:first .oe_currency_value");
|
||||
var variant_ids = $ul.data("attribute_value_ids");
|
||||
var values = [];
|
||||
@@ -454,5 +457,8 @@ odoo.define('website_sale.website_sale', function (require) {
|
||||
$("select[name='country_id']").change();
|
||||
});
|
||||
|
||||
$('.ecom-zoomable img[data-zoom]').zoomOdoo({ attach: '#o-carousel-product'});
|
||||
// Deactivate image zoom for mobile devices, since it might prevent users to scroll
|
||||
if (config.device.size_class > config.device.SIZES.XS) {
|
||||
$('.ecom-zoomable img[data-zoom]').zoomOdoo({ attach: '#o-carousel-product'});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -580,7 +580,7 @@
|
||||
<a t-attf-href="#" class="mb8 input-group-addon js_add_cart_json">
|
||||
<i class="fa fa-minus"></i>
|
||||
</a>
|
||||
<input type="text" class="form-control" data-min="1" name="add_qty" value="1"/>
|
||||
<input type="text" class="form-control quantity" data-min="1" name="add_qty" value="1"/>
|
||||
<a t-attf-href="#" class="mb8 input-group-addon float_left js_add_cart_json">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
@@ -888,7 +888,7 @@
|
||||
<a t-attf-href="#" class="mb8 input-group-addon js_add_cart_json hidden-xs">
|
||||
<i class="fa fa-minus"></i>
|
||||
</a>
|
||||
<input type="text" class="js_quantity form-control" t-att-data-line-id="line.id" t-att-data-product-id="line.product_id.id" t-att-value="int(line.product_uom_qty)" />
|
||||
<input type="text" class="js_quantity form-control quantity" t-att-data-line-id="line.id" t-att-data-product-id="line.product_id.id" t-att-value="int(line.product_uom_qty)" />
|
||||
<a t-attf-href="#" class="mb8 input-group-addon float_left js_add_cart_json hidden-xs">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
|
||||
@@ -58,14 +58,14 @@
|
||||
<a t-attf-href="#" class="mb8 input-group-addon js_add_cart_json hidden-xs">
|
||||
<i class="fa fa-minus"></i>
|
||||
</a>
|
||||
<input type="text" class="js_quantity form-control" data-min="1" name="add_qty" value="1"/>
|
||||
<input type="text" class="js_quantity form-control quantity" data-min="1" name="add_qty" value="1"/>
|
||||
<a t-attf-href="#" class="mb8 input-group-addon float_left js_add_cart_json hidden-xs">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center td-price" name="price">
|
||||
<span t-attf-class="text-danger oe_default_price" t-att-style="'text-decoration: line-through; white-space: nowrap;' + '' if (compute_currency(product.lst_price) - product.with_context(quantity=1).website_price ) > 0.01 else 'display: none;'"
|
||||
<span t-attf-class="text-danger oe_default_price" t-att-style="text-decoration: line-through; white-space: nowrap;"
|
||||
t-field="product.lst_price"
|
||||
t-options='{
|
||||
"widget": "monetary",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
Canada, 2017-3-24
|
||||
|
||||
Métal Sartigan agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Jérôme Thériault jerther@gmail.com https://github.com/jerther
|
||||
|
||||
List of contributors:
|
||||
|
||||
Jérôme Thériault jerther@gmail.com https://github.com/jerther
|
||||
@@ -0,0 +1,15 @@
|
||||
Spain, 2017-03-30
|
||||
|
||||
OnTruck agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Iñigo Juantegui
|
||||
|
||||
List of contributors:
|
||||
|
||||
Antonio Espinosa <antonio.espinosa@ontruck.com> https://github.com/antespi
|
||||
@@ -0,0 +1,22 @@
|
||||
Spain, 2017-04-04
|
||||
|
||||
Tecnativa S.L. agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Pedro M. Baeza pedro.baeza@tecnativa.com https://github.com/pedrobaeza
|
||||
|
||||
List of contributors:
|
||||
|
||||
Pedro M. Baeza pedro.baeza@tecnativa.com https://github.com/pedrobaeza
|
||||
Rafael Blasco rafael.blasco@tecnativa.com https://github.com/rafaelbn
|
||||
Sergio Teruel sergio.teruel@tecnativa.com https://github.com/sergio-teruel
|
||||
Carlos Dauden carlos.dauden@tecnativa.com https://github.com/carlosdauden
|
||||
Jairo Llopis jairo.llopis@tecnativa.com https://github.com/yajo
|
||||
Vicent Cubells vicent.cubells@tecnativa.com https://github.com/cubells
|
||||
Luis Montalba luis.montalba@tecnativa.com https://github.com/luismontalba
|
||||
David Vidal david.vidal@tecnativa.com https://github.com/chienandalu
|
||||
@@ -1,4 +1,4 @@
|
||||
Vietnam, 2016-11-30
|
||||
Vietnam, 2017-04-03
|
||||
|
||||
T.V.T Marine Automation agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
@@ -12,4 +12,6 @@ David Tran david.tran@tvtmarine.com https://github.com/tvtma/
|
||||
|
||||
List of contributors:
|
||||
|
||||
David Tran david.tran@tvtmarine.com https://github.com/tvtma/
|
||||
David Tran david.tran@tvtmarine.com https://github.com/davidtranhp
|
||||
Long Do dhlong.1209@gmail.com https://github.com/hoanglong87
|
||||
Hao Hoang hao.hoang@ma.tvtmarine.com https://github.com/hoanghao2001
|
||||
@@ -0,0 +1,9 @@
|
||||
Azer, 2017-03-26
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
GHADHOUN AZER ezer1989@gmail.com https://github.com/monami7001
|
||||
@@ -0,0 +1,11 @@
|
||||
Vietnam, 2017-04-10
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Tran Thanh Phuc ttp.phuctran@gmail.com https://github.com/ttpphuctran
|
||||
+2
-2
@@ -14,7 +14,7 @@ requirement is explained in the [FAQ](#faq).
|
||||
`odony`, the file would be `doc/cla/individual/odony.md`. The file should
|
||||
contain:
|
||||
|
||||
```
|
||||
```
|
||||
<country>, <date>
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
@@ -50,7 +50,7 @@ signature is merged.
|
||||
If the name of the company is Odoo, the file would be
|
||||
`doc/cla/corporate/odoo.md`. The file should contain:
|
||||
|
||||
```
|
||||
```
|
||||
<country>, <date>
|
||||
|
||||
<company-name> agrees to the terms of the Odoo Corporate Contributor License
|
||||
|
||||
@@ -58,7 +58,7 @@ following attributes:
|
||||
Requires an :term:`external id`, defaults to ``True``.
|
||||
|
||||
``field``
|
||||
'''''''''
|
||||
----------
|
||||
|
||||
Each record can be composed of ``field`` tags, defining values to set when
|
||||
creating the record. A ``record`` with no ``field`` will use all default
|
||||
|
||||
@@ -15,7 +15,7 @@ In any case the basic guidelines are:
|
||||
|
||||
* Shutdown your server
|
||||
|
||||
* Upgrade the web module
|
||||
* Install the web_enterprise module
|
||||
|
||||
* Restart your server
|
||||
|
||||
@@ -45,7 +45,7 @@ On Linux, using an installer
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python /usr/bin/odoo.py -d <database_name> -u web --stop-after-init
|
||||
$ python /usr/bin/odoo.py -d <database_name> -i web_enterprise --stop-after-init
|
||||
|
||||
* You should be able to connect to your Odoo Enterprise instance using your usual mean of identification.
|
||||
You can then link your database with your Odoo Enterprise Subscription by entering the code you received
|
||||
@@ -61,11 +61,11 @@ have your own favourite. You may need to adapt sections to your usual workflow.
|
||||
* Shutdown your server
|
||||
* Backup your community database
|
||||
* Update the ``--addons-path`` parameter of your launch command (see :ref:`setup/install/source`)
|
||||
* Update the web module by using
|
||||
* Install the web_enterprise module by using
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ -d <database_name> -u web --stop-after-init
|
||||
$ -d <database_name> -i web_enterprise --stop-after-init
|
||||
|
||||
Depending on the size of your database, this may take some time.
|
||||
|
||||
@@ -97,7 +97,7 @@ On Windows
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ odoo.exe -d <database_name> -u web --stop-after-init
|
||||
$ odoo.exe -d <database_name> -i web_enterprise --stop-after-init
|
||||
|
||||
* No need to manually launch the server, the service is running.
|
||||
You should be able to connect to your Odoo Enterprise instance using your usual
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<record id="ir_filters_delete_own_rule" model="ir.rule">
|
||||
<field name="name">ir.filters.own.rule.delete</field>
|
||||
<field name="model_id" ref="model_ir_filters"/>
|
||||
<field name="domain_force">[('create_uid', '=', user.id)]</field>
|
||||
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
|
||||
<field name="perm_read" eval="False"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
|
||||
@@ -9,6 +9,7 @@ from collections import defaultdict
|
||||
from odoo import api, fields, models, SUPERUSER_ID, tools, _
|
||||
from odoo.exceptions import AccessError, UserError, ValidationError
|
||||
from odoo.modules.registry import Registry
|
||||
from odoo.osv import expression
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -292,6 +293,25 @@ class IrModelFields(models.Model):
|
||||
raise UserError(_("The Selection Options expression is not a valid Pythonic expression."
|
||||
"Please provide an expression in the [('key','Label'), ...] format."))
|
||||
|
||||
@api.constrains('name', 'state')
|
||||
def _check_name(self):
|
||||
for field in self:
|
||||
if field.state == 'manual' and not field.name.startswith('x_'):
|
||||
raise ValidationError(_("Custom fields must have a name that starts with 'x_' !"))
|
||||
try:
|
||||
models.check_pg_name(field.name)
|
||||
except ValidationError:
|
||||
msg = _("Field names can only contain characters, digits and underscores (up to 63).")
|
||||
raise ValidationError(msg)
|
||||
|
||||
@api.constrains('model', 'name')
|
||||
def _unique_name(self):
|
||||
# fix on stable branch (to be converted into an SQL constraint)
|
||||
for field in self:
|
||||
count = self.search_count([('model', '=', field.model), ('name', '=', field.name)])
|
||||
if count > 1:
|
||||
raise ValidationError(_("Field names must be unique per model."))
|
||||
|
||||
_sql_constraints = [
|
||||
('size_gt_zero', 'CHECK (size>=0)', 'Size of the field cannot be negative.'),
|
||||
]
|
||||
@@ -449,6 +469,10 @@ class IrModelFields(models.Model):
|
||||
This method prevents the modification/deletion of many2one fields
|
||||
that have an inverse one2many, for instance.
|
||||
"""
|
||||
self = self.filtered(lambda record: record.state == 'manual')
|
||||
if not self:
|
||||
return
|
||||
|
||||
for record in self:
|
||||
model = self.env[record.model]
|
||||
field = model._fields[record.name]
|
||||
@@ -462,6 +486,23 @@ class IrModelFields(models.Model):
|
||||
msg = _("The field '%s' cannot be removed because the field '%s' depends on it.")
|
||||
raise UserError(msg % (field, model._field_inverses[field][0]))
|
||||
|
||||
# remove fields from registry, and check that views are not broken
|
||||
fields = [self.env[record.model]._pop_field(record.name) for record in self]
|
||||
domain = expression.OR([('arch_db', 'like', record.name)] for record in self)
|
||||
views = self.env['ir.ui.view'].search(domain)
|
||||
try:
|
||||
for view in views:
|
||||
view._check_xml()
|
||||
except Exception:
|
||||
raise UserError("\n".join([
|
||||
_("Cannot rename/delete fields that are still present in views:"),
|
||||
_("Fields:") + " " + ", ".join(map(str, fields)),
|
||||
_("View:") + " " + view.name,
|
||||
]))
|
||||
finally:
|
||||
# the registry has been modified, restore it
|
||||
self.pool.setup_models(self._cr)
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
if not self:
|
||||
@@ -504,9 +545,6 @@ class IrModelFields(models.Model):
|
||||
res = super(IrModelFields, self).create(vals)
|
||||
|
||||
if vals.get('state', 'manual') == 'manual':
|
||||
if not vals['name'].startswith('x_'):
|
||||
raise UserError(_("Custom fields must have a name that starts with 'x_' !"))
|
||||
|
||||
if vals.get('relation') and not self.env['ir.model'].search([('model', '=', vals['relation'])]):
|
||||
raise UserError(_("Model %s does not exist!") % vals['relation'])
|
||||
|
||||
@@ -569,12 +607,6 @@ class IrModelFields(models.Model):
|
||||
item._prepare_update()
|
||||
if column_rename:
|
||||
raise UserError(_('Can only rename one field at a time!'))
|
||||
if vals['name'] in obj._fields:
|
||||
raise UserError(_('Cannot rename field to %s, because that field already exists!') % vals['name'])
|
||||
if vals.get('state', 'manual') == 'manual' and not vals['name'].startswith('x_'):
|
||||
raise UserError(_('New field name must still start with x_ , because it is a custom field!'))
|
||||
if '\'' in vals['name'] or '"' in vals['name'] or ';' in vals['name']:
|
||||
raise ValueError('Invalid character in column name')
|
||||
column_rename = (obj._table, item.name, vals['name'], item.index)
|
||||
|
||||
# We don't check the 'state', because it might come from the context
|
||||
|
||||
@@ -340,7 +340,7 @@ class MonetaryConverter(models.AbstractModel):
|
||||
if not options.get('display_currency') and field.type == 'monetary' and field.currency_field:
|
||||
options['display_currency'] = record[field.currency_field]
|
||||
|
||||
return self.value_to_html(record[field_name], options)
|
||||
return super(MonetaryConverter, self).record_to_html(record, field_name, options)
|
||||
|
||||
TIMEDELTA_UNITS = (
|
||||
('year', 3600 * 24 * 365),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<report id="preview_rml_report" model="res.company" name="preview.report" menu="False" rml="base/report/preview_report.rml" string="Preview RML Report"/>
|
||||
<report id="preview_rml_report" model="res.config.settings" name="preview.report" menu="False" rml="base/report/preview_report.rml" string="Preview RML Report"/>
|
||||
|
||||
<record id="view_company_form" model="ir.ui.view">
|
||||
<field name="name">res.company.form</field>
|
||||
|
||||
@@ -744,7 +744,7 @@ class GroupsView(models.Model):
|
||||
xml = E.field(E.group(*(xml1), col="2"), E.group(*(xml2), col="4"), name="groups_id", position="replace")
|
||||
xml.addprevious(etree.Comment("GENERATED AUTOMATICALLY BY GROUPS"))
|
||||
xml_content = etree.tostring(xml, pretty_print=True, xml_declaration=True, encoding="utf-8")
|
||||
view.with_context(lang=None).write({'arch': xml_content})
|
||||
view.with_context(lang=None).write({'arch': xml_content, 'arch_fs': False})
|
||||
|
||||
def get_application_groups(self, domain):
|
||||
""" Return the non-share groups that satisfy ``domain``. """
|
||||
|
||||
@@ -683,8 +683,8 @@ class TestAutoJoin(TransactionCase):
|
||||
self.assertEqual(partners, p_aa,
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
|
||||
# Test produced queries
|
||||
self.assertEqual(len(self.query_list), 3,
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') should produce 3 queries (1 in res_partner_bank, 2 on res_partner)")
|
||||
self.assertEqual(len(self.query_list), 2,
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') should produce 2 queries (1 in res_partner_bank, 1 on res_partner)")
|
||||
sql_query = self.query_list[0].get_sql()
|
||||
self.assertIn('res_partner_bank', sql_query[0],
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect main table")
|
||||
@@ -692,16 +692,16 @@ class TestAutoJoin(TransactionCase):
|
||||
expected = "%s::text like %s" % (unaccent('"res_partner_bank"."sanitized_acc_number"'), unaccent('%s'))
|
||||
self.assertIn(expected, sql_query[1],
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect where condition")
|
||||
|
||||
|
||||
self.assertEqual(['%' + name_test + '%'], sql_query[2],
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect parameter")
|
||||
sql_query = self.query_list[2].get_sql()
|
||||
sql_query = self.query_list[1].get_sql()
|
||||
self.assertIn('res_partner', sql_query[0],
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') third query incorrect main table")
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') second query incorrect main table")
|
||||
self.assertIn('"res_partner"."id" in (%s)', sql_query[1],
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') third query incorrect where condition")
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') second query incorrect where condition")
|
||||
self.assertIn(p_aa.id, sql_query[2],
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') third query incorrect parameter")
|
||||
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') second query incorrect parameter")
|
||||
|
||||
# Do: cascaded one2many without _auto_join
|
||||
self._reinit_mock()
|
||||
@@ -710,8 +710,8 @@ class TestAutoJoin(TransactionCase):
|
||||
self.assertEqual(partners, p_a + p_b,
|
||||
"_auto_join off: ('child_ids.bank_ids.id', 'in', [..]): incorrect result")
|
||||
# Test produced queries
|
||||
self.assertEqual(len(self.query_list), 5,
|
||||
"_auto_join off: ('child_ids.bank_ids.id', 'in', [..]) should produce 5 queries (1 in res_partner_bank, 4 on res_partner)")
|
||||
self.assertEqual(len(self.query_list), 3,
|
||||
"_auto_join off: ('child_ids.bank_ids.id', 'in', [..]) should produce 3 queries (1 in res_partner_bank, 2 on res_partner)")
|
||||
|
||||
# Do: one2many with _auto_join
|
||||
patch_auto_join(partner_obj, 'bank_ids', True)
|
||||
@@ -943,7 +943,7 @@ class TestAutoJoin(TransactionCase):
|
||||
self.assertLessEqual(p_a + p_b, partners,
|
||||
"_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result")
|
||||
# Test produced queries
|
||||
self.assertEqual(len(self.query_list), 5,
|
||||
self.assertEqual(len(self.query_list), 4,
|
||||
"_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect")
|
||||
|
||||
# Do: ('child_ids.state_id.country_id.code', 'like', '..') with _auto_join
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import mute_logger
|
||||
import odoo.tests.common as common
|
||||
|
||||
@@ -181,3 +181,112 @@ class TestServerActions(TestServerActionsBase):
|
||||
self.action.write({
|
||||
'child_ids': [(6, 0, [self.action.id])]
|
||||
})
|
||||
|
||||
|
||||
class TestCustomFields(common.TransactionCase):
|
||||
MODEL = 'res.partner'
|
||||
|
||||
def setUp(self):
|
||||
# use a test cursor instead of a real cursor
|
||||
registry = odoo.registry()
|
||||
registry.enter_test_mode()
|
||||
fnames = set(registry[self.MODEL]._fields)
|
||||
|
||||
@self.addCleanup
|
||||
def callback():
|
||||
registry.leave_test_mode()
|
||||
# the tests may have modified the registry, reset it
|
||||
with registry.cursor() as cr:
|
||||
registry.clear_manual_fields()
|
||||
registry.setup_models(cr)
|
||||
assert set(registry[self.MODEL]._fields) == fnames
|
||||
|
||||
super(TestCustomFields, self).setUp()
|
||||
|
||||
# do not reload the registry after removing a field
|
||||
self.env = self.env(context={'_force_unlink': True})
|
||||
|
||||
def create_field(self, name):
|
||||
""" create a custom field and return it """
|
||||
model = self.env['ir.model'].search([('model', '=', self.MODEL)])
|
||||
field = self.env['ir.model.fields'].create({
|
||||
'model_id': model.id,
|
||||
'name': name,
|
||||
'field_description': name,
|
||||
'ttype': 'char',
|
||||
})
|
||||
self.assertIn(name, self.env[self.MODEL]._fields)
|
||||
return field
|
||||
|
||||
def create_view(self, name):
|
||||
""" create a view with the given field name """
|
||||
return self.env['ir.ui.view'].create({
|
||||
'name': 'yet another view',
|
||||
'model': self.MODEL,
|
||||
'arch': '<tree string="X"><field name="%s"/></tree>' % name,
|
||||
})
|
||||
|
||||
def test_create_custom(self):
|
||||
""" custom field names must be start with 'x_' """
|
||||
with self.assertRaises(ValidationError):
|
||||
self.create_field('foo')
|
||||
|
||||
def test_rename_custom(self):
|
||||
""" custom field names must be start with 'x_' """
|
||||
field = self.create_field('x_foo')
|
||||
with self.assertRaises(ValidationError):
|
||||
field.name = 'foo'
|
||||
|
||||
def test_create_valid(self):
|
||||
""" field names must be valid pg identifiers """
|
||||
with self.assertRaises(ValidationError):
|
||||
self.create_field('x_foo bar')
|
||||
|
||||
def test_rename_valid(self):
|
||||
""" field names must be valid pg identifiers """
|
||||
field = self.create_field('x_foo')
|
||||
with self.assertRaises(ValidationError):
|
||||
field.name = 'x_foo bar'
|
||||
|
||||
def test_create_unique(self):
|
||||
""" one cannot create two fields with the same name on a given model """
|
||||
self.create_field('x_foo')
|
||||
with self.assertRaises(ValidationError):
|
||||
self.create_field('x_foo')
|
||||
|
||||
def test_rename_unique(self):
|
||||
""" one cannot create two fields with the same name on a given model """
|
||||
field1 = self.create_field('x_foo')
|
||||
field2 = self.create_field('x_bar')
|
||||
with self.assertRaises(ValidationError):
|
||||
field2.name = field1.name
|
||||
|
||||
def test_remove_without_view(self):
|
||||
""" try removing a custom field that does not occur in views """
|
||||
field = self.create_field('x_foo')
|
||||
field.unlink()
|
||||
|
||||
def test_rename_without_view(self):
|
||||
""" try renaming a custom field that does not occur in views """
|
||||
field = self.create_field('x_foo')
|
||||
field.name = 'x_bar'
|
||||
|
||||
def test_remove_with_view(self):
|
||||
""" try removing a custom field that occurs in a view """
|
||||
field = self.create_field('x_foo')
|
||||
self.create_view('x_foo')
|
||||
|
||||
# try to delete the field, this should fail but not modify the registry
|
||||
with self.assertRaises(UserError):
|
||||
field.unlink()
|
||||
self.assertIn('x_foo', self.env[self.MODEL]._fields)
|
||||
|
||||
def test_rename_with_view(self):
|
||||
""" try renaming a custom field that occurs in a view """
|
||||
field = self.create_field('x_foo')
|
||||
self.create_view('x_foo')
|
||||
|
||||
# try to delete the field, this should fail but not modify the registry
|
||||
with self.assertRaises(UserError):
|
||||
field.name = 'x_bar'
|
||||
self.assertIn('x_foo', self.env[self.MODEL]._fields)
|
||||
|
||||
@@ -17,3 +17,4 @@ access_test_new_api_company,access_test_new_api_company,model_test_new_api_compa
|
||||
access_test_new_api_company_attr,access_test_new_api_company_attr,model_test_new_api_company_attr,,1,1,1,1
|
||||
access_test_new_api_sparse,access_test_new_api_sparse,model_test_new_api_sparse,,1,1,1,1
|
||||
access_test_new_api_compute_inverse,access_test_new_api_compute_inverse,model_test_new_api_compute_inverse,,1,1,1,1
|
||||
access_test_new_api_recursive,access_test_new_api_recursive,model_test_new_api_recursive,,1,1,1,1
|
||||
|
||||
|
@@ -360,3 +360,19 @@ class Sparse(models.Model):
|
||||
char = fields.Char(sparse='data')
|
||||
selection = fields.Selection([('one', 'One'), ('two', 'Two')], sparse='data')
|
||||
partner = fields.Many2one('res.partner', sparse='data')
|
||||
|
||||
|
||||
class ComputeRecursive(models.Model):
|
||||
_name = 'test_new_api.recursive'
|
||||
|
||||
name = fields.Char(required=True)
|
||||
parent = fields.Many2one('test_new_api.recursive')
|
||||
display_name = fields.Char(compute='_compute_display_name', store=True)
|
||||
|
||||
@api.depends('name', 'parent.display_name')
|
||||
def _compute_display_name(self):
|
||||
for rec in self:
|
||||
if rec.parent:
|
||||
rec.display_name = rec.parent.display_name + " / " + rec.name
|
||||
else:
|
||||
rec.display_name = rec.name
|
||||
|
||||
@@ -179,6 +179,29 @@ class TestFields(common.TransactionCase):
|
||||
cath.parent = finn
|
||||
self.assertEqual(ewan.display_name, "Gabriel / Finnley / Catherine / Ewan")
|
||||
|
||||
def test_12_recursive_recompute(self):
|
||||
""" test recomputation on recursively dependent field """
|
||||
a = self.env['test_new_api.recursive'].create({'name': 'A'})
|
||||
b = self.env['test_new_api.recursive'].create({'name': 'B', 'parent': a.id})
|
||||
c = self.env['test_new_api.recursive'].create({'name': 'C', 'parent': b.id})
|
||||
d = self.env['test_new_api.recursive'].create({'name': 'D', 'parent': c.id})
|
||||
self.assertEqual(a.display_name, 'A')
|
||||
self.assertEqual(b.display_name, 'A / B')
|
||||
self.assertEqual(c.display_name, 'A / B / C')
|
||||
self.assertEqual(d.display_name, 'A / B / C / D')
|
||||
|
||||
b.parent = False
|
||||
self.assertEqual(a.display_name, 'A')
|
||||
self.assertEqual(b.display_name, 'B')
|
||||
self.assertEqual(c.display_name, 'B / C')
|
||||
self.assertEqual(d.display_name, 'B / C / D')
|
||||
|
||||
b.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')
|
||||
|
||||
def test_12_cascade(self):
|
||||
""" test computed field depending on computed field """
|
||||
message = self.env.ref('test_new_api.message_0_0')
|
||||
|
||||
@@ -708,6 +708,7 @@ class Field(object):
|
||||
model._field_triggers.add(field, (self, path_str))
|
||||
elif path:
|
||||
self.recursive = True
|
||||
model._field_triggers.add(field, (self, '.'.join(path)))
|
||||
|
||||
############################################################################
|
||||
#
|
||||
|
||||
+3
-2
@@ -669,7 +669,7 @@ class BaseModel(object):
|
||||
field = cls._fields.get(name)
|
||||
if not field:
|
||||
_logger.warning("method %s.%s: @constrains parameter %r is not a field name", cls._name, attr, name)
|
||||
if not (field.store or field.inverse):
|
||||
elif not (field.store or field.inverse):
|
||||
_logger.warning("method %s.%s: @constrains parameter %r is not writeable", cls._name, attr, name)
|
||||
methods.append(func)
|
||||
|
||||
@@ -4342,7 +4342,8 @@ class BaseModel(object):
|
||||
"""
|
||||
self.ensure_one()
|
||||
vals = self.copy_data(default)[0]
|
||||
new = self.create(vals)
|
||||
# To avoid to create a translation in the lang of the user, copy_translation will do it
|
||||
new = self.with_context(lang=None).create(vals)
|
||||
self.copy_translations(new)
|
||||
return new
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ class Graph(dict):
|
||||
info = odoo.modules.module.load_information_from_description_file(module)
|
||||
if info and info['installable']:
|
||||
packages.append((module, info)) # TODO directly a dict, like in get_modules_with_version
|
||||
else:
|
||||
elif module != 'studio_customization':
|
||||
_logger.warning('module %s: not installable, skipped', module)
|
||||
|
||||
dependencies = dict([(p, info['depends']) for p, info in packages])
|
||||
@@ -112,10 +112,7 @@ class Graph(dict):
|
||||
unmet_deps = filter(lambda p: p not in self, dependencies[package])
|
||||
_logger.error('module %s: Unmet dependencies: %s', package, ', '.join(unmet_deps))
|
||||
|
||||
result = len(self) - len_graph
|
||||
if result != len(module_list):
|
||||
_logger.warning('Some modules were not loaded.')
|
||||
return result
|
||||
return len(self) - len_graph
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
|
||||
@@ -302,7 +302,7 @@ def load_information_from_description_file(module, mod_path=None):
|
||||
:param mod_path: Physical path of module, if not providedThe name of the module (sale, purchase, ...)
|
||||
"""
|
||||
if not mod_path:
|
||||
mod_path = get_module_path(module)
|
||||
mod_path = get_module_path(module, downloaded=True)
|
||||
manifest_file = module_manifest(mod_path)
|
||||
if manifest_file:
|
||||
# default values for descriptor
|
||||
|
||||
+13
-10
@@ -876,8 +876,7 @@ class expression(object):
|
||||
# Making search easier when there is a left operand as one2many or many2many
|
||||
elif len(path) > 1 and field.store and field.type in ('many2many', 'one2many'):
|
||||
right_ids = comodel.search([('.'.join(path[1:]), operator, right)]).ids
|
||||
table_ids = model.with_context(active_test=False).search([(path[0], 'in', right_ids)]).ids
|
||||
leaf.leaf = ('id', 'in', table_ids)
|
||||
leaf.leaf = (path[0], 'in', right_ids)
|
||||
push(leaf)
|
||||
|
||||
elif not field.store:
|
||||
@@ -980,15 +979,16 @@ class expression(object):
|
||||
rel_table, rel_id1, rel_id2 = field.relation, field.column1, field.column2
|
||||
|
||||
if operator in HIERARCHY_FUNCS:
|
||||
def _rec_convert(ids):
|
||||
if comodel == model:
|
||||
return ids
|
||||
return select_from_where(cr, rel_id1, rel_table, rel_id2, ids, operator)
|
||||
|
||||
ids2 = to_ids(right, comodel)
|
||||
dom = HIERARCHY_FUNCS[operator]('id', ids2, comodel)
|
||||
ids2 = comodel.search(dom).ids
|
||||
push(create_substitution_leaf(leaf, ('id', 'in', _rec_convert(ids2)), model))
|
||||
if comodel == model:
|
||||
push(create_substitution_leaf(leaf, ('id', 'in', ids2), model))
|
||||
else:
|
||||
subquery = 'SELECT "%s" FROM "%s" WHERE "%s" IN %%s' % (rel_id1, rel_table, rel_id2)
|
||||
# avoid flattening of argument in to_sql()
|
||||
subquery = cr.mogrify(subquery, [tuple(ids2)])
|
||||
push(create_substitution_leaf(leaf, ('id', 'inselect', (subquery, [])), internal=True))
|
||||
else:
|
||||
call_null_m2m = True
|
||||
if right is not False:
|
||||
@@ -1014,8 +1014,11 @@ class expression(object):
|
||||
operator = 'in' # operator changed because ids are directly related to main object
|
||||
else:
|
||||
call_null_m2m = False
|
||||
m2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
|
||||
push(create_substitution_leaf(leaf, ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]), model))
|
||||
subop = 'not inselect' if operator in NEGATIVE_TERM_OPERATORS else 'inselect'
|
||||
subquery = 'SELECT "%s" FROM "%s" WHERE "%s" IN %%s' % (rel_id1, rel_table, rel_id2)
|
||||
# avoid flattening of argument in to_sql()
|
||||
subquery = cr.mogrify(subquery, [tuple(filter(None, res_ids))])
|
||||
push(create_substitution_leaf(leaf, ('id', subop, (subquery, [])), internal=True))
|
||||
|
||||
if call_null_m2m:
|
||||
m2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'
|
||||
|
||||
Reference in New Issue
Block a user