[MERGE] forward port branch saas-18 up to d15a31ae6d
This commit is contained in:
@@ -431,6 +431,18 @@ class AccountInvoice(models.Model):
|
||||
sequence = journal_sequence._get_current_sequence()
|
||||
sequence.number_next = int(result.group(2))
|
||||
|
||||
@api.multi
|
||||
def _get_printed_report_name(self):
|
||||
self.ensure_one()
|
||||
return self.type == 'out_invoice' and self.state == 'draft' and _('Draft Invoice') or \
|
||||
self.type == 'out_invoice' and self.state in ('open','paid') and _('Invoice - %s') % (self.number) or \
|
||||
self.type == 'out_refund' and self.state == 'draft' and _('Credit Note') or \
|
||||
self.type == 'out_refund' and _('Credit Note - %s') % (self.number) or \
|
||||
self.type == 'in_invoice' and self.state == 'draft' and _('Vendor Bill') or \
|
||||
self.type == 'in_invoice' and self.state in ('open','paid') and _('Vendor Bill - %s') % (self.number) or \
|
||||
self.type == 'in_refund' and self.state == 'draft' and _('Vendor Credit Note') or \
|
||||
self.type == 'in_refund' and _('Vendor Credit Note - %s') % (self.number)
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
onchanges = {
|
||||
@@ -551,7 +563,8 @@ class AccountInvoice(models.Model):
|
||||
default_template_id=template and template.id or False,
|
||||
default_composition_mode='comment',
|
||||
mark_invoice_as_sent=True,
|
||||
custom_layout="account.mail_template_data_notification_email_account_invoice"
|
||||
custom_layout="account.mail_template_data_notification_email_account_invoice",
|
||||
force_email=True
|
||||
)
|
||||
return {
|
||||
'name': _('Compose Email'),
|
||||
|
||||
@@ -124,7 +124,7 @@ var AccountSetupBarModel = KanbanModel.extend({
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @returns {Deferred -> Object} resolves to the required dashboard data
|
||||
* @returns {Deferred<Object>} resolves to the required dashboard data
|
||||
*/
|
||||
_fetchDashboardData: function () {
|
||||
return $.when(this._rpc({
|
||||
@@ -136,7 +136,7 @@ var AccountSetupBarModel = KanbanModel.extend({
|
||||
/**
|
||||
* @private
|
||||
* @param {Deferred} super_def a deferred that resolves with a dataPoint id
|
||||
* @returns {Deferred -> string} resolves to the dataPoint id
|
||||
* @returns {Deferred<string>} resolves to the dataPoint id
|
||||
*/
|
||||
_loadDashboard: function (super_def) {
|
||||
var self = this;
|
||||
|
||||
@@ -12,7 +12,7 @@ var _t = core._t;
|
||||
* Model use to fetch, format and update 'account.bank.statement' and
|
||||
* 'account.bank.statement.line' datas allowing reconciliation
|
||||
*
|
||||
* The statement internal structure:
|
||||
* The statement internal structure::
|
||||
*
|
||||
* {
|
||||
* valuenow: integer
|
||||
@@ -25,7 +25,7 @@ var _t = core._t;
|
||||
* accounts: {id: code}
|
||||
* }
|
||||
*
|
||||
* The internal structure of each line is:
|
||||
* The internal structure of each line is::
|
||||
*
|
||||
* {
|
||||
* balance: {
|
||||
@@ -114,7 +114,7 @@ var StatementModel = BasicModel.extend({
|
||||
* account type
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {number} mv_lines id
|
||||
* @param {number} mv_line_id
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
addProposition: function (handle, mv_line_id) {
|
||||
@@ -167,13 +167,15 @@ var StatementModel = BasicModel.extend({
|
||||
* change the mode line ('inactive', 'match', 'create'), and fetch the new
|
||||
* matched lines or prepare to create a new line
|
||||
*
|
||||
* 'match': display the matched lines, the user can select the lines to
|
||||
* apply there as proposition
|
||||
* 'create': display fields and quick create button to create a new
|
||||
* proposition for the reconciliation
|
||||
* ``match``
|
||||
* display the matched lines, the user can select the lines to apply
|
||||
* there as proposition
|
||||
* ``create``
|
||||
* display fields and quick create button to create a new proposition
|
||||
* for the reconciliation
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {string} mode
|
||||
* @param {'inactive' | 'match' | 'create'} mode
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
changeMode: function (handle, mode) {
|
||||
@@ -316,7 +318,7 @@ var StatementModel = BasicModel.extend({
|
||||
/**
|
||||
* get the line data for this handle
|
||||
*
|
||||
* @param {OdooEvent} event
|
||||
* @param {Object} handle
|
||||
* @returns {Object}
|
||||
*/
|
||||
getLine: function (handle) {
|
||||
@@ -324,6 +326,7 @@ var StatementModel = BasicModel.extend({
|
||||
},
|
||||
/**
|
||||
* load data from
|
||||
*
|
||||
* - 'account.bank.statement' fetch the line id and bank_statement_id info
|
||||
* - 'account.reconcile.model' fetch all reconcile model (for quick add)
|
||||
* - 'account.account' fetch all account code
|
||||
@@ -430,7 +433,7 @@ var StatementModel = BasicModel.extend({
|
||||
* 'amount_type'
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {integer} reconcile model id
|
||||
* @param {integer} reconcileModelId
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
quickCreateProposition: function (handle, reconcileModelId) {
|
||||
@@ -460,7 +463,7 @@ var StatementModel = BasicModel.extend({
|
||||
* Remove a proposition and switch to an active mode ('create' or 'match')
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {number} proposition id (move line id)
|
||||
* @param {number} id (move line id)
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
removeProposition: function (handle, id) {
|
||||
@@ -519,7 +522,7 @@ var StatementModel = BasicModel.extend({
|
||||
* Then the total is recomputed to have 100%.
|
||||
*
|
||||
* @param {string} handle
|
||||
* @param {number[]} context.statement_ids
|
||||
* @param {*} values
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
updateProposition: function (handle, values) {
|
||||
@@ -636,7 +639,7 @@ var StatementModel = BasicModel.extend({
|
||||
* stop the editable proposition line and remove it if it's invalid then
|
||||
* compute the line
|
||||
*
|
||||
* @see '_computeLine'
|
||||
* See :func:`_computeLine`
|
||||
*
|
||||
* @private
|
||||
* @param {string} handle
|
||||
@@ -679,7 +682,7 @@ var StatementModel = BasicModel.extend({
|
||||
* Check the taxes server side for each updated propositions with tax_id
|
||||
*
|
||||
* @private
|
||||
* @param {Object}
|
||||
* @param {Object} line
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
_computeLine: function (line) {
|
||||
@@ -771,7 +774,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {string} handle
|
||||
* @param {integer} reconcile model id
|
||||
* @param {integer} reconcileModelId
|
||||
*/
|
||||
_computeReconcileModels: function (handle, reconcileModelId) {
|
||||
var line = this.getLine(handle);
|
||||
@@ -788,8 +791,7 @@ var StatementModel = BasicModel.extend({
|
||||
* format a name_get into an object {id, display_name}, idempotent
|
||||
*
|
||||
* @private
|
||||
* @param {Object|Array} data or name_get
|
||||
* @param {Object|false} {id, display_name}
|
||||
* @param {Object|Array} [value] data or name_get
|
||||
*/
|
||||
_formatNameGet: function (value) {
|
||||
return value ? (value.id ? value : {'id': value[0], 'display_name': value[1]}) : false;
|
||||
@@ -798,8 +800,8 @@ var StatementModel = BasicModel.extend({
|
||||
* Format each propositions (amount, label, account_id)
|
||||
*
|
||||
* @private
|
||||
* @param {Object}
|
||||
* @param {Object[]}
|
||||
* @param {Object} line
|
||||
* @param {Object[]} props
|
||||
*/
|
||||
_formatLineProposition: function (line, props) {
|
||||
var self = this;
|
||||
@@ -818,7 +820,7 @@ var StatementModel = BasicModel.extend({
|
||||
* @see '_computeLine'
|
||||
*
|
||||
* @private
|
||||
* @param {Object[]}
|
||||
* @param {Object[]} lines
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
_formatLine: function (lines) {
|
||||
@@ -845,7 +847,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {string} handle
|
||||
* @param {Object[]}
|
||||
* @param {Object[]} mv_lines
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
_formatMoveLine: function (handle, mv_lines) {
|
||||
@@ -869,8 +871,8 @@ var StatementModel = BasicModel.extend({
|
||||
* base_amount with the decimal number from the currency
|
||||
*
|
||||
* @private
|
||||
* @param {Object}
|
||||
* @param {Object}
|
||||
* @param {Object} line
|
||||
* @param {Object} values
|
||||
* @returns {Object}
|
||||
*/
|
||||
_formatQuickCreate: function (line, values) {
|
||||
@@ -980,7 +982,7 @@ var StatementModel = BasicModel.extend({
|
||||
*
|
||||
* @private
|
||||
* @param {object} line
|
||||
* @param {object} proposition
|
||||
* @param {object} prop
|
||||
* @returns {object}
|
||||
*/
|
||||
_formatToProcessReconciliation: function (line, prop) {
|
||||
@@ -1073,7 +1075,7 @@ var ManualModel = StatementModel.extend({
|
||||
return self._rpc({
|
||||
model: 'account.move.line',
|
||||
method: 'get_data_for_manual_reconciliation',
|
||||
args: ['account', model.account_ids || self.account_ids],
|
||||
args: ['account', context.account_ids || self.account_ids],
|
||||
context: context,
|
||||
})
|
||||
.then(function (result) {
|
||||
@@ -1216,7 +1218,7 @@ var ManualModel = StatementModel.extend({
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
* @param {Object}
|
||||
* @param {Object} line
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
_computeLine: function (line) {
|
||||
@@ -1237,8 +1239,8 @@ var ManualModel = StatementModel.extend({
|
||||
* @see '_computeLine'
|
||||
*
|
||||
* @private
|
||||
* @param {string} 'customers', 'suppliers' or 'accounts'
|
||||
* @param {Object}
|
||||
* @param {'customers' | 'suppliers' | 'accounts'} type
|
||||
* @param {Object} data
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
_formatLine: function (type, data) {
|
||||
@@ -1264,8 +1266,8 @@ var ManualModel = StatementModel.extend({
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
* @param {Object}
|
||||
* @param {Object}
|
||||
* @param {Object} line
|
||||
* @param {Object} props
|
||||
*/
|
||||
_formatLineProposition: function (line, props) {
|
||||
var self = this;
|
||||
@@ -1330,4 +1332,4 @@ return {
|
||||
StatementModel: StatementModel,
|
||||
ManualModel: ManualModel,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,15 +10,7 @@
|
||||
name="account.report_invoice_with_payments"
|
||||
file="account.report_invoice_with_payments"
|
||||
attachment="(object.state in ('open','paid')) and ('INV'+(object.number or '').replace('/','')+'.pdf')"
|
||||
print_report_name="(
|
||||
object.type == 'out_invoice' and object.state == 'draft' and 'Draft Invoice' or
|
||||
object.type == 'out_invoice' and object.state in ('open','paid') and 'Invoice - %s' % (object.number) or
|
||||
object.type == 'out_refund' and object.state == 'draft' and 'Credit Note' or
|
||||
object.type == 'out_refund' and 'Credit Note - %s' % (object.number) or
|
||||
object.type == 'in_invoice' and object.state == 'draft' and 'Vendor Bill' or
|
||||
object.type == 'in_invoice' and object.state in ('open','paid') and 'Vendor Bill - %s' % (object.number) or
|
||||
object.type == 'in_refund' and object.state == 'draft' and 'Vendor Credit Note' or
|
||||
object.type == 'in_refund' and 'Vendor Credit Note - %s' % (object.number))"
|
||||
print_report_name="(object._get_printed_report_name())"
|
||||
/>
|
||||
|
||||
<report
|
||||
@@ -29,15 +21,7 @@
|
||||
name="account.report_invoice"
|
||||
file="account.report_invoice"
|
||||
attachment="(object.state in ('open','paid')) and ('INV'+(object.number or '').replace('/','')+'.pdf')"
|
||||
print_report_name="(
|
||||
object.type == 'out_invoice' and object.state == 'draft' and 'Draft Invoice' or
|
||||
object.type == 'out_invoice' and object.state in ('open','paid') and 'Invoice - %s' % (object.number) or
|
||||
object.type == 'out_refund' and object.state == 'draft' and 'Credit Note' or
|
||||
object.type == 'out_refund' and 'Credit Note - %s' % (object.number) or
|
||||
object.type == 'in_invoice' and object.state == 'draft' and 'Vendor Bill' or
|
||||
object.type == 'in_invoice' and object.state in ('open','paid') and 'Vendor Bill - %s' % (object.number) or
|
||||
object.type == 'in_refund' and object.state == 'draft' and 'Vendor Credit Note' or
|
||||
object.type == 'in_refund' and 'Vendor Credit Note - %s' % (object.number))"
|
||||
print_report_name="(object._get_printed_report_name())"
|
||||
/>
|
||||
|
||||
<report
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div name="invoice_address" class="col-xs-5 col-xs-offset-7">
|
||||
<address t-field="o.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
|
||||
<div t-if="o.partner_id.vat" class="mt16">VAT: <span t-field="o.partner_id.vat"/></div>
|
||||
<div t-if="o.partner_id.vat" class="mt16"><t t-esc="o.company_id.country_id.vat_label or 'TIN'"/>: <span t-field="o.partner_id.vat"/></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<!-- Is there a discount on at least one line? -->
|
||||
<t t-set="display_discount" t-value="any([l.discount for l in o.invoice_line_ids])"/>
|
||||
|
||||
<table class="table table-condensed">
|
||||
<table class="table table-condensed" name="invoice_line_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
|
||||
@@ -182,9 +182,12 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
|
||||
});
|
||||
|
||||
return {
|
||||
// Singleton that emits barcode_scanned events on core.bus
|
||||
/** Singleton that emits barcode_scanned events on core.bus */
|
||||
BarcodeEvents: new BarcodeEvents(),
|
||||
// List of barcode prefixes that are reserved for internal purposes
|
||||
/**
|
||||
* List of barcode prefixes that are reserved for internal purposes
|
||||
* @type Array
|
||||
*/
|
||||
ReservedBarcodePrefixes: ['O-CMD'],
|
||||
};
|
||||
|
||||
|
||||
@@ -136,12 +136,13 @@ bus.Bus = Widget.extend({
|
||||
* one in the list of open tabs. This one start polling for the other. When a notification is recieved from the poll, it
|
||||
* is signaling through the localStorage too.
|
||||
*
|
||||
* localStorage used keys are :
|
||||
* - bus.channels : shared public channel list to listen during the poll
|
||||
* - bus.options : shared options
|
||||
* - bus.notification : the received notifications from the last poll
|
||||
* - bus.tab_list : list of opened tab ids
|
||||
* - bus.tab_master : generated id of the master tab
|
||||
* localStorage used keys are:
|
||||
*
|
||||
* - bus.channels : shared public channel list to listen during the poll
|
||||
* - bus.options : shared options
|
||||
* - bus.notification : the received notifications from the last poll
|
||||
* - bus.tab_list : list of opened tab ids
|
||||
* - bus.tab_master : generated id of the master tab
|
||||
*/
|
||||
var CrossTabBus = bus.Bus.extend({
|
||||
init: function(){
|
||||
|
||||
@@ -23,7 +23,7 @@ var GreetingMessage = Widget.extend({
|
||||
// to the (likely) appropriate menu, according to the user access rights
|
||||
if(!action.attendance) {
|
||||
this.activeBarcode = false;
|
||||
this.session.user_has_group('hr_attendance.group_hr_attendance_user').then(function(has_group) {
|
||||
this.getSession().user_has_group('hr_attendance.group_hr_attendance_user').then(function(has_group) {
|
||||
if(has_group) {
|
||||
self.next_action = 'hr_attendance.hr_attendance_action_kiosk_mode';
|
||||
} else {
|
||||
|
||||
@@ -67,7 +67,7 @@ var KioskConfirm = Widget.extend({
|
||||
|
||||
start: function () {
|
||||
var self = this;
|
||||
self.session.user_has_group('hr_attendance.group_hr_attendance_use_pin').then(function(has_group){
|
||||
self.getSession().user_has_group('hr_attendance.group_hr_attendance_use_pin').then(function(has_group){
|
||||
self.use_pin = has_group;
|
||||
self.$el.html(QWeb.render("HrAttendanceKioskConfirm", {widget: self}));
|
||||
self.start_clock();
|
||||
|
||||
@@ -20,7 +20,7 @@ class CalendarEvent(models.Model):
|
||||
|
||||
# sync res_model / res_id to opportunity id (aka creating meeting from lead chatter)
|
||||
if 'applicant_id' not in defaults and defaults.get('res_id') and (defaults.get('res_model') or defaults.get('res_model_id')):
|
||||
if (defaults.get('res_model') and defaults['res_model'] == 'hr.applicant') or self.env['ir.model'].sudo().browse(defaults['res_model_id']).model == 'hr.applicant':
|
||||
if (defaults.get('res_model') and defaults['res_model'] == 'hr.applicant') or (defaults.get('res_model_id') and self.env['ir.model'].sudo().browse(defaults['res_model_id']).model == 'hr.applicant'):
|
||||
defaults['applicant_id'] = defaults['res_id']
|
||||
|
||||
return defaults
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
|
||||
@@ -18,12 +18,19 @@ Sheet, now only Vertical format has been permitted Which is Supported By Odoo.
|
||||
""",
|
||||
'category': 'Localization',
|
||||
'depends': [
|
||||
'account',
|
||||
'account_tax_python',
|
||||
],
|
||||
'data': [
|
||||
'data/account_data.xml',
|
||||
'data/l10n_in_chart_data.xml',
|
||||
'data/account_tax_template_data.xml',
|
||||
'data/account_fiscal_position_data.xml',
|
||||
'data/res_country_state_data.xml',
|
||||
'data/account_chart_template_data.yml',
|
||||
'views/product_template_view.xml',
|
||||
'views/report_invoice.xml',
|
||||
'views/res_company_view.xml',
|
||||
'views/res_country_state_view.xml',
|
||||
'views/res_partner_views.xml',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -3,26 +3,17 @@
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- Account Tax Group -->
|
||||
<record id="tax_group_1" model="account.tax.group">
|
||||
<field name="name">VAT 1%</field>
|
||||
<record id="sgst_group" model="account.tax.group">
|
||||
<field name="name">SGST</field>
|
||||
</record>
|
||||
<record id="tax_group_25" model="account.tax.group">
|
||||
<field name="name">VAT 2.5%</field>
|
||||
<record id="cgst_group" model="account.tax.group">
|
||||
<field name="name">CGST</field>
|
||||
</record>
|
||||
<record id="tax_group_4" model="account.tax.group">
|
||||
<field name="name">VAT 4%</field>
|
||||
<record id="igst_group" model="account.tax.group">
|
||||
<field name="name">IGST</field>
|
||||
</record>
|
||||
<record id="tax_group_12" model="account.tax.group">
|
||||
<field name="name">VAT 12%</field>
|
||||
</record>
|
||||
<record id="tax_group_125" model="account.tax.group">
|
||||
<field name="name">VAT 12.5%</field>
|
||||
</record>
|
||||
<record id="tax_group_14" model="account.tax.group">
|
||||
<field name="name">VAT 14%</field>
|
||||
</record>
|
||||
<record id="tax_group_15" model="account.tax.group">
|
||||
<field name="name">VAT 15%</field>
|
||||
<record id="cess_group" model="account.tax.group">
|
||||
<field name="name">Cess</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Fiscal Position Templates -->
|
||||
<record model="account.fiscal.position.template" id="fiscal_position_in_inter_state">
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="name">Inter State</field>
|
||||
</record>
|
||||
|
||||
<record model="account.fiscal.position.template" id="fiscal_position_in_export">
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="name">Export</field>
|
||||
</record>
|
||||
|
||||
<!-- Fiscal Position Tax Templates -->
|
||||
<record id="account_fiscal_position_tax_in_sale_1_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_1"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_1"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_2_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_2"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_2"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_5_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_5"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_5"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_12_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_12"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_12"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_18_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_18"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_18"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_28_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_28"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_28"/>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_tax_in_purchase_1_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_1"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_1"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_2_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_2"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_2"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_5_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_5"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_5"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_12_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_12"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_12"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_18_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_18"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_18"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_28_inter" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_inter_state"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_28"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_28"/>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_tax_in_sale_1_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_1"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_1"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_2_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_2"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_2"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_5_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_5"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_5"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_12_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_12"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_12"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_18_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_18"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_18"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_sale_28_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_sale_28"/>
|
||||
<field name="tax_dest_id" ref="igst_sale_28"/>
|
||||
</record>
|
||||
|
||||
<record id="account_fiscal_position_tax_in_purchase_1_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_1"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_1"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_2_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_2"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_2"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_5_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_5"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_5"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_12_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_12"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_12"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_18_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_18"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_18"/>
|
||||
</record>
|
||||
<record id="account_fiscal_position_tax_in_purchase_28_exp" model="account.fiscal.position.tax.template">
|
||||
<field name="position_id" ref="fiscal_position_in_export"/>
|
||||
<field name="tax_src_id" ref="sgst_purchase_28"/>
|
||||
<field name="tax_dest_id" ref="igst_purchase_28"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,19 @@
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
|
||||
<record id="sgst_tag_account" model="account.account.tag">
|
||||
<field name="name">SGST</field>
|
||||
<field name="applicability">accounts</field>
|
||||
</record>
|
||||
<record id="cgst_tag_account" model="account.account.tag">
|
||||
<field name="name">CGST</field>
|
||||
<field name="applicability">accounts</field>
|
||||
</record>
|
||||
<record id="igst_tag_account" model="account.account.tag">
|
||||
<field name="name">IGST</field>
|
||||
<field name="applicability">accounts</field>
|
||||
</record>
|
||||
|
||||
<record model="account.account.template" id="p10031">
|
||||
<field name="name">Inventories</field>
|
||||
<field name="code">10031</field>
|
||||
@@ -34,47 +47,31 @@
|
||||
<field name="reconcile" eval="True"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p10050">
|
||||
<field name="name">Sales Tax Receivable</field>
|
||||
<field name="code">10050</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p10051">
|
||||
<field name="name">VAT Receivable</field>
|
||||
<field name="name">SGST Receivable</field>
|
||||
<field name="code">10051</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('sgst_tag_account'),])]"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p100521">
|
||||
<field name="name">Excise Duty Receivable</field>
|
||||
<field name="code">100521</field>
|
||||
|
||||
<record model="account.account.template" id="p10052">
|
||||
<field name="name">CGST Receivable</field>
|
||||
<field name="code">10052</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('cgst_tag_account'),])]"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p100531">
|
||||
<field name="name">Service Tax Receivable</field>
|
||||
<field name="code">100531</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p100532">
|
||||
<field name="name">Swachh Bharat Cess Receivable On Service</field>
|
||||
<field name="code">100532</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p100533">
|
||||
<field name="name">Krishi Kalyan Cess Receivable On Service</field>
|
||||
<field name="code">100533</field>
|
||||
|
||||
<record model="account.account.template" id="p10053">
|
||||
<field name="name">IGST Receivable</field>
|
||||
<field name="code">10053</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('igst_tag_account'),])]"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p10054">
|
||||
<field name="name">TDS Receivable</field>
|
||||
@@ -207,46 +204,30 @@
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p11232">
|
||||
<field name="name">VAT Payable</field>
|
||||
<field name="name">SGST Payable</field>
|
||||
<field name="code">11232</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('sgst_tag_account'),])]"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p112331">
|
||||
<field name="name">Excise Duty Payable</field>
|
||||
<field name="code">112331</field>
|
||||
|
||||
<record model="account.account.template" id="p11233">
|
||||
<field name="name">CGST Payable</field>
|
||||
<field name="code">11233</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('cgst_tag_account'),])]"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p112341">
|
||||
<field name="name">Service Tax Payable</field>
|
||||
<field name="code">112341</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p112342">
|
||||
<field name="name">Swachh Bharat Cess Payable On Service</field>
|
||||
<field name="code">112342</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p112343">
|
||||
<field name="name">Krishi Kalyan Cess Payable On Service</field>
|
||||
<field name="code">112343</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p11235">
|
||||
<field name="name">Sales Tax Payable</field>
|
||||
<field name="code">11235</field>
|
||||
|
||||
<record model="account.account.template" id="p11234">
|
||||
<field name="name">IGST Payable</field>
|
||||
<field name="code">11234</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_standard"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('igst_tag_account'),])]"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="p11241">
|
||||
<field name="name">Wages Payable</field>
|
||||
@@ -528,4 +509,5 @@
|
||||
<field name="income_currency_exchange_account_id" ref="p2013"/>
|
||||
<field name="expense_currency_exchange_account_id" ref="p2117"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="base.state_in_an" model="res.country.state">
|
||||
<field name="l10n_in_tin">35</field>
|
||||
</record>
|
||||
<record id="base.state_in_ap" model="res.country.state">
|
||||
<field name="l10n_in_tin">28</field>
|
||||
</record>
|
||||
<record id="base.state_in_ar" model="res.country.state">
|
||||
<field name="l10n_in_tin">12</field>
|
||||
</record>
|
||||
<record id="base.state_in_as" model="res.country.state">
|
||||
<field name="l10n_in_tin">18</field>
|
||||
</record>
|
||||
<record id="base.state_in_br" model="res.country.state">
|
||||
<field name="l10n_in_tin">10</field>
|
||||
</record>
|
||||
<record id="base.state_in_ch" model="res.country.state">
|
||||
<field name="l10n_in_tin">04</field>
|
||||
</record>
|
||||
<record id="base.state_in_cg" model="res.country.state">
|
||||
<field name="l10n_in_tin">22</field>
|
||||
</record>
|
||||
<record id="base.state_in_dn" model="res.country.state">
|
||||
<field name="l10n_in_tin">26</field>
|
||||
</record>
|
||||
<record id="base.state_in_dd" model="res.country.state">
|
||||
<field name="l10n_in_tin">25</field>
|
||||
</record>
|
||||
<record id="base.state_in_dl" model="res.country.state">
|
||||
<field name="l10n_in_tin">07</field>
|
||||
</record>
|
||||
<record id="base.state_in_ga" model="res.country.state">
|
||||
<field name="l10n_in_tin">30</field>
|
||||
</record>
|
||||
<record id="base.state_in_gj" model="res.country.state">
|
||||
<field name="l10n_in_tin">24</field>
|
||||
</record>
|
||||
<record id="base.state_in_hr" model="res.country.state">
|
||||
<field name="l10n_in_tin">06</field>
|
||||
</record>
|
||||
<record id="base.state_in_hp" model="res.country.state">
|
||||
<field name="l10n_in_tin">02</field>
|
||||
</record>
|
||||
<record id="base.state_in_jk" model="res.country.state">
|
||||
<field name="l10n_in_tin">01</field>
|
||||
</record>
|
||||
<record id="base.state_in_jh" model="res.country.state">
|
||||
<field name="l10n_in_tin">20</field>
|
||||
</record>
|
||||
<record id="base.state_in_ka" model="res.country.state">
|
||||
<field name="l10n_in_tin">29</field>
|
||||
</record>
|
||||
<record id="base.state_in_kl" model="res.country.state">
|
||||
<field name="l10n_in_tin">32</field>
|
||||
</record>
|
||||
<record id="base.state_in_ld" model="res.country.state">
|
||||
<field name="l10n_in_tin">31</field>
|
||||
</record>
|
||||
<record id="base.state_in_mp" model="res.country.state">
|
||||
<field name="l10n_in_tin">23</field>
|
||||
</record>
|
||||
<record id="base.state_in_mh" model="res.country.state">
|
||||
<field name="l10n_in_tin">27</field>
|
||||
</record>
|
||||
<record id="base.state_in_mn" model="res.country.state">
|
||||
<field name="l10n_in_tin">14</field>
|
||||
</record>
|
||||
<record id="base.state_in_ml" model="res.country.state">
|
||||
<field name="l10n_in_tin">17</field>
|
||||
</record>
|
||||
<record id="base.state_in_mz" model="res.country.state">
|
||||
<field name="l10n_in_tin">15</field>
|
||||
</record>
|
||||
<record id="base.state_in_nl" model="res.country.state">
|
||||
<field name="l10n_in_tin">13</field>
|
||||
</record>
|
||||
<record id="base.state_in_or" model="res.country.state">
|
||||
<field name="l10n_in_tin">21</field>
|
||||
</record>
|
||||
<record id="base.state_in_py" model="res.country.state">
|
||||
<field name="l10n_in_tin">34</field>
|
||||
</record>
|
||||
<record id="base.state_in_pb" model="res.country.state">
|
||||
<field name="l10n_in_tin">03</field>
|
||||
</record>
|
||||
<record id="base.state_in_rj" model="res.country.state">
|
||||
<field name="l10n_in_tin">08</field>
|
||||
</record>
|
||||
<record id="base.state_in_sk" model="res.country.state">
|
||||
<field name="l10n_in_tin">11</field>
|
||||
</record>
|
||||
<record id="base.state_in_tn" model="res.country.state">
|
||||
<field name="l10n_in_tin">33</field>
|
||||
</record>
|
||||
<record id="base.state_in_ts" model="res.country.state">
|
||||
<field name="l10n_in_tin">36</field>
|
||||
</record>
|
||||
<record id="base.state_in_tr" model="res.country.state">
|
||||
<field name="l10n_in_tin">16</field>
|
||||
</record>
|
||||
<record id="base.state_in_up" model="res.country.state">
|
||||
<field name="l10n_in_tin">09</field>
|
||||
</record>
|
||||
<record id="base.state_in_uk" model="res.country.state">
|
||||
<field name="l10n_in_tin">05</field>
|
||||
</record>
|
||||
<record id="base.state_in_wb" model="res.country.state">
|
||||
<field name="l10n_in_tin">19</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,7 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_invoice
|
||||
from . import chart_template
|
||||
from . import product_template
|
||||
from . import res_country_state
|
||||
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
|
||||
_inherit = "account.invoice"
|
||||
|
||||
@api.depends('amount_total')
|
||||
def _compute_amount_total_words(self):
|
||||
for invoice in self:
|
||||
invoice.amount_total_words = invoice.currency_id.amount_to_text(invoice.amount_total)
|
||||
|
||||
amount_total_words = fields.Char("Total (In Words)", compute="_compute_amount_total_words")
|
||||
|
||||
def _get_printed_report_name(self):
|
||||
self.ensure_one()
|
||||
if self.company_id.country_id.code != 'IN':
|
||||
return super(AccountInvoice, self)._get_printed_report_name()
|
||||
return self.type == 'out_invoice' and self.state == 'draft' and _('Draft %s') % (self.journal_id.name) or \
|
||||
self.type == 'out_invoice' and self.state in ('open','paid') and '%s - %s' % (self.journal_id.name, self.number) or \
|
||||
self.type == 'out_refund' and self.state == 'draft' and _('Credit Note') or \
|
||||
self.type == 'out_refund' and _('Credit Note - %s') % (self.number) or \
|
||||
self.type == 'in_invoice' and self.state == 'draft' and _('Vendor Bill') or \
|
||||
self.type == 'in_invoice' and self.state in ('open','paid') and _('Vendor Bill - %s') % (self.number) or \
|
||||
self.type == 'in_refund' and self.state == 'draft' and _('Vendor Credit Note') or \
|
||||
self.type == 'in_refund' and _('Vendor Credit Note - %s') % (self.number)
|
||||
|
||||
@api.multi
|
||||
def _invoice_line_tax_values(self):
|
||||
self.ensure_one()
|
||||
tax_datas = {}
|
||||
TAX = self.env['account.tax']
|
||||
|
||||
for line in self.mapped('invoice_line_ids'):
|
||||
price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||
tax_lines = line.invoice_line_tax_ids.compute_all(price_unit, line.invoice_id.currency_id, line.quantity, line.product_id, line.invoice_id.partner_id)['taxes']
|
||||
for tax_line in tax_lines:
|
||||
tax_line['tag_ids'] = TAX.browse(tax_line['id']).tag_ids.ids
|
||||
tax_datas[line.id] = tax_lines
|
||||
return tax_datas
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models, _
|
||||
|
||||
|
||||
class AccountChartTemplate(models.Model):
|
||||
_inherit = 'account.chart.template'
|
||||
|
||||
|
||||
@api.multi
|
||||
def _prepare_all_journals(self, acc_template_ref, company, journals_dict=None):
|
||||
res = super(AccountChartTemplate, self)._prepare_all_journals(
|
||||
acc_template_ref, company, journals_dict=journals_dict)
|
||||
if not self == self.env.ref('l10n_in.indian_chart_template_standard'):
|
||||
return res
|
||||
for journal in res:
|
||||
if journal['code'] == 'INV':
|
||||
journal['name'] = _('Tax Invoices')
|
||||
|
||||
res += [{'type': 'sale', 'name': _('Retail Invoices'), 'code': 'RETINV', 'company_id': company.id, 'show_on_dashboard': True,},
|
||||
{'type': 'sale', 'name': _('Export Invoices'), 'code': 'EXPINV', 'company_id': company.id, 'show_on_dashboard': True,},]
|
||||
return res
|
||||
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = 'product.template'
|
||||
|
||||
l10n_in_hsn_code = fields.Char(string="HSN/SAC Code", help="Harmonized System Nomenclature/Services Accounting Code")
|
||||
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class CountryState(models.Model):
|
||||
_inherit = 'res.country.state'
|
||||
|
||||
l10n_in_tin = fields.Char('TIN Number', size=2, help="TIN number-first two digits")
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record model="ir.ui.view" id="l10n_in.product_template_hs_code">
|
||||
<field name="name">l10n_in.product.template.form.hs_code</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="categ_id" position="after">
|
||||
<field name="l10n_in_hsn_code"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="l10n_in_report_invoice_document_inherit" inherit_id="account.report_invoice_document">
|
||||
|
||||
<xpath expr="//table[@name='invoice_line_table']" position="before">
|
||||
<t t-if="o.company_id.country_id.code == 'IN'">
|
||||
<!-- Tag wise Tax Calculation -->
|
||||
<t t-set="tag_cgst_id" t-value="env.ref('l10n_in.cgst_tag_tax').id"/>
|
||||
<t t-set="tag_sgst_id" t-value="env.ref('l10n_in.sgst_tag_tax').id"/>
|
||||
<t t-set="tag_igst_id" t-value="env.ref('l10n_in.igst_tag_tax').id"/>
|
||||
<t t-set="tag_cess_id" t-value="env.ref('l10n_in.cess_tag_tax').id"/>
|
||||
<t t-set="tax_map" t-value="o.tax_line_ids.mapped('tax_id.tag_ids.id')"/>
|
||||
<t t-set="is_igst" t-value="tag_igst_id in tax_map"/>
|
||||
<t t-set="is_cgst" t-value="tag_cgst_id in tax_map"/>
|
||||
<t t-set="is_cess" t-value="tag_cess_id in tax_map"/>
|
||||
<t t-set="tax_datas" t-value="o._invoice_line_tax_values()"/>
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//p[@t-if='o.comment']" position="before">
|
||||
<t t-if="o.company_id.country_id.code == 'IN'">
|
||||
<p id="total_in_words" class="mb16">
|
||||
<strong>Total (In Words): </strong>
|
||||
<span t-field="o.amount_total_words"/>
|
||||
</p>
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//table[@name='invoice_line_table']" position="replace">
|
||||
<table class="table table-condensed" name="invoice_line_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th class="hidden">Source Document</th>
|
||||
<th class="text-right">Quantity</th>
|
||||
<th class="text-right">Unit Price</th>
|
||||
<th t-if="display_discount" class="text-right">Disc.(%)</th>
|
||||
<t t-if="len(o.tax_line_ids) > 0">
|
||||
<th class="text-right">Taxes</th>
|
||||
</t>
|
||||
<th class="text-right">Amount</th>
|
||||
<t t-if="o.company_id.country_id.code == 'IN'">
|
||||
<th class="text-right" t-if="is_cgst">CGST</th>
|
||||
<th class="text-right" t-if="is_cgst">SGST</th>
|
||||
<th class="text-right" t-if="is_igst">IGST</th>
|
||||
<th class="text-right" t-if="is_cess">CESS</th>
|
||||
</t>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="invoice_tbody">
|
||||
<tr t-foreach="o.invoice_line_ids" t-as="l">
|
||||
<td><span t-field="l.name"/><t t-if="l.product_id.l10n_in_hsn_code"><h6><strong class="ml16">HSN/SAC Code:</strong> <span t-field="l.product_id.l10n_in_hsn_code"/></h6></t></td>
|
||||
<td class="hidden"><span t-field="l.origin"/></td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.quantity"/>
|
||||
<span t-field="l.uom_id" groups="product.group_uom"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_unit"/>
|
||||
</td>
|
||||
<td t-if="display_discount" class="text-right">
|
||||
<span t-field="l.discount"/>
|
||||
</td>
|
||||
<t t-if="len(o.tax_line_ids) > 0">
|
||||
<td class="text-right">
|
||||
<span t-esc="', '.join(map(lambda x: (x.description or x.name), l.invoice_line_tax_ids))"/>
|
||||
</td>
|
||||
</t>
|
||||
<td class="text-right" id="subtotal">
|
||||
<span t-field="l.price_subtotal"
|
||||
t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
</td>
|
||||
<t t-if="o.company_id.country_id.code == 'IN'">
|
||||
<td class="text-right" t-if="is_cgst">
|
||||
<t t-foreach="tax_datas[l.id]" t-as="tax_data">
|
||||
<t t-if="tag_cgst_id in tax_data['tag_ids']">
|
||||
<t t-esc="tax_data['amount']"/>
|
||||
</t>
|
||||
</t>
|
||||
</td>
|
||||
<td class="text-right" t-if="is_cgst">
|
||||
<t t-foreach="tax_datas[l.id]" t-as="tax_data">
|
||||
<t t-if="tag_sgst_id in tax_data['tag_ids']">
|
||||
<t t-esc="tax_data['amount']"/>
|
||||
</t>
|
||||
</t>
|
||||
</td>
|
||||
<td class="text-right" t-if="is_igst">
|
||||
<t t-foreach="tax_datas[l.id]" t-as="tax_data">
|
||||
<t t-if="tag_igst_id in tax_data['tag_ids']">
|
||||
<t t-esc="tax_data['amount']"/>
|
||||
</t>
|
||||
</t>
|
||||
</td>
|
||||
<t t-set="total_cess" t-value="0.0"/>
|
||||
<td class="text-right" t-if="is_cess">
|
||||
<t t-foreach="tax_datas[l.id]" t-as="tax_data">
|
||||
<t t-if="tag_cess_id in tax_data['tag_ids']">
|
||||
<t t-set="total_cess" t-value="total_cess + tax_data['amount']"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-esc="total_cess"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr t-foreach="range(max(5-len(o.invoice_line_ids),0))" t-as="l">
|
||||
<td>&nbsp;</td>
|
||||
<td class="hidden"></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td t-if="display_discount" groups="sale.group_discount_per_so_line"></td>
|
||||
<td t-att-colspan="o.company_id.country_id.code == 'IN' and 5 or 1"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//h2" position="replace">
|
||||
<h2>
|
||||
<span t-if="o.type == 'out_invoice' and (o.state == 'open' or o.state == 'paid')" t-field="o.journal_id.name"/>
|
||||
<span t-if="o.type == 'out_invoice' and o.state == 'draft'">Draft <span t-field="o.journal_id.name"/></span>
|
||||
<span t-if="o.type == 'out_invoice' and o.state == 'cancel'">Cancelled <span t-field="o.journal_id.name"/></span>
|
||||
<span t-if="o.type == 'out_refund'">Credit Note</span>
|
||||
<span t-if="o.type == 'in_refund'">Vendor Credit Note</span>
|
||||
<span t-if="o.type == 'in_invoice'">Vendor Bill</span>
|
||||
<span t-field="o.number"/>
|
||||
</h2>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="l10n_in_view_company_form" model="ir.ui.view">
|
||||
<field name="name">l10n.in.view.company.form</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="priority" eval="100"/>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='vat']" position="attributes">
|
||||
<attribute name="string">GSTIN</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="l10n_in_view_country_state_form_inherit" model="ir.ui.view">
|
||||
<field name="name">l10n.in.res.country.state.form.inhert</field>
|
||||
<field name="model">res.country.state</field>
|
||||
<field name="inherit_id" ref="base.view_country_state_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="code" position="after">
|
||||
<field name="l10n_in_tin"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="l10n_in_view_partner_form" model="ir.ui.view">
|
||||
<field name="name">l10n.in.res.partner.vat.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="priority" eval="100"/>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='vat']" position="attributes">
|
||||
<attribute name="string">GSTIN</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Indian - Purchase Report(GST)',
|
||||
'version': '1.0',
|
||||
'description': """GST Purchase Report""",
|
||||
'category': 'Localization',
|
||||
'depends': [
|
||||
'l10n_in',
|
||||
'purchase',
|
||||
],
|
||||
'data': [
|
||||
'views/report_purchase_order.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': True,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="gst_report_purchaseorder_document_inherit" inherit_id="purchase.report_purchaseorder_document">
|
||||
<xpath expr="//tr[@t-foreach='o.order_line']/td[1]" position="replace">
|
||||
|
||||
<td><span t-field="line.name"/><t t-if="line.product_id.l10n_in_hsn_code and o.company_id.country_id.code == 'IN'"><h6><strong class="ml16">HSN/SAC Code:</strong> <span t-field="line.product_id.l10n_in_hsn_code"/></h6></t></td>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="gst_report_purchasequotation_document_inherit" inherit_id="purchase.report_purchasequotation_document">
|
||||
<xpath expr="//tr[@t-foreach='o.order_line']/td[1]" position="replace">
|
||||
<td><span t-field="order_line.name"/><t t-if="order_line.product_id.l10n_in_hsn_code and o.company_id.country_id.code == 'IN'"><h6><strong class="ml16">HSN/SAC Code:</strong> <span t-field="order_line.product_id.l10n_in_hsn_code"/></h6></t></td>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Indian - Sale Report(GST)',
|
||||
'version': '1.0',
|
||||
'description': """GST Sale Report""",
|
||||
'category': 'Localization',
|
||||
'depends': [
|
||||
'l10n_in',
|
||||
'sale',
|
||||
],
|
||||
'data': [
|
||||
'views/report_sale_order.xml',
|
||||
'views/report_invoice_view.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': True,
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="gst_report_invoice_layouted_inherit" inherit_id="sale.report_invoice_layouted">
|
||||
<xpath expr="//t[@name='lines_layouted']" position="replace">
|
||||
<t groups="sale.group_sale_layout" t-foreach="o.order_lines_layouted()" t-as="page" name="lines_layouted">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<th>Description</th>
|
||||
<th class="text-right">Quantity</th>
|
||||
<th class="text-right">Unit Price</th>
|
||||
<th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">Disc.(%)</th>
|
||||
<th class="text-right" t-if="len(o.tax_line_ids) > 0">Tax</th>
|
||||
<th class="text-right">Price</th>
|
||||
<t t-if="o.company_id.country_id.code == 'IN'">
|
||||
<th class="text-right" t-if="is_cgst">CGST</th>
|
||||
<th class="text-right" t-if="is_cgst">SGST</th>
|
||||
<th class="text-right" t-if="is_igst">IGST</th>
|
||||
<th class="text-right" t-if="is_cess">CESS</th>
|
||||
</t>
|
||||
</thead>
|
||||
<tbody class="invoice_tbody">
|
||||
<t t-foreach="page" t-as="layout_category">
|
||||
|
||||
<t t-if="layout_category_size > 1 or page_size > 1" groups="sale.group_sale_layout">
|
||||
<tr class="active">
|
||||
<td colspan="7" style="font-weight: bold; border-bottom: 1px solid black;">&bull;
|
||||
<t t-esc="layout_category['name']"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
|
||||
<!-- Lines associated -->
|
||||
<t t-foreach="layout_category['lines']" t-as="l">
|
||||
<tr>
|
||||
<td><span t-field="l.name"/>
|
||||
<t t-if="l.product_id.l10n_in_hsn_code"><h6><strong class="ml16">HSN/SAC Code:</strong> <span t-field="l.product_id.l10n_in_hsn_code"/></h6></t></td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.quantity"/>
|
||||
<span t-field="l.uom_id" groups="product.group_uom"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_unit"/>
|
||||
</td>
|
||||
<td t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
|
||||
<span t-field="l.discount"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="', '.join(map(lambda x: x.description or x.name, l.invoice_line_tax_ids))"/>
|
||||
</td>
|
||||
<td class="text-right" groups="sale.group_show_price_subtotal">
|
||||
<span t-field="l.price_subtotal"
|
||||
t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
</td>
|
||||
<td class="text-right" groups="sale.group_show_price_total">
|
||||
<span t-field="l.price_total"
|
||||
t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
</td>
|
||||
<t t-if="o.company_id.country_id.code == 'IN'">
|
||||
<td class="text-right" t-if="is_cgst">
|
||||
<t t-foreach="tax_datas[l.id]" t-as="tax_data">
|
||||
<t t-if="tag_cgst_id in tax_data['tag_ids']">
|
||||
<t t-esc="tax_data['amount']"/>
|
||||
</t>
|
||||
</t>
|
||||
</td>
|
||||
<td class="text-right" t-if="is_cgst">
|
||||
<t t-foreach="tax_datas[l.id]" t-as="tax_data">
|
||||
<t t-if="tag_sgst_id in tax_data['tag_ids']">
|
||||
<t t-esc="tax_data['amount']"/>
|
||||
</t>
|
||||
</t>
|
||||
</td>
|
||||
<td class="text-right" t-if="is_igst">
|
||||
<t t-foreach="tax_datas[l.id]" t-as="tax_data">
|
||||
<t t-if="tag_igst_id in tax_data['tag_ids']">
|
||||
<t t-esc="tax_data['amount']"/>
|
||||
</t>
|
||||
</t>
|
||||
</td>
|
||||
<t t-set="total_cess" t-value="0.0"/>
|
||||
<td class="text-right" t-if="is_cess">
|
||||
<t t-foreach="tax_datas[l.id]" t-as="tax_data">
|
||||
<t t-if="tag_cess_id in tax_data['tag_ids']">
|
||||
<t t-set="total_cess" t-value="total_cess + tax_data['amount']"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-esc="total_cess"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</t>
|
||||
|
||||
<t t-if="(layout_category_size > 1 or page_size > 1) and layout_category['subtotal']" groups="sale.group_sale_layout">
|
||||
<tr class="text-right">
|
||||
<td t-att-colspan="o.company_id.country_id.code == 'IN' and 9 or 6">
|
||||
<strong>Subtotal: </strong>
|
||||
<t t-set="subtotal" t-value="sum(line.price_subtotal for line in layout_category['lines'])"/>
|
||||
<span t-esc="subtotal" t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<t t-if="page_index < page_size - 1" groups="sale.group_sale_layout">
|
||||
<p style="page-break-before:always;"> </p>
|
||||
</t>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="gst_report_saleorder_document_inherit" inherit_id="sale.report_saleorder_document">
|
||||
<xpath expr="//span[@t-field='l.name']" position="after">
|
||||
<t t-if="l.product_id.l10n_in_hsn_code"><h6><strong class="ml16">HSN/SAC Code:</strong> <span t-field="l.product_id.l10n_in_hsn_code"/></h6></t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -18,7 +18,7 @@ Sheet, now only Vertical format has been permitted Which is Supported By OpenERP
|
||||
""",
|
||||
'category': 'Localization',
|
||||
'depends': [
|
||||
'account',
|
||||
'account_tax_python',
|
||||
],
|
||||
'demo': [],
|
||||
'data': [
|
||||
|
||||
@@ -3,41 +3,17 @@
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- Account Tax Group -->
|
||||
<record id="tax_group_024" model="account.tax.group">
|
||||
<field name="name">VAT 0.24%</field>
|
||||
<record id="schedule6_sgst_group" model="account.tax.group">
|
||||
<field name="name">SGST</field>
|
||||
</record>
|
||||
<record id="tax_group_01224" model="account.tax.group">
|
||||
<field name="name">VAT 0.1224%</field>
|
||||
<record id="schedule6_cgst_group" model="account.tax.group">
|
||||
<field name="name">CGST</field>
|
||||
</record>
|
||||
<record id="tax_group_1" model="account.tax.group">
|
||||
<field name="name">VAT 1%</field>
|
||||
<record id="schedule6_igst_group" model="account.tax.group">
|
||||
<field name="name">IGST</field>
|
||||
</record>
|
||||
<record id="tax_group_2" model="account.tax.group">
|
||||
<field name="name">VAT 2%</field>
|
||||
</record>
|
||||
<record id="tax_group_25" model="account.tax.group">
|
||||
<field name="name">VAT 2.5%</field>
|
||||
</record>
|
||||
<record id="tax_group_4" model="account.tax.group">
|
||||
<field name="name">VAT 4%</field>
|
||||
</record>
|
||||
<record id="tax_group_8" model="account.tax.group">
|
||||
<field name="name">VAT 8%</field>
|
||||
</record>
|
||||
<record id="tax_group_10" model="account.tax.group">
|
||||
<field name="name">VAT 10%</field>
|
||||
</record>
|
||||
<record id="tax_group_12" model="account.tax.group">
|
||||
<field name="name">VAT 12%</field>
|
||||
</record>
|
||||
<record id="tax_group_125" model="account.tax.group">
|
||||
<field name="name">VAT 12.5%</field>
|
||||
</record>
|
||||
<record id="tax_group_1236" model="account.tax.group">
|
||||
<field name="name">VAT 12.36%</field>
|
||||
</record>
|
||||
<record id="tax_group_15" model="account.tax.group">
|
||||
<field name="name">VAT 15%</field>
|
||||
<record id="schedule6_cess_group" model="account.tax.group">
|
||||
<field name="name">Cess</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<odoo>
|
||||
<!-- India - Standard Accounting Chart -->
|
||||
<record id="standard_transfer_account_id" model="account.account.template">
|
||||
<field name="code">1206</field>
|
||||
@@ -190,44 +189,23 @@
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<!-- Other current liabilities -->
|
||||
<record model="account.account.template" id="c1142111">
|
||||
<field name="name">Excise Duty Payable</field>
|
||||
<field name="code">1142111</field>
|
||||
<record model="account.account.template" id="c114210">
|
||||
<field name="name">(schedule6)SGST Payable</field>
|
||||
<field name="code">114210</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1142112">
|
||||
<field name="name">Education Cess Payable On Excise Duty</field>
|
||||
<field name="code">1142112</field>
|
||||
<record model="account.account.template" id="c114211">
|
||||
<field name="name">(schedule6)CGST Payable</field>
|
||||
<field name="code">114211</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1142113">
|
||||
<field name="name">Higher Education Cess Payable On Excise Duty</field>
|
||||
<field name="code">1142113</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1142121">
|
||||
<field name="name">Service Tax Payable</field>
|
||||
<field name="code">1142121</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1142122">
|
||||
<field name="name">Education Cess Payable On Service Tax</field>
|
||||
<field name="code">1142122</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1142123">
|
||||
<field name="name">Higher Education Cess Payable On Service Tax</field>
|
||||
<field name="code">1142123</field>
|
||||
<record model="account.account.template" id="c114212">
|
||||
<field name="name">(schedule6)IGST Payable</field>
|
||||
<field name="code">114212</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
@@ -239,20 +217,6 @@
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c114214">
|
||||
<field name="name">Central Sales Tax Payable</field>
|
||||
<field name="code">114214</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c114215">
|
||||
<field name="name">VAT Payable</field>
|
||||
<field name="code">114215</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_liabilities"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c11422">
|
||||
<field name="name">Out Standing Cheques for Clearance</field>
|
||||
<field name="code">11422</field>
|
||||
@@ -810,58 +774,23 @@
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<!-- Other current assets -->
|
||||
<record model="account.account.template" id="c121610">
|
||||
<field name="name">Sales Tax Receivable</field>
|
||||
<field name="code">121610</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c121611">
|
||||
<field name="name">VAT Receivable</field>
|
||||
<field name="name">(schedule6)SGST Receivable</field>
|
||||
<field name="code">121611</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1216121">
|
||||
<field name="name">Excise Duty Receivable</field>
|
||||
<field name="code">1216121</field>
|
||||
<record model="account.account.template" id="c121612">
|
||||
<field name="name">(schedule6)CGST Receivable</field>
|
||||
<field name="code">121612</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1216122">
|
||||
<field name="name">Education Cess Receivable On Excise Duty</field>
|
||||
<field name="code">1216122</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1216123">
|
||||
<field name="name">Higher Education Cess Receivable On Excise Duty</field>
|
||||
<field name="code">1216123</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1216131">
|
||||
<field name="name">Service Tax Receivable</field>
|
||||
<field name="code">1216131</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1216132">
|
||||
<field name="name">Education Cess Receivable On Service Tax</field>
|
||||
<field name="code">1216132</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
</record>
|
||||
<record model="account.account.template" id="c1216133">
|
||||
<field name="name">Higher Education Cess Receivable On Service Tax</field>
|
||||
<field name="code">1216133</field>
|
||||
<record model="account.account.template" id="c121613">
|
||||
<field name="name">(schedule6)IGST Receivable</field>
|
||||
<field name="code">121613</field>
|
||||
<field name="user_type_id" ref="account.data_account_type_current_assets"/>
|
||||
<field name="reconcile" eval="False"/>
|
||||
<field name="chart_template_id" ref="indian_chart_template_schedule6"/>
|
||||
@@ -1429,5 +1358,4 @@
|
||||
<field name="expense_currency_exchange_account_id" ref="c2170"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Indian - Stock Report(GST)',
|
||||
'version': '1.0',
|
||||
'description': """GST Stock Report""",
|
||||
'category': 'Localization',
|
||||
'depends': [
|
||||
'l10n_in',
|
||||
'stock',
|
||||
],
|
||||
'data': [
|
||||
'views/report_stockpicking_operations.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': True,
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="gst_report_picking_inherit" inherit_id="stock.report_picking">
|
||||
<xpath expr="//span[@t-field='pack_operation.product_id.description_picking']" position="after">
|
||||
<t t-if="pack_operation.product_id and pack_operation.product_id.l10n_in_hsn_code and o.company_id.country_id.code == 'IN'"><h6><strong class="ml16">HSN/SAC Code:</strong> <span t-field="pack_operation.product_id.l10n_in_hsn_code"/></h6></t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -13,7 +13,7 @@ var _t = core._t;
|
||||
|
||||
/**
|
||||
* Set the 'label_delay' entry in activity data according to the deadline date
|
||||
* @param {Array} list of activity Object
|
||||
* @param {Array} activities list of activity Object
|
||||
* @return {Array} : list of modified activity Object
|
||||
*/
|
||||
var setDelayLabel = function(activities){
|
||||
|
||||
@@ -89,7 +89,7 @@ var ChatterComposer = composer.BasicComposer.extend({
|
||||
|
||||
/**
|
||||
* Get the list of selected suggested partners
|
||||
* @returns Array() : list of 'recipient' selected partners (may not be created in db)
|
||||
* @returns {Array} list of 'recipient' selected partners (may not be created in db)
|
||||
**/
|
||||
get_checked_suggested_partners: function () {
|
||||
var self = this;
|
||||
@@ -106,8 +106,8 @@ var ChatterComposer = composer.BasicComposer.extend({
|
||||
/**
|
||||
* Check the additional partners (not necessary registered partners), and open a popup form view
|
||||
* for the ones who informations is missing.
|
||||
* @param Array : list of 'recipient' partners to complete informations or validate
|
||||
* @returns Deferred resolved with the list of checked suggested partners (real partner)
|
||||
* @param {Array} checked_suggested_partners list of 'recipient' partners to complete informations or validate
|
||||
* @returns {Deferred} resolved with the list of checked suggested partners (real partner)
|
||||
**/
|
||||
check_suggested_partners: function (checked_suggested_partners) {
|
||||
var self = this;
|
||||
|
||||
@@ -34,7 +34,7 @@ var DocumentViewer = Widget.extend({
|
||||
* Documents that are not of type image or video are filtered out.
|
||||
*
|
||||
* @override
|
||||
* @param {Array[Object]} attachments list of attachments
|
||||
* @param {Array<Object>} attachments list of attachments
|
||||
* @param {integer} activeAttachmentID
|
||||
*/
|
||||
init: function (parent, attachments, activeAttachmentID) {
|
||||
|
||||
@@ -80,6 +80,7 @@ var FieldMany2ManyTagsEmail = M2MTags.extend({
|
||||
var pop = new form_common.FormViewDialog(self, {
|
||||
res_model: self.field.relation,
|
||||
res_id: resID,
|
||||
context: self.record.context,
|
||||
title: _t("Please complete partner's informations and Email"),
|
||||
on_saved: function (record) {
|
||||
if (record.data.email) {
|
||||
|
||||
@@ -130,11 +130,11 @@ var Thread = Widget.extend({
|
||||
},
|
||||
|
||||
/**
|
||||
* Modifies $element to add the 'read more/read less' functionality
|
||||
* All element nodes with "data-o-mail-quote" attribute are concerned.
|
||||
* All text nodes after a ""#stopSpelling" element are concerned.
|
||||
* Those text nodes need to be wrapped in a span (toggle functionality).
|
||||
* All consecutive elements are joined in one 'read more/read less'.
|
||||
* Modifies $element to add the 'read more/read less' functionality
|
||||
* All element nodes with "data-o-mail-quote" attribute are concerned.
|
||||
* All text nodes after a ``#stopSpelling`` element are concerned.
|
||||
* Those text nodes need to be wrapped in a span (toggle functionality).
|
||||
* All consecutive elements are joined in one 'read more/read less'.
|
||||
*/
|
||||
insert_read_more: function ($element) {
|
||||
var self = this;
|
||||
|
||||
@@ -104,6 +104,10 @@
|
||||
max-width: 100%;
|
||||
word-wrap: break-word;
|
||||
|
||||
&.o_mail_note {
|
||||
background-color: @odoo-color-silver-dark;
|
||||
}
|
||||
|
||||
.o_mail_subject {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.ChatWindow">
|
||||
<div class="o_chat_window">
|
||||
<div class="o_chat_window o_in_appswitcher">
|
||||
<div class="o_chat_header">
|
||||
<t t-call="mail.ChatWindowHeaderContent">
|
||||
<t t-set="status" t-value="widget.status"/>
|
||||
@@ -19,7 +19,7 @@
|
||||
</t>
|
||||
|
||||
<t t-name="mail.ChatWindowsDropdown">
|
||||
<div t-attf-class="o_chat_window o_chat_window_dropdown dropup #{open ? 'open' : ''}">
|
||||
<div t-attf-class="o_chat_window o_in_appswitcher o_chat_window_dropdown dropup #{open ? 'open' : ''}">
|
||||
<span class="o_chat_window_dropdown_toggler dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-comments-o"/> <t t-esc="sessions.length"/>
|
||||
<span class="o_total_unread_counter">
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<button t-if="log_note_btn" class="btn btn-sm btn-link o_chatter_button_log_note" title="Log a note. Followers will not be notified.">
|
||||
Log note
|
||||
</button>
|
||||
<button t-if="schedule_activity_btn" class="btn btn-sm btn-link o_chatter_button_schedule_activity" title="Lor or schedule an activity">
|
||||
<button t-if="schedule_activity_btn" class="btn btn-sm btn-link o_chatter_button_schedule_activity" title="Log or schedule an activity">
|
||||
<i class="fa fa-clock-o"/> Schedule activity
|
||||
</button>
|
||||
</t>
|
||||
|
||||
@@ -124,12 +124,12 @@ class StockMove(models.Model):
|
||||
}
|
||||
return result
|
||||
|
||||
def _action_confirm(self):
|
||||
def _action_confirm(self, merge=True):
|
||||
moves = self
|
||||
for move in self.filtered(lambda m: m.production_id):
|
||||
moves |= move.action_explode()
|
||||
# we go further with the list of ids potentially changed by action_explode
|
||||
return super(StockMove, moves)._action_confirm()
|
||||
return super(StockMove, moves)._action_confirm(merge=merge)
|
||||
|
||||
def action_explode(self):
|
||||
""" Explodes pickings """
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
<strong t-if="o.address_id != o.partner_invoice_id">Invoice address: </strong>
|
||||
<div t-field="o.partner_invoice_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
|
||||
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
|
||||
<p t-if="o.partner_id.vat"><t t-esc="o.company_id.country_id.vat_label or 'TIN'"/>: <span t-field="o.partner_id.vat"/></p>
|
||||
</div>
|
||||
<div t-if="o.address_id != o.partner_invoice_id">
|
||||
<strong>Shipping address :</strong>
|
||||
<div t-field="o.address_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
|
||||
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
|
||||
<p t-if="o.partner_id.vat"><t t-esc="o.company_id.country_id.vat_label or 'TIN'"/>: <span t-field="o.partner_id.vat"/></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-5 col-xs-offset-1">
|
||||
|
||||
@@ -175,7 +175,4 @@ class OgonePayment(PaymentAcquirerCommon):
|
||||
'number': '4000000000000002',
|
||||
'brand': 'VISA'})
|
||||
|
||||
# check an alias is set, containing at least OPENERP
|
||||
self.assertIn('OPENERP', tx.partner_reference, 'ogone: wrong partner reference after creating an alias')
|
||||
|
||||
res = tx.ogone_s2s_execute({})
|
||||
|
||||
@@ -40,16 +40,11 @@ class AcquirerSips(models.Model):
|
||||
_inherit = 'payment.acquirer'
|
||||
|
||||
provider = fields.Selection(selection_add=[('sips', 'Sips')])
|
||||
sips_merchant_id = fields.Char('SIPS API User Password', required_if_provider='sips', groups='base.group_user')
|
||||
sips_secret = fields.Char('SIPS Secret', size=64, required_if_provider='sips', groups='base.group_user')
|
||||
|
||||
def _get_sips_urls(self, environment):
|
||||
""" Worldline SIPS URLS """
|
||||
url = {
|
||||
'prod': 'https://payment-webinit.sips-atos.com/paymentInit',
|
||||
'test': 'https://payment-webinit.simu.sips-atos.com/paymentInit', }
|
||||
|
||||
return {'sips_form_url': url.get(environment, url['test']), }
|
||||
sips_merchant_id = fields.Char('Merchant ID', help="Used for production only", required_if_provider='sips', groups='base.group_user')
|
||||
sips_secret = fields.Char('Secret Key', size=64, required_if_provider='sips', groups='base.group_user')
|
||||
sips_test_url = fields.Char("Test's url", required_if_provider='sips', groups='base.group_no_one', default='https://payment-webinit.sips-atos.com/paymentInit')
|
||||
sips_prod_url = fields.Char("Prod's url", required_if_provider='sips', groups='base.group_no_one', default='https://payment-webinit.simu.sips-atos.com/paymentInit')
|
||||
sips_version = fields.Char("Interface Version", required_if_provider='sips', groups='base.group_no_one', default='HP_2.3')
|
||||
|
||||
def _sips_generate_shasign(self, values):
|
||||
""" Generate the shasign for incoming or outgoing communications.
|
||||
@@ -97,7 +92,7 @@ class AcquirerSips(models.Model):
|
||||
u'transactionReference=%s|' % values['reference'] +
|
||||
u'statementReference=%s|' % values['reference'] +
|
||||
u'keyVersion=%s' % key_version,
|
||||
'InterfaceVersion': 'HP_2.3',
|
||||
'InterfaceVersion': self.sips_version,
|
||||
})
|
||||
|
||||
return_context = {}
|
||||
@@ -113,7 +108,7 @@ class AcquirerSips(models.Model):
|
||||
@api.multi
|
||||
def sips_get_form_action_url(self):
|
||||
self.ensure_one()
|
||||
return self._get_sips_urls(self.environment)['sips_form_url']
|
||||
return self.environment == 'prod' and self.sips_prod_url or self.sips_test_url
|
||||
|
||||
|
||||
class TxSips(models.Model):
|
||||
@@ -172,8 +167,6 @@ class TxSips(models.Model):
|
||||
# check what is bought
|
||||
if float_compare(float(data.get('amount', '0.0')) / 100, self.amount, 2) != 0:
|
||||
invalid_parameters.append(('amount', data.get('amount'), '%.2f' % self.amount))
|
||||
if self.partner_reference and data.get('customerId') != self.partner_reference:
|
||||
invalid_parameters.append(('customerId', data.get('customerId'), self.partner_reference))
|
||||
|
||||
return invalid_parameters
|
||||
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr='//group[@name="acquirer"]' position='after'>
|
||||
<group attrs="{'invisible': [('provider', '!=', 'sips')]}">
|
||||
<field name="sips_secret"/>
|
||||
<field name="sips_merchant_id"/>
|
||||
<field name="sips_secret"/>
|
||||
<field name="sips_test_url"/>
|
||||
<field name="sips_prod_url"/>
|
||||
<field name="sips_version"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
@@ -18,7 +18,6 @@ var _t = core._t;
|
||||
var Mutex = concurrency.Mutex;
|
||||
var round_di = utils.round_decimals;
|
||||
var round_pr = utils.round_precision;
|
||||
var Backbone = window.Backbone;
|
||||
|
||||
var exports = {};
|
||||
|
||||
@@ -2208,6 +2207,8 @@ exports.Order = Backbone.Model.extend({
|
||||
|
||||
initialize_validation_date: function () {
|
||||
this.validation_date = new Date();
|
||||
this.formatted_validation_date = field_utils.format.datetime(
|
||||
moment(this.validation_date), {}, {timezone: false});
|
||||
},
|
||||
|
||||
set_tip: function(tip) {
|
||||
|
||||
@@ -135,21 +135,23 @@ var ConfirmPopupWidget = PopupWidget.extend({
|
||||
gui.define_popup({name:'confirm', widget: ConfirmPopupWidget});
|
||||
|
||||
/**
|
||||
* A popup that allows the user to select one item from a list.
|
||||
* A popup that allows the user to select one item from a list.
|
||||
*
|
||||
* show_popup('selection',{
|
||||
* title: "Popup Title",
|
||||
* list: [
|
||||
* { label: 'foobar', item: 45 },
|
||||
* { label: 'bar foo', item: 'stuff' },
|
||||
* ],
|
||||
* confirm: function(item) {
|
||||
* // get the item selected by the user.
|
||||
* },
|
||||
* cancel: function(){
|
||||
* // user chose nothing
|
||||
* }
|
||||
* });
|
||||
* Example::
|
||||
*
|
||||
* show_popup('selection',{
|
||||
* title: "Popup Title",
|
||||
* list: [
|
||||
* { label: 'foobar', item: 45 },
|
||||
* { label: 'bar foo', item: 'stuff' },
|
||||
* ],
|
||||
* confirm: function(item) {
|
||||
* // get the item selected by the user.
|
||||
* },
|
||||
* cancel: function(){
|
||||
* // user chose nothing
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
|
||||
var SelectionPopupWidget = PopupWidget.extend({
|
||||
|
||||
@@ -1444,7 +1444,7 @@
|
||||
<t t-name="PosTicket">
|
||||
<div class="pos-sale-ticket">
|
||||
|
||||
<div class="pos-center-align"><t t-esc="moment().format('L LT')"/> <t t-esc="order.name"/></div>
|
||||
<div class="pos-center-align"><t t-esc="order.formatted_validation_date"/> <t t-esc="order.name"/></div>
|
||||
<br />
|
||||
<t t-esc="widget.pos.company.name"/><br />
|
||||
<div class="receipt-phone">
|
||||
|
||||
@@ -76,7 +76,7 @@ var PortalChatter = Widget.extend({
|
||||
* Fetch the messages and the message count from the server for the
|
||||
* current page and current domain.
|
||||
*
|
||||
* @param {Array}
|
||||
* @param {Array} domain
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
messageFetch: function(domain){
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<div>Tel:<t t-esc='receipt.company.phone' /></div>
|
||||
</t>
|
||||
<t t-if='receipt.company.vat'>
|
||||
<div>VAT:<t t-esc='receipt.company.vat' /></div>
|
||||
<div><t t-esc="receipt.company.country_id.vat_label or 'TIN'"/>:<t t-esc='receipt.company.vat' /></div>
|
||||
</t>
|
||||
<t t-if='receipt.company.email'>
|
||||
<div><t t-esc='receipt.company.email' /></div>
|
||||
|
||||
@@ -306,7 +306,8 @@ class PurchaseOrder(models.Model):
|
||||
'default_use_template': bool(template_id),
|
||||
'default_template_id': template_id,
|
||||
'default_composition_mode': 'comment',
|
||||
'custom_layout': "purchase.mail_template_data_notification_email_purchase_order"
|
||||
'custom_layout': "purchase.mail_template_data_notification_email_purchase_order",
|
||||
'force_email': True
|
||||
})
|
||||
return {
|
||||
'name': _('Compose Email'),
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="col-xs-5 col-xs-offset-1">
|
||||
<div t-field="o.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
|
||||
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
|
||||
<p t-if="o.partner_id.vat"><t t-esc="o.company_id.country_id.vat_label or 'TIN'"/>: <span t-field="o.partner_id.vat"/></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="col-xs-5 col-xs-offset-1">
|
||||
<div t-field="o.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
|
||||
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
|
||||
<p t-if="o.partner_id.vat"><t t-esc="o.company_id.country_id.vat_label or 'TIN'"/>: <span t-field="o.partner_id.vat"/></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class TestCreatePicking(common.TestProductCommon):
|
||||
self.assertEqual(len(self.po.order_line.move_ids), 1, 'One move should be created')
|
||||
# Change purchase order line product quantity
|
||||
self.po.order_line.write({'product_qty': 7.0})
|
||||
self.assertEqual(len(self.po.order_line.move_ids), 2, 'Two move should be created')
|
||||
self.assertEqual(len(self.po.order_line.move_ids), 1, 'The two moves should be merged in one')
|
||||
|
||||
# Validate first shipment
|
||||
self.picking = self.po.picking_ids[0]
|
||||
|
||||
@@ -69,7 +69,7 @@ class Rating(models.Model):
|
||||
for rating in self:
|
||||
try:
|
||||
image_path = get_resource_path('rating', 'static/src/img', 'rating_%s.png' % (int(rating.rating),))
|
||||
rating.rating_image = base64.b64decode(open(image_path, 'rb').read())
|
||||
rating.rating_image = base64.b64encode(open(image_path, 'rb').read())
|
||||
except (IOError, OSError):
|
||||
rating.rating_image = False
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button class="oe_stat_button" type="object" name="action_open_rated_object" icon="fa-file">
|
||||
<div class="o_form_field o_stat_info">
|
||||
<span class="o_stat_text">Ressource</span>
|
||||
<span class="o_stat_text">Resource</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -477,7 +477,8 @@ class SaleOrder(models.Model):
|
||||
'default_composition_mode': 'comment',
|
||||
'mark_so_as_sent': True,
|
||||
'custom_layout': "sale.mail_template_data_notification_email_sale_order",
|
||||
'proforma': self.env.context.get('proforma', False)
|
||||
'proforma': self.env.context.get('proforma', False),
|
||||
'force_email': True
|
||||
}
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<attribute name="groups">!sale.group_sale_layout</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//table" position="after">
|
||||
<t groups="sale.group_sale_layout" t-foreach="o.order_lines_layouted()" t-as="page">
|
||||
<t groups="sale.group_sale_layout" t-foreach="o.order_lines_layouted()" t-as="page" name="lines_layouted">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -100,7 +100,7 @@
|
||||
<div class="col-xs-5 col-xs-offset-1" groups="sale.group_delivery_invoice_address">
|
||||
<div t-field="o.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'/>
|
||||
<div t-if="o.partner_id.vat" class="mt16">VAT: <span t-field="o.partner_id.vat"/></div>
|
||||
<div t-if="o.partner_id.vat" class="mt16"><t t-esc="o.company_id.country_id.vat_label or 'TIN'"/>: <span t-field="o.partner_id.vat"/></div>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="col-xs-5 col-xs-offset-1">
|
||||
<div t-field="doc.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
|
||||
<p t-if="doc.partner_id.vat">VAT: <span t-field="doc.partner_id.vat"/></p>
|
||||
<p t-if="doc.partner_id.vat"><t t-esc="doc.company_id.country_id.vat_label or 'TIN'"/>: <span t-field="doc.partner_id.vat"/></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ var PlanAction = Widget.extend(ControlPanelMixin, {
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* Refresh the DOM html
|
||||
* @param {string|html}
|
||||
* @param {string|html} dom
|
||||
* @private
|
||||
*/
|
||||
_refreshPlan: function(dom){
|
||||
|
||||
@@ -71,6 +71,7 @@ class Product(models.Model):
|
||||
"Otherwise, this includes goods leaving any Stock "
|
||||
"Location with 'internal' type.")
|
||||
|
||||
orderpoint_ids = fields.One2many('stock.warehouse.orderpoint', 'product_id', 'Minimum Stock Rules')
|
||||
nbr_reordering_rules = fields.Integer('Reordering Rules', compute='_compute_nbr_reordering_rules')
|
||||
reordering_min_qty = fields.Float(compute='_compute_nbr_reordering_rules')
|
||||
reordering_max_qty = fields.Float(compute='_compute_nbr_reordering_rules')
|
||||
|
||||
@@ -462,6 +462,91 @@ class StockMove(models.Model):
|
||||
if rules and (not move.origin_returned_move_id or move.origin_returned_move_id.location_dest_id.id != rules.location_dest_id.id):
|
||||
rules._apply(move)
|
||||
|
||||
def _merge_moves_fields(self):
|
||||
""" This method will return a dict of stock move’s values that represent the values of all moves in `self` merged. """
|
||||
state = self._get_relevant_state_among_moves()
|
||||
origin = '/'.join(set(self.filtered(lambda m: m.origin).mapped('origin')))
|
||||
return {
|
||||
'product_uom_qty': sum(self.mapped('product_uom_qty')),
|
||||
'date': min(self.mapped('date')),
|
||||
'date_expected': min(self.mapped('date_expected')) if self.mapped('picking_id').move_type == 'direct' else max(self.mapped('date_expected')),
|
||||
'move_dest_ids': [(4, m.id) for m in self.mapped('move_dest_ids')],
|
||||
'move_orig_ids': [(4, m.id) for m in self.mapped('move_orig_ids')],
|
||||
'state': state,
|
||||
'origin': origin,
|
||||
}
|
||||
|
||||
def _merge_moves(self):
|
||||
""" This method will, for each move in `self`, go up in their linked picking and try to
|
||||
find in their existing moves a candidate into which we can merge the move.
|
||||
:return: Recordset of moves passed to this method. If some of the passed moves were merged
|
||||
into another existing one, return this one and not the (now unlinked) original.
|
||||
"""
|
||||
distinct_fields = ['price_unit', 'product_id', 'product_packaging',
|
||||
'product_uom', 'restrict_partner_id', 'scrapped', 'origin_returned_move_id']
|
||||
|
||||
def _keys_sorted(move):
|
||||
return tuple([getattr(move, attr) for attr in distinct_fields])
|
||||
|
||||
# Move removed after merge
|
||||
moves_to_unlink = self.env['stock.move']
|
||||
moves_to_merge = []
|
||||
for picking in self.mapped('picking_id'):
|
||||
# First step find move to merge.
|
||||
for k, g in groupby(sorted(picking.move_lines, key=_keys_sorted), key=itemgetter(*distinct_fields)):
|
||||
moves = self.env['stock.move'].concat(*g).filtered(lambda m: m.state not in ('done', 'cancel', 'draft'))
|
||||
# If we have multiple records we will merge then in a single one.
|
||||
if len(moves) > 1:
|
||||
moves_to_merge.append(moves)
|
||||
|
||||
# second step merge its move lines, initial demand, ...
|
||||
for moves in moves_to_merge:
|
||||
# link all move lines to record 0 (the one we will keep).
|
||||
moves.mapped('move_line_ids').write({'move_id': moves[0].id})
|
||||
# merge move data
|
||||
moves[0].write(moves._merge_moves_fields())
|
||||
# update merged moves dicts
|
||||
moves_to_unlink |= moves[1:]
|
||||
|
||||
if moves_to_unlink:
|
||||
# We are using propagate to False in order to not cancel destination moves merged in moves[0]
|
||||
moves_to_unlink.write({'propagate': False})
|
||||
moves_to_unlink._action_cancel()
|
||||
moves_to_unlink.unlink()
|
||||
return (self | self.env['stock.move'].concat(*moves_to_merge)) - moves_to_unlink
|
||||
|
||||
def _get_relevant_state_among_moves(self):
|
||||
# We sort our moves by importance of state:
|
||||
# ------------- 0
|
||||
# | Assigned |
|
||||
# -------------
|
||||
# | Waiting |
|
||||
# -------------
|
||||
# | Partial |
|
||||
# -------------
|
||||
# | Confirm |
|
||||
# ------------- len-1
|
||||
sort_map = {
|
||||
'assigned': 4,
|
||||
'waiting': 3,
|
||||
'partially_available': 2,
|
||||
'confirmed': 1,
|
||||
}
|
||||
moves_todo = self\
|
||||
.filtered(lambda move: move.state not in ['cancel', 'done'])\
|
||||
.sorted(key=lambda move: sort_map.get(move.state, 0))
|
||||
# The picking should be the same for all moves.
|
||||
if moves_todo[0].picking_id.move_type == 'one':
|
||||
if moves_todo[0].state in ('partially_available', 'confirmed'):
|
||||
return 'confirmed'
|
||||
else:
|
||||
return moves_todo[0].state or 'draft'
|
||||
elif moves_todo[0].state != 'assigned' and any(move.state in ['assigned', 'partially_available'] for move in moves_todo):
|
||||
return 'partially_available'
|
||||
else:
|
||||
# take the less important state among all move_lines.
|
||||
return moves_todo[-1].state or 'draft'
|
||||
|
||||
@api.onchange('product_id', 'product_qty')
|
||||
def onchange_quantity(self):
|
||||
if not self.product_id or self.product_qty < 0.0:
|
||||
@@ -538,8 +623,11 @@ class StockMove(models.Model):
|
||||
'location_dest_id': self.location_dest_id.id,
|
||||
}
|
||||
|
||||
def _action_confirm(self):
|
||||
""" Confirms stock move or put it in waiting if it's linked to another move. """
|
||||
def _action_confirm(self, merge=True):
|
||||
""" Confirms stock move or put it in waiting if it's linked to another move.
|
||||
:param: merge: According to this boolean, a newly confirmed move will be merged
|
||||
in another move of the same picking sharing its characteristics.
|
||||
"""
|
||||
move_create_proc = self.env['stock.move']
|
||||
move_to_confirm = self.env['stock.move']
|
||||
move_waiting = self.env['stock.move']
|
||||
@@ -574,6 +662,8 @@ class StockMove(models.Model):
|
||||
for moves in to_assign.values():
|
||||
moves._assign_picking()
|
||||
self._push_apply()
|
||||
if merge:
|
||||
return self._merge_moves()
|
||||
return self
|
||||
|
||||
def _prepare_procurement_values(self):
|
||||
@@ -662,7 +752,7 @@ class StockMove(models.Model):
|
||||
return taken_quantity
|
||||
|
||||
def _action_assign(self):
|
||||
""" Reserve stock moves by creating their stock move lines. A stock move is
|
||||
""" Reserve stock moves by creating their stock move lines. A stock move is
|
||||
considered reserved once the sum of `product_qty` for all its move lines is
|
||||
equal to its `product_qty`. If it is less, the stock move is considered
|
||||
partially available.
|
||||
@@ -806,24 +896,6 @@ class StockMove(models.Model):
|
||||
rounding_method ='UP')
|
||||
extra_move_vals = self._prepare_extra_move_vals(extra_move_quantity)
|
||||
extra_move = self.copy(default=extra_move_vals)._action_confirm()
|
||||
|
||||
# link it to some move lines
|
||||
for move_line in self.move_line_ids.filtered(lambda ml: ml.qty_done):
|
||||
if float_compare(move_line.qty_done, extra_move_quantity, precision_rounding=rounding) <= 0:
|
||||
# move this move line to our extra move
|
||||
move_line.move_id = extra_move.id
|
||||
extra_move_quantity -= move_line.qty_done
|
||||
else:
|
||||
# split this move line and assign the new part to our extra move
|
||||
quantity_split = float_round(
|
||||
move_line.qty_done - extra_move_quantity,
|
||||
precision_rounding=self.product_uom.rounding,
|
||||
rounding_method='UP')
|
||||
move_line.qty_done = quantity_split
|
||||
move_line.copy(default={'move_id': extra_move.id, 'qty_done': extra_move_quantity, 'product_uom_qty': 0})
|
||||
extra_move_quantity -= extra_move_quantity
|
||||
if extra_move_quantity == 0.0:
|
||||
break
|
||||
return extra_move
|
||||
|
||||
def _action_done(self):
|
||||
@@ -937,7 +1009,8 @@ class StockMove(models.Model):
|
||||
# new_move.write({'move_dest_id': new_move_prop})
|
||||
# returning the first element of list returned by action_confirm is ok because we checked it wouldn't be exploded (and
|
||||
# thus the result of action_confirm should always be a list of 1 element length)
|
||||
new_move._action_confirm()
|
||||
# In this case we don't merge move since the new move with 0 quantity done will be used for the backorder.
|
||||
new_move = new_move._action_confirm(merge=False)
|
||||
# TDE FIXME: due to action confirm change
|
||||
return new_move.id
|
||||
|
||||
|
||||
@@ -305,40 +305,16 @@ class Picking(models.Model):
|
||||
elif all(move.state in ['cancel', 'done'] for move in self.move_lines):
|
||||
self.state = 'done'
|
||||
else:
|
||||
# We sort our moves by importance of state:
|
||||
# ------------- 0
|
||||
# | Confirmed |
|
||||
# -------------
|
||||
# | Partial |
|
||||
# -------------
|
||||
# | Waiting |
|
||||
# -------------
|
||||
# | Assigned |
|
||||
# ------------- len - 1
|
||||
sort_map = {
|
||||
'assigned': 4,
|
||||
'waiting': 3,
|
||||
'partially_available': 2,
|
||||
'confirmed': 1,
|
||||
}
|
||||
moves_todo = self.move_lines\
|
||||
.filtered(lambda move: move.state not in ['cancel', 'done'])\
|
||||
.sorted(key=lambda move: sort_map.get(move.state, 0))
|
||||
if self.move_type == 'one':
|
||||
if moves_todo[0].state in ('partially_available', 'confirmed'):
|
||||
self.state = 'confirmed'
|
||||
else:
|
||||
self.state = moves_todo[0].state or 'draft'
|
||||
elif moves_todo[0].state != 'assigned' and any(x.state in ['assigned', 'partially_available'] for x in moves_todo):
|
||||
self.state = 'partially_available'
|
||||
else:
|
||||
# take the less important state among all move_lines.
|
||||
self.state = moves_todo[-1].state or 'draft'
|
||||
self.state = self.move_lines._get_relevant_state_among_moves()
|
||||
|
||||
@api.one
|
||||
@api.depends('move_lines.priority')
|
||||
def _compute_priority(self):
|
||||
self.priority = self.mapped('move_lines') and max(self.mapped('move_lines').mapped('priority')) or '1'
|
||||
if self.mapped('move_lines'):
|
||||
priorities = [priority for priority in self.mapped('move_lines.priority') if priority] or ['1']
|
||||
self.priority = max(priorities)
|
||||
else:
|
||||
self.priority = '1'
|
||||
|
||||
@api.one
|
||||
def _set_priority(self):
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<div t-if="o.partner_id" name="partner_header">
|
||||
<div t-field="o.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True, "phone_icons": True}'/>
|
||||
<p t-if="o.sudo().partner_id.vat">VAT: <span t-field="o.sudo().partner_id.vat"/></p>
|
||||
<p t-if="o.sudo().partner_id.vat"><t t-esc="o.company_id.country_id.vat_label or 'TIN'"/>: <span t-field="o.sudo().partner_id.vat"/></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -625,23 +625,13 @@ class TestSinglePicking(TestStockCommon):
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, pack_location), 0.0)
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, pack_location, allow_negative=True), -1.0)
|
||||
|
||||
extra_move = delivery_order.move_lines - move1
|
||||
extra_move_line = extra_move.move_line_ids[0]
|
||||
|
||||
# self.assertEqual(move1.product_qty, 1.0)
|
||||
# self.assertEqual(move1.quantity_done, 1.0)
|
||||
self.assertEqual(move1.product_qty, 2.0)
|
||||
self.assertEqual(move1.quantity_done, 2.0)
|
||||
self.assertEqual(move1.reserved_availability, 0.0)
|
||||
self.assertEqual(move1.move_line_ids.product_qty, 0.0) # change reservation to 0 for done move
|
||||
self.assertEqual(move1.move_line_ids.qty_done, 1.0)
|
||||
self.assertEqual(sum(move1.move_line_ids.mapped('qty_done')), 2.0)
|
||||
self.assertEqual(move1.state, 'done')
|
||||
|
||||
self.assertEqual(extra_move.product_qty, 1.0)
|
||||
self.assertEqual(extra_move.quantity_done, 1.0)
|
||||
self.assertEqual(extra_move.reserved_availability, 0.0)
|
||||
self.assertEqual(extra_move_line.product_qty, 0.0) # should not be able to reserve
|
||||
self.assertEqual(extra_move_line.qty_done, 1.0)
|
||||
self.assertEqual(extra_move.state, 'done')
|
||||
|
||||
def test_extra_move_2(self):
|
||||
""" Check the good behavior of creating an extra move in a delivery order. This usecase
|
||||
simulates the delivery of 3 item while the initial stock move had to move 1 and there's
|
||||
@@ -681,23 +671,13 @@ class TestSinglePicking(TestStockCommon):
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, pack_location), 0.0)
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, pack_location, allow_negative=True), -2.0)
|
||||
|
||||
extra_move = delivery_order.move_lines - move1
|
||||
extra_move_line = extra_move.move_line_ids[0]
|
||||
|
||||
self.assertEqual(move1.product_qty, 1.0)
|
||||
self.assertEqual(move1.quantity_done, 1.0)
|
||||
self.assertEqual(move1.product_qty, 3.0)
|
||||
self.assertEqual(move1.quantity_done, 3.0)
|
||||
self.assertEqual(move1.reserved_availability, 0.0)
|
||||
self.assertEqual(move1.move_line_ids.product_qty, 0.0) # change reservation to 0 for done move
|
||||
self.assertEqual(move1.move_line_ids.qty_done, 1.0)
|
||||
self.assertEqual(sum(move1.move_line_ids.mapped('qty_done')), 3.0)
|
||||
self.assertEqual(move1.state, 'done')
|
||||
|
||||
self.assertEqual(extra_move.product_qty, 2.0)
|
||||
self.assertEqual(extra_move.quantity_done, 2.0)
|
||||
self.assertEqual(extra_move.reserved_availability, 0.0)
|
||||
self.assertEqual(extra_move_line.product_qty, 0.0) # should not be able to reserve
|
||||
self.assertEqual(extra_move_line.qty_done, 2.0)
|
||||
self.assertEqual(extra_move.state, 'done')
|
||||
|
||||
def test_extra_move_3(self):
|
||||
""" Check the good behavior of creating an extra move in a receipt. This usecase simulates
|
||||
the receipt of 2 item while the initial stock move had to move 1.
|
||||
@@ -729,23 +709,13 @@ class TestSinglePicking(TestStockCommon):
|
||||
receipt.with_context(debug=True).do_transfer()
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, stock_location), 2.0)
|
||||
|
||||
extra_move = receipt.move_lines - move1
|
||||
extra_move_line = extra_move.move_line_ids[0]
|
||||
|
||||
self.assertEqual(move1.product_qty, 1.0)
|
||||
self.assertEqual(move1.quantity_done, 1.0)
|
||||
self.assertEqual(move1.product_qty, 2.0)
|
||||
self.assertEqual(move1.quantity_done, 2.0)
|
||||
self.assertEqual(move1.reserved_availability, 0.0)
|
||||
self.assertEqual(move1.move_line_ids.product_qty, 0.0) # change reservation to 0 for done move
|
||||
self.assertEqual(move1.move_line_ids.qty_done, 1.0)
|
||||
self.assertEqual(sum(move1.move_line_ids.mapped('qty_done')), 2.0)
|
||||
self.assertEqual(move1.state, 'done')
|
||||
|
||||
self.assertEqual(extra_move.product_qty, 1.0)
|
||||
self.assertEqual(extra_move.quantity_done, 1.0)
|
||||
self.assertEqual(extra_move.reserved_availability, 0.0)
|
||||
self.assertEqual(extra_move_line.product_qty, 0.0) # should not be able to reserve
|
||||
self.assertEqual(extra_move_line.qty_done, 1.0)
|
||||
self.assertEqual(extra_move.state, 'done')
|
||||
|
||||
def test_recheck_availability_1(self):
|
||||
""" Check the good behavior of check availability. I create a DO for 2 unit with
|
||||
only one in stock. After the first check availability, I should have 1 reserved
|
||||
@@ -1208,6 +1178,159 @@ class TestSinglePicking(TestStockCommon):
|
||||
move_line.lot_id = lot1
|
||||
delivery_order.action_done()
|
||||
|
||||
def test_merge_moves_1(self):
|
||||
receipt = self.env['stock.picking'].create({
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location,
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': self.picking_type_in,
|
||||
})
|
||||
self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 3,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': receipt.id,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location,
|
||||
})
|
||||
self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 5,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': receipt.id,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location,
|
||||
})
|
||||
self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 1,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': receipt.id,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location,
|
||||
})
|
||||
self.MoveObj.create({
|
||||
'name': self.productB.name,
|
||||
'product_id': self.productB.id,
|
||||
'product_uom_qty': 5,
|
||||
'product_uom': self.productB.uom_id.id,
|
||||
'picking_id': receipt.id,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location,
|
||||
})
|
||||
receipt.action_confirm()
|
||||
self.assertEqual(len(receipt.move_lines), 2, 'Moves were not merged')
|
||||
self.assertEqual(receipt.move_lines.filtered(lambda m: m.product_id == self.productA).product_uom_qty, 9, 'Merged quantity is not correct')
|
||||
self.assertEqual(receipt.move_lines.filtered(lambda m: m.product_id == self.productB).product_uom_qty, 5, 'Merge should not impact product B reserved quantity')
|
||||
|
||||
def test_merge_moves_2(self):
|
||||
receipt = self.env['stock.picking'].create({
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location,
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': self.picking_type_in,
|
||||
})
|
||||
self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 3,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': receipt.id,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location,
|
||||
'origin': 'MPS'
|
||||
})
|
||||
self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 5,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': receipt.id,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location,
|
||||
'origin': 'PO0001'
|
||||
})
|
||||
self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 3,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': receipt.id,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location,
|
||||
'origin': 'MPS'
|
||||
})
|
||||
receipt.action_confirm()
|
||||
self.assertEqual(len(receipt.move_lines), 1, 'Moves were not merged')
|
||||
self.assertEqual(receipt.move_lines.origin.count('MPS'), 1, 'Origin not merged together or duplicated')
|
||||
self.assertEqual(receipt.move_lines.origin.count('PO0001'), 1, 'Origin not merged together or duplicated')
|
||||
|
||||
def test_merge_chained_moves(self):
|
||||
""" Imagine multiple step delivery. Two different receipt picking for the same product should only generate
|
||||
1 picking from input to QC and another from QC to stock. The link at the end should follow this scheme.
|
||||
Move receipt 1 \
|
||||
Move Input-> QC - Move QC -> Stock
|
||||
Move receipt 2 /
|
||||
"""
|
||||
warehouse = self.env['stock.warehouse'].create({
|
||||
'name': 'TEST WAREHOUSE',
|
||||
'code': 'TEST1',
|
||||
'reception_steps': 'three_steps',
|
||||
})
|
||||
receipt1 = self.env['stock.picking'].create({
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': warehouse.wh_input_stock_loc_id.id,
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': warehouse.in_type_id.id,
|
||||
})
|
||||
move_receipt_1 = self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 5,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': receipt1.id,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': warehouse.wh_input_stock_loc_id.id,
|
||||
})
|
||||
receipt2 = self.env['stock.picking'].create({
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': warehouse.wh_input_stock_loc_id.id,
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': warehouse.in_type_id.id,
|
||||
})
|
||||
move_receipt_2 = self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 3,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': receipt2.id,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': warehouse.wh_input_stock_loc_id.id,
|
||||
})
|
||||
receipt1.action_confirm()
|
||||
receipt2.action_confirm()
|
||||
|
||||
# Check following move has been created and grouped in one picking.
|
||||
self.assertTrue(move_receipt_1.move_dest_ids, 'No move created from push rules')
|
||||
self.assertTrue(move_receipt_2.move_dest_ids, 'No move created from push rules')
|
||||
self.assertEqual(move_receipt_1.move_dest_ids.picking_id, move_receipt_2.move_dest_ids.picking_id, 'Destination moves should be in the same picking')
|
||||
|
||||
# Check link for input move are correct.
|
||||
input_move = move_receipt_2.move_dest_ids
|
||||
self.assertEqual(len(input_move.move_dest_ids), 1)
|
||||
self.assertEqual(set(input_move.move_orig_ids.ids), set((move_receipt_2 | move_receipt_1).ids),
|
||||
'Move from input to QC should be merged and have the two receipt moves as origin.')
|
||||
self.assertEqual(move_receipt_1.move_dest_ids, input_move)
|
||||
self.assertEqual(move_receipt_2.move_dest_ids, input_move)
|
||||
|
||||
# Check link for quality check move are also correct.
|
||||
qc_move = input_move.move_dest_ids
|
||||
self.assertEqual(len(qc_move), 1)
|
||||
self.assertTrue(qc_move.move_orig_ids == input_move, 'Move between QC and stock should only have the input move as origin')
|
||||
|
||||
|
||||
class TestStockUOM(TestStockCommon):
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ class TestStockFlow(TestStockCommon):
|
||||
back_order_in = self.PickingObj.search([('backorder_id', '=', picking_in.id)])
|
||||
self.assertEqual(len(back_order_in), 1, 'Back order should be created.')
|
||||
# Check total move lines of back order.
|
||||
self.assertEqual(len(back_order_in.move_lines), 3, 'Wrong number of move lines.')
|
||||
self.assertEqual(len(back_order_in.move_lines), 2, 'Wrong number of move lines.')
|
||||
# Check back order should be created with 3 quantity of product C.
|
||||
moves = self.MoveObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', back_order_in.id)])
|
||||
product_c_qty = [move.product_uom_qty for move in moves]
|
||||
@@ -320,13 +320,9 @@ class TestStockFlow(TestStockCommon):
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
packD = self.StockPackObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', back_order_in.id)], order='product_qty')
|
||||
self.assertEqual(len(packD), 2, 'Wrong number of pack operation.')
|
||||
self.assertEqual(len(packD), 1, 'Wrong number of pack operation.')
|
||||
packD[0].write({
|
||||
'qty_done': 3,
|
||||
'lot_id': lot1_productD.id,
|
||||
})
|
||||
packD[1].write({
|
||||
'qty_done': 5,
|
||||
'qty_done': 8,
|
||||
'lot_id': lot1_productD.id,
|
||||
})
|
||||
packCs = self.StockPackObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', back_order_in.id)], limit=1)
|
||||
@@ -376,7 +372,7 @@ class TestStockFlow(TestStockCommon):
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Check total no of move lines.
|
||||
self.assertEqual(len(back_order_in.move_lines), 5, 'Wrong number of move lines')
|
||||
self.assertEqual(len(back_order_in.move_lines), 4, 'Wrong number of move lines')
|
||||
# Check incoming shipment state must be 'Done'.
|
||||
self.assertEqual(back_order_in.state, 'done', 'Wrong state of picking.')
|
||||
# Check incoming shipment move lines state must be 'Done'.
|
||||
@@ -392,7 +388,7 @@ class TestStockFlow(TestStockCommon):
|
||||
# Check product D done quantity must be 5.0 and 3.0
|
||||
movesD = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', back_order_in.id)])
|
||||
d_done_qty = [move.product_uom_qty for move in movesD]
|
||||
self.assertEqual(set(d_done_qty), set([3.0, 5.0]), 'Wrong quantity of moves product D.')
|
||||
self.assertEqual(set(d_done_qty), set([8.0]), 'Wrong quantity of moves product D.')
|
||||
# Check no back order is created.
|
||||
self.assertFalse(self.PickingObj.search([('backorder_id', '=', back_order_in.id)]), "Should not create any back order.")
|
||||
|
||||
@@ -662,7 +658,7 @@ class TestStockFlow(TestStockCommon):
|
||||
for move in picking_in_B.move_lines:
|
||||
self.assertEqual(move.state, 'done', 'Wrong state of move line.')
|
||||
# Check total done move lines for incoming shipment.
|
||||
self.assertEqual(len(picking_in_B.move_lines), 6, 'Wrong number of move lines')
|
||||
self.assertEqual(len(picking_in_B.move_lines), 5, 'Wrong number of move lines')
|
||||
# Check product DozA done quantity.
|
||||
moves_DozA = self.MoveObj.search([('product_id', '=', self.DozA.id), ('picking_id', '=', picking_in_B.id)], limit=1)
|
||||
self.assertEqual(moves_DozA.product_uom_qty, 96, 'Wrong move quantity (%s found instead of 96)' % (moves_DozA.product_uom_qty))
|
||||
@@ -681,7 +677,7 @@ class TestStockFlow(TestStockCommon):
|
||||
self.assertEqual(moves_kgB.product_uom.id, self.uom_gm.id, 'Wrong uom in move for product kgB.')
|
||||
# Check two moves created for product gB with quantity (0.525 kg and 0.3 g)
|
||||
moves_gB_kg = self.MoveObj.search([('product_id', '=', self.gB.id), ('picking_id', '=', picking_in_B.id), ('product_uom', '=', self.uom_kg.id)], limit=1)
|
||||
self.assertEqual(moves_gB_kg.product_uom_qty, 0.525, 'Wrong move quantity (%s found instead of 0.525)' % (moves_gB_kg.product_uom_qty))
|
||||
self.assertEqual(moves_gB_kg.product_uom_qty, 0.526, 'Wrong move quantity (%s found instead of 0.526)' % (moves_gB_kg.product_uom_qty))
|
||||
self.assertEqual(moves_gB_kg.product_uom.id, self.uom_kg.id, 'Wrong uom in move for product gB.')
|
||||
|
||||
# TODO Test extra move once the uom is editable in the move_lines
|
||||
@@ -1769,27 +1765,27 @@ class TestStockFlow(TestStockCommon):
|
||||
'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,
|
||||
'name': self.productB.name,
|
||||
'product_id': self.productB.id,
|
||||
'product_uom_qty': 2,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'product_uom': self.productB.uom_id.id,
|
||||
'picking_id': picking_out.id,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'name': self.productB.name,
|
||||
'product_id': self.productB.id,
|
||||
'product_uom_qty': 2,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'product_uom': self.productB.uom_id.id,
|
||||
'picking_id': picking_out.id,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location,
|
||||
'move_dest_ids': [(4, move_with_ancestors.id, 0)]})
|
||||
other_move = self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'name': self.productC.name,
|
||||
'product_id': self.productC.id,
|
||||
'product_uom_qty': 2,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'product_uom': self.productC.uom_id.id,
|
||||
'picking_id': picking_out.id,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<field name="location_id" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="location_dest_id" groups="stock.group_stock_multi_locations"/>
|
||||
<separator/>
|
||||
<filter string="To Do" name="todo" domain="[('state','not in ',['done', 'draft'])]"/>
|
||||
<filter string="To Do" name="todo" domain="[('state','not in', ['done', 'draft'])]"/>
|
||||
<filter string="Done" name="done" domain="[('state','=','done')]"/>
|
||||
<separator/>
|
||||
<filter string="Receptions" name="incoming" domain="[('picking_id.picking_type_id.code','=','incoming')]"/>
|
||||
|
||||
@@ -733,6 +733,7 @@ var data = {
|
||||
DataSet: DataSet,
|
||||
DataSetStatic: DataSetStatic,
|
||||
DataSetSearch: DataSetSearch,
|
||||
/** @type String */
|
||||
noDisplayContent: "<em class=\"text-warning\">" + _t("Unnamed") + "</em>",
|
||||
};
|
||||
|
||||
|
||||
@@ -206,16 +206,17 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, {
|
||||
// Window title handling
|
||||
// --------------------------------------------------------------
|
||||
/**
|
||||
Sets the first part of the title of the window, dedicated to the current action.
|
||||
* Sets the first part of the title of the window, dedicated to the current action.
|
||||
*/
|
||||
set_title: function (title) {
|
||||
this.set_title_part("action", title);
|
||||
},
|
||||
/**
|
||||
Sets an arbitrary part of the title of the window. Title parts are identified by strings. Each time
|
||||
a title part is changed, all parts are gathered, ordered by alphabetical order and displayed in the
|
||||
title of the window separated by '-'.
|
||||
*/
|
||||
* Sets an arbitrary part of the title of the window. Title parts are
|
||||
* identified by strings. Each time a title part is changed, all parts
|
||||
* are gathered, ordered by alphabetical order and displayed in the title
|
||||
* of the window separated by ``-``.
|
||||
*/
|
||||
set_title_part: function (part, title) {
|
||||
var tmp = _.clone(this.get("title_part"));
|
||||
tmp[part] = title;
|
||||
|
||||
@@ -67,7 +67,7 @@ var Action = core.Class.extend({
|
||||
},
|
||||
/**
|
||||
* Stores the DOM fragment of the action
|
||||
* @param {jQuery} [fragment] the DOM fragment
|
||||
* @param {jQuery} [$fragment] the DOM fragment
|
||||
*/
|
||||
set_fragment: function($fragment) {
|
||||
this.$fragment = $fragment;
|
||||
@@ -300,11 +300,11 @@ var ActionManager = Widget.extend({
|
||||
/**
|
||||
* Add a new action to the action manager
|
||||
*
|
||||
* widget: typically, widgets added are openerp.web.ViewManager. The action manager
|
||||
* uses the stack of actions to handle the breadcrumbs.
|
||||
* action_descr: new action description
|
||||
* options.on_reverse_breadcrumb: will be called when breadcrumb is clicked on
|
||||
* options.clear_breadcrumbs: boolean, if true, action stack is destroyed
|
||||
* @param {Widget} widget typically, widgets added are openerp.web.ViewManager. The action manager uses the stack of actions to handle the breadcrumbs.
|
||||
* @param {Object} action_descr new action description
|
||||
* @param {Object} options
|
||||
* @param options.on_reverse_breadcrumb will be called when breadcrumb is clicked on
|
||||
* @param options.clear_breadcrumbs: boolean, if true, action stack is destroyed
|
||||
*/
|
||||
push_action: function(widget, action_descr, options) {
|
||||
var self = this;
|
||||
@@ -613,7 +613,7 @@ var ActionManager = Widget.extend({
|
||||
/**
|
||||
* Execute an OpenERP action
|
||||
*
|
||||
* @param {Number|String|String|Object} Can be either an action id, an action XML id, a client action tag or an action descriptor.
|
||||
* @param {Number|String|String|Object} action Can be either an action id, an action XML id, a client action tag or an action descriptor.
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.clear_breadcrumbs=false] Clear the breadcrumbs history list
|
||||
* @param {Boolean} [options.replace_breadcrumb=false] Replace the current breadcrumb with the action
|
||||
|
||||
@@ -96,7 +96,7 @@ var ExtendedSearchProposition = Widget.extend(/** @lends instance.web.search.Ext
|
||||
},
|
||||
});
|
||||
|
||||
ExtendedSearchProposition.Field = Widget.extend({
|
||||
var Field = Widget.extend({
|
||||
init: function (parent, field) {
|
||||
this._super(parent);
|
||||
this.field = field;
|
||||
@@ -153,7 +153,7 @@ ExtendedSearchProposition.Field = Widget.extend({
|
||||
}
|
||||
});
|
||||
|
||||
ExtendedSearchProposition.Char = ExtendedSearchProposition.Field.extend({
|
||||
var Char = Field.extend({
|
||||
tagName: 'input',
|
||||
className: 'o_input',
|
||||
attributes: {
|
||||
@@ -172,7 +172,7 @@ ExtendedSearchProposition.Char = ExtendedSearchProposition.Field.extend({
|
||||
}
|
||||
});
|
||||
|
||||
ExtendedSearchProposition.DateTime = ExtendedSearchProposition.Field.extend({
|
||||
var DateTime = Field.extend({
|
||||
tagName: 'span',
|
||||
attributes: {
|
||||
type: 'datetime'
|
||||
@@ -241,7 +241,7 @@ ExtendedSearchProposition.DateTime = ExtendedSearchProposition.Field.extend({
|
||||
},
|
||||
});
|
||||
|
||||
ExtendedSearchProposition.Date = ExtendedSearchProposition.DateTime.extend({
|
||||
var Date = DateTime.extend({
|
||||
attributes: {
|
||||
type: 'date'
|
||||
},
|
||||
@@ -250,7 +250,7 @@ ExtendedSearchProposition.Date = ExtendedSearchProposition.DateTime.extend({
|
||||
},
|
||||
});
|
||||
|
||||
ExtendedSearchProposition.Integer = ExtendedSearchProposition.Field.extend({
|
||||
var Integer = Field.extend({
|
||||
tagName: 'input',
|
||||
className: 'o_input',
|
||||
attributes: {
|
||||
@@ -280,11 +280,11 @@ ExtendedSearchProposition.Integer = ExtendedSearchProposition.Field.extend({
|
||||
}
|
||||
});
|
||||
|
||||
ExtendedSearchProposition.Id = ExtendedSearchProposition.Integer.extend({
|
||||
var Id = Integer.extend({
|
||||
operators: [{value: "=", text: _lt("is")}]
|
||||
});
|
||||
|
||||
ExtendedSearchProposition.Float = ExtendedSearchProposition.Field.extend({
|
||||
var Float = Field.extend({
|
||||
template: 'SearchView.extended_search.proposition.float',
|
||||
operators: [
|
||||
{value: "=", text: _lt("is equal to")},
|
||||
@@ -313,7 +313,7 @@ ExtendedSearchProposition.Float = ExtendedSearchProposition.Field.extend({
|
||||
}
|
||||
});
|
||||
|
||||
ExtendedSearchProposition.Selection = ExtendedSearchProposition.Field.extend({
|
||||
var Selection = Field.extend({
|
||||
template: 'SearchView.extended_search.proposition.selection',
|
||||
operators: [
|
||||
{value: "=", text: _lt("is")},
|
||||
@@ -331,7 +331,7 @@ ExtendedSearchProposition.Selection = ExtendedSearchProposition.Field.extend({
|
||||
}
|
||||
});
|
||||
|
||||
ExtendedSearchProposition.Boolean = ExtendedSearchProposition.Field.extend({
|
||||
var Boolean = Field.extend({
|
||||
tagName: 'span',
|
||||
operators: [
|
||||
{value: "=", text: _lt("is true")},
|
||||
@@ -347,19 +347,19 @@ ExtendedSearchProposition.Boolean = ExtendedSearchProposition.Field.extend({
|
||||
});
|
||||
|
||||
core.search_filters_registry
|
||||
.add('char', ExtendedSearchProposition.Char)
|
||||
.add('text', ExtendedSearchProposition.Char)
|
||||
.add('one2many', ExtendedSearchProposition.Char)
|
||||
.add('many2one', ExtendedSearchProposition.Char)
|
||||
.add('many2many', ExtendedSearchProposition.Char)
|
||||
.add('datetime', ExtendedSearchProposition.DateTime)
|
||||
.add('date', ExtendedSearchProposition.Date)
|
||||
.add('integer', ExtendedSearchProposition.Integer)
|
||||
.add('float', ExtendedSearchProposition.Float)
|
||||
.add('monetary', ExtendedSearchProposition.Float)
|
||||
.add('boolean', ExtendedSearchProposition.Boolean)
|
||||
.add('selection', ExtendedSearchProposition.Selection)
|
||||
.add('id', ExtendedSearchProposition.Id);
|
||||
.add('char', Char)
|
||||
.add('text', Char)
|
||||
.add('one2many', Char)
|
||||
.add('many2one', Char)
|
||||
.add('many2many', Char)
|
||||
.add('datetime', DateTime)
|
||||
.add('date', Date)
|
||||
.add('integer', Integer)
|
||||
.add('float', Float)
|
||||
.add('monetary', Float)
|
||||
.add('boolean', Boolean)
|
||||
.add('selection', Selection)
|
||||
.add('id', Id);
|
||||
|
||||
return {
|
||||
ExtendedSearchProposition: ExtendedSearchProposition
|
||||
|
||||
@@ -265,7 +265,7 @@ var SearchView = Widget.extend({
|
||||
*
|
||||
* @param parent
|
||||
* @param dataset
|
||||
* @param fields_view
|
||||
* @param fvg
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.hidden=false] hide the search view
|
||||
* @param {Boolean} [options.disable_custom_filters=false] do not load custom filters from ir.filters
|
||||
@@ -499,8 +499,8 @@ var SearchView = Widget.extend({
|
||||
.$el.focus();
|
||||
},
|
||||
/**
|
||||
* @param {openerp.web.search.SearchQuery | undefined} Undefined if event is change
|
||||
* @param {openerp.web.search.Facet}
|
||||
* @param {openerp.web.search.SearchQuery | undefined} collection Undefined if event is change
|
||||
* @param {openerp.web.search.Facet} model
|
||||
* @param {Object} [options]
|
||||
*/
|
||||
renderFacets: function (collection, model, options) {
|
||||
|
||||
@@ -26,7 +26,7 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
search: function(event) {
|
||||
var d = event.data;
|
||||
_.extend(this.env, this._process_search_data(d.domains, d.contexts, d.groupbys));
|
||||
this.active_view.controller.reload(_.extend({}, this.env));
|
||||
this.active_view.controller.reload(_.extend({offset: 0}, this.env));
|
||||
},
|
||||
add_filter: '_onAddFilter',
|
||||
switch_view: function(event) {
|
||||
@@ -232,8 +232,9 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
},
|
||||
/**
|
||||
* Returns the default view with the following fallbacks:
|
||||
* - use the default_view defined in the flags, if any
|
||||
* - use the first view in the view_order
|
||||
*
|
||||
* - use the default_view defined in the flags, if any
|
||||
* - use the first view in the view_order
|
||||
*
|
||||
* @returns {Object} the default view
|
||||
*/
|
||||
|
||||
@@ -211,7 +211,7 @@ var loadJS = (function () {
|
||||
* @param {HTMLFormElement} [options.form] the form to submit in order to fetch the file
|
||||
* @param {Function} [options.success] callback in case of download success
|
||||
* @param {Function} [options.error] callback in case of request error, provided with the error body
|
||||
* @param {Function} [options.complete] called after both ``success`` and ``error` callbacks have executed
|
||||
* @param {Function} [options.complete] called after both ``success`` and ``error`` callbacks have executed
|
||||
*/
|
||||
function get_file(options) {
|
||||
// need to detect when the file is done downloading (not used
|
||||
|
||||
@@ -6,6 +6,8 @@ var mixins = require('web.mixins');
|
||||
|
||||
/**
|
||||
* Event Bus used to bind events scoped in the current instance
|
||||
*
|
||||
* @class Bus
|
||||
*/
|
||||
return Class.extend(mixins.EventDispatcherMixin, {
|
||||
init: function() {
|
||||
|
||||
@@ -12,46 +12,47 @@ odoo.define('web.Class', function () {
|
||||
* Defines The Class object. That object can be used to define and inherit classes using
|
||||
* the extend() method.
|
||||
*
|
||||
* Example:
|
||||
* Example::
|
||||
*
|
||||
* var Person = Class.extend({
|
||||
* init: function(isDancing){
|
||||
* this.dancing = isDancing;
|
||||
* },
|
||||
* dance: function(){
|
||||
* return this.dancing;
|
||||
* }
|
||||
* });
|
||||
* var Person = Class.extend({
|
||||
* init: function(isDancing){
|
||||
* this.dancing = isDancing;
|
||||
* },
|
||||
* dance: function(){
|
||||
* return this.dancing;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* The init() method act as a constructor. This class can be instancied this way:
|
||||
* The init() method act as a constructor. This class can be instanced this way::
|
||||
*
|
||||
* var person = new Person(true);
|
||||
* person.dance();
|
||||
* var person = new Person(true);
|
||||
* person.dance();
|
||||
*
|
||||
* The Person class can also be extended again:
|
||||
* The Person class can also be extended again:
|
||||
*
|
||||
* var Ninja = Person.extend({
|
||||
* init: function(){
|
||||
* this._super( false );
|
||||
* },
|
||||
* dance: function(){
|
||||
* // Call the inherited version of dance()
|
||||
* return this._super();
|
||||
* },
|
||||
* swingSword: function(){
|
||||
* return true;
|
||||
* }
|
||||
* });
|
||||
* var Ninja = Person.extend({
|
||||
* init: function(){
|
||||
* this._super( false );
|
||||
* },
|
||||
* dance: function(){
|
||||
* // Call the inherited version of dance()
|
||||
* return this._super();
|
||||
* },
|
||||
* swingSword: function(){
|
||||
* return true;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* When extending a class, each re-defined method can use this._super() to call the previous
|
||||
* implementation of that method.
|
||||
*
|
||||
* @class Class
|
||||
*/
|
||||
function OdooClass(){}
|
||||
|
||||
var initializing = false;
|
||||
var fnTest = /xyz/.test(function(){xyz();}) ? /\b_super\b/ : /.*/;
|
||||
|
||||
// The web Class implementation (does nothing)
|
||||
function OdooClass(){};
|
||||
|
||||
/**
|
||||
* Subclass an existing class
|
||||
*
|
||||
|
||||
@@ -122,7 +122,7 @@ return {
|
||||
* the last operation.
|
||||
*
|
||||
* For example, let us say that we have a _fetch method on a widget which
|
||||
* fetches data. We want to rerender the widget after. We could do this:
|
||||
* fetches data. We want to rerender the widget after. We could do this::
|
||||
*
|
||||
* this._fetch().then(function (result) {
|
||||
* self.state = result;
|
||||
@@ -130,17 +130,18 @@ return {
|
||||
* });
|
||||
*
|
||||
* Now, we have at least two problems:
|
||||
*
|
||||
* - if this code is called twice and the second _fetch completes before the
|
||||
* first, the end state will be the result of the first _fetch, which is
|
||||
* not what we expect
|
||||
* - in any cases, the user interface will rerender twice, which is bad.
|
||||
*
|
||||
* Now, if we have a DropPrevious:
|
||||
* Now, if we have a DropPrevious::
|
||||
*
|
||||
* this.dropPrevious = new DropPrevious();
|
||||
*
|
||||
* Then we can wrap the _fetch in a DropPrevious and have the expected
|
||||
* result:
|
||||
* result::
|
||||
*
|
||||
* this.dropPrevious
|
||||
* .add(this._fetch())
|
||||
@@ -153,7 +154,7 @@ return {
|
||||
/**
|
||||
* Registers a new deferred and rejects the previous one
|
||||
*
|
||||
* @param {Deferred} the new deferred
|
||||
* @param {Deferred} deferred the new deferred
|
||||
* @returns {Promise}
|
||||
*/
|
||||
add: function (deferred) {
|
||||
@@ -171,7 +172,7 @@ return {
|
||||
*
|
||||
* Imagine that we have a function to fetch some data _load(), which returns
|
||||
* a deferred which resolves to something useful. Now, we have some code
|
||||
* looking like this:
|
||||
* looking like this::
|
||||
*
|
||||
* return this._load().then(function (result) {
|
||||
* this.state = result;
|
||||
@@ -179,11 +180,11 @@ return {
|
||||
*
|
||||
* If this code is run twice, but the second execution ends before the
|
||||
* first, then the final state will be the result of the first call to
|
||||
* _load. However, if we have a mutex:
|
||||
* _load. However, if we have a mutex::
|
||||
*
|
||||
* this.mutex = new Mutex();
|
||||
*
|
||||
* and if we wrap the calls to _load in a mutex:
|
||||
* and if we wrap the calls to _load in a mutex::
|
||||
*
|
||||
* return this.mutex.exec(function() {
|
||||
* return this._load().then(function (result) {
|
||||
|
||||
@@ -9,17 +9,17 @@ var QWeb = core.qweb;
|
||||
var _t = core._t;
|
||||
|
||||
/**
|
||||
A useful class to handle dialogs.
|
||||
|
||||
Attributes:
|
||||
- $footer: A jQuery element targeting a dom part where buttons can be added. It always exists
|
||||
during the lifecycle of the dialog.
|
||||
*/
|
||||
* A useful class to handle dialogs.
|
||||
* Attributes:
|
||||
*
|
||||
* ``$footer``
|
||||
* A jQuery element targeting a dom part where buttons can be added. It
|
||||
* always exists during the lifecycle of the dialog.
|
||||
**/
|
||||
var Dialog = Widget.extend({
|
||||
xmlDependencies: ['/web/static/src/xml/dialog.xml'],
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Widget} parent
|
||||
* @param {Object} [options]
|
||||
* @param {string} [options.title=Odoo]
|
||||
|
||||
@@ -115,10 +115,14 @@ return {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* jQuery find function behavior is:
|
||||
* jQuery find function behavior is::
|
||||
*
|
||||
* $('A').find('A B') <=> $('A A B')
|
||||
* The searches behavior to find options' DOM needs to be
|
||||
*
|
||||
* The searches behavior to find options' DOM needs to be::
|
||||
*
|
||||
* $('A').find('A B') <=> $('A B')
|
||||
*
|
||||
* This is what this function does.
|
||||
*
|
||||
* @param {jQuery} $from - the jQuery element(s) from which to search
|
||||
|
||||
@@ -10,6 +10,9 @@ var AbstractService = require('web.AbstractService');
|
||||
* relationship. Each object can a have a parent and multiple children.
|
||||
* When an object is destroyed, all its children are destroyed too releasing
|
||||
* any resource they could have reserved before.
|
||||
*
|
||||
* @name ParentedMixin
|
||||
* @mixin
|
||||
*/
|
||||
var ParentedMixin = {
|
||||
__parentedMixin : true,
|
||||
@@ -57,21 +60,21 @@ var ParentedMixin = {
|
||||
return this.__parentedDestroyed;
|
||||
},
|
||||
/**
|
||||
Utility method to only execute asynchronous actions if the current
|
||||
object has not been destroyed.
|
||||
|
||||
@param {$.Deferred} promise The promise representing the asynchronous
|
||||
action.
|
||||
@param {bool} [reject=false] If true, the returned promise will be
|
||||
rejected with no arguments if the current
|
||||
object is destroyed. If false, the
|
||||
returned promise will never be resolved
|
||||
or rejected.
|
||||
@returns {$.Deferred} A promise that will mirror the given promise if
|
||||
everything goes fine but will either be rejected
|
||||
with no arguments or never resolved if the
|
||||
current object is destroyed.
|
||||
*/
|
||||
* Utility method to only execute asynchronous actions if the current
|
||||
* object has not been destroyed.
|
||||
*
|
||||
* @param {$.Deferred} promise The promise representing the asynchronous
|
||||
* action.
|
||||
* @param {bool} [reject=false] If true, the returned promise will be
|
||||
* rejected with no arguments if the current
|
||||
* object is destroyed. If false, the
|
||||
* returned promise will never be resolved
|
||||
* or rejected.
|
||||
* @returns {$.Deferred} A promise that will mirror the given promise if
|
||||
* everything goes fine but will either be rejected
|
||||
* with no arguments or never resolved if the
|
||||
* current object is destroyed.
|
||||
*/
|
||||
alive: function (promise, reject) {
|
||||
var self = this;
|
||||
return $.Deferred(function (def) {
|
||||
@@ -228,13 +231,16 @@ var Events = Class.extend({
|
||||
});
|
||||
|
||||
/**
|
||||
Mixin containing an event system. Events are also registered by specifying the target object
|
||||
(the object which will receive the event when it is raised). Both the event-emitting object
|
||||
and the target object store or reference to each other. This is used to correctly remove all
|
||||
reference to the event handler when any of the object is destroyed (when the destroy() method
|
||||
from ParentedMixin is called). Removing those references is necessary to avoid memory leak
|
||||
and phantom events (events which are raised and sent to a previously destroyed object).
|
||||
*/
|
||||
* Mixin containing an event system. Events are also registered by specifying the target object
|
||||
* (the object which will receive the event when it is raised). Both the event-emitting object
|
||||
* and the target object store or reference to each other. This is used to correctly remove all
|
||||
* reference to the event handler when any of the object is destroyed (when the destroy() method
|
||||
* from ParentedMixin is called). Removing those references is necessary to avoid memory leak
|
||||
* and phantom events (events which are raised and sent to a previously destroyed object).
|
||||
*
|
||||
* @name EventDispatcherMixin
|
||||
* @mixin
|
||||
*/
|
||||
var EventDispatcherMixin = _.extend({}, ParentedMixin, {
|
||||
__eventDispatcherMixin: true,
|
||||
custom_events: {},
|
||||
@@ -349,6 +355,10 @@ var EventDispatcherMixin = _.extend({}, ParentedMixin, {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @name PropertiesMixin
|
||||
* @mixin
|
||||
*/
|
||||
var PropertiesMixin = _.extend({}, EventDispatcherMixin, {
|
||||
init: function () {
|
||||
EventDispatcherMixin.init.call(this);
|
||||
@@ -432,6 +442,10 @@ odoo.define('web.ServicesMixin', function (require) {
|
||||
|
||||
var rpc = require('web.rpc');
|
||||
|
||||
/**
|
||||
* @mixin
|
||||
* @name ServicesMixin
|
||||
*/
|
||||
var ServicesMixin = {
|
||||
call: function (service, method) {
|
||||
var args = Array.prototype.slice.call(arguments, 2);
|
||||
@@ -450,9 +464,9 @@ var ServicesMixin = {
|
||||
* Builds and executes RPC query. Returns a deferred's promise resolved with
|
||||
* the RPC result.
|
||||
*
|
||||
* @param {string} arg1 either a route or a model
|
||||
* @param {string} method if a model is given, this argument is a method
|
||||
* @returns {Deferred's Promise}
|
||||
* @param {string} params either a route or a model
|
||||
* @param {string} options if a model is given, this argument is a method
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_rpc: function (params, options) {
|
||||
var query = rpc.buildQuery(params);
|
||||
|
||||
@@ -530,11 +530,11 @@ datetime.date = py.type('date', null, {
|
||||
}),
|
||||
});
|
||||
/**
|
||||
Returns the current local date, which means the date on the client (which can be different
|
||||
compared to the date of the server).
|
||||
|
||||
@return {datetime.date}
|
||||
*/
|
||||
* Returns the current local date, which means the date on the client (which can be different
|
||||
* compared to the date of the server).
|
||||
*
|
||||
* @return {datetime.date}
|
||||
*/
|
||||
function context_today() {
|
||||
var d = new Date();
|
||||
return py.PY_call(
|
||||
|
||||
@@ -321,8 +321,6 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
||||
* @param {String} url RPC endpoint
|
||||
* @param {Object} params call parameters
|
||||
* @param {Object} options additional options for rpc call
|
||||
* @param {Function} success_callback function to execute on RPC call success
|
||||
* @param {Function} error_callback function to execute on RPC call failure
|
||||
* @returns {jQuery.Deferred} jquery-provided ajax deferred
|
||||
*/
|
||||
rpc: function (url, params, options) {
|
||||
@@ -413,7 +411,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
||||
* (host system settings) to UTC, for a given date. The offset is positive
|
||||
* if the local timezone is behind UTC, and negative if it is ahead.
|
||||
*
|
||||
* @param {string || moment} date a valid string date or moment instance
|
||||
* @param {string | moment} date a valid string date or moment instance
|
||||
* @returns {integer}
|
||||
*/
|
||||
getTZOffset: function (date) {
|
||||
|
||||
@@ -71,6 +71,16 @@ var TranslationDataBase = Class.extend(/** @lends instance.TranslationDataBase#
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Eager translation function, performs translation immediately at call
|
||||
* site. Beware using this outside of method bodies (before the
|
||||
* translation database is loaded), you probably want :func:`_lt`
|
||||
* instead.
|
||||
*
|
||||
* @function _t
|
||||
* @param {String} source string to translate
|
||||
* @returns {String} source translated into the current locale
|
||||
*/
|
||||
var _t = new TranslationDataBase().build_translation_function();
|
||||
/**
|
||||
* Lazy translation function, only performs the translation when actually
|
||||
|
||||
@@ -107,7 +107,7 @@ var utils = {
|
||||
/**
|
||||
* Returns a human readable size
|
||||
*
|
||||
* @param {Number} number of bytes
|
||||
* @param {Number} size number of bytes
|
||||
*/
|
||||
human_size: function (size) {
|
||||
var units = _t("Bytes,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb").split(',');
|
||||
@@ -271,8 +271,8 @@ var utils = {
|
||||
/**
|
||||
* performs a half up rounding with a fixed amount of decimals, correcting for float loss of precision
|
||||
* See the corresponding float_round() in server/tools/float_utils.py for more info
|
||||
* @param {Number} the value to be rounded
|
||||
* @param {Number} the number of decimals. eg: round_decimals(3.141592,2) -> 3.14
|
||||
* @param {Number} value the value to be rounded
|
||||
* @param {Number} decimals the number of decimals. eg: round_decimals(3.141592,2) -> 3.14
|
||||
*/
|
||||
round_decimals: function (value, decimals) {
|
||||
return utils.round_precision(value, Math.pow(10,-decimals));
|
||||
@@ -319,9 +319,9 @@ var utils = {
|
||||
},
|
||||
/**
|
||||
* Create a cookie
|
||||
* @param {String} name : the name of the cookie
|
||||
* @param {String} value : the value stored in the cookie
|
||||
* @param {Integer} ttl : time to live of the cookie in millis. -1 to erase the cookie.
|
||||
* @param {String} name the name of the cookie
|
||||
* @param {String} value the value stored in the cookie
|
||||
* @param {Integer} ttl time to live of the cookie in millis. -1 to erase the cookie.
|
||||
*/
|
||||
set_cookie: function (name, value, ttl) {
|
||||
ttl = ttl || 24*60*60*365;
|
||||
|
||||
@@ -7,50 +7,51 @@ var mixins = require('web.mixins');
|
||||
var ServicesMixin = require('web.ServicesMixin');
|
||||
|
||||
/**
|
||||
* Base class for all visual components. Provides a lot of functionalities helpful
|
||||
* Base class for all visual components. Provides a lot of functions helpful
|
||||
* for the management of a part of the DOM.
|
||||
*
|
||||
* Widget handles:
|
||||
*
|
||||
* - Rendering with QWeb.
|
||||
* - Life-cycle management and parenting (when a parent is destroyed, all its children are
|
||||
* destroyed too).
|
||||
* - Life-cycle management and parenting (when a parent is destroyed, all its
|
||||
* children are destroyed too).
|
||||
* - Insertion in DOM.
|
||||
*
|
||||
* Guide to create implementations of the Widget class:
|
||||
* ==============================================
|
||||
* **Guide to create implementations of the Widget class**
|
||||
*
|
||||
* Here is a sample child class:
|
||||
* Here is a sample child class::
|
||||
*
|
||||
* MyWidget = openerp.base.Widget.extend({
|
||||
* // the name of the QWeb template to use for rendering
|
||||
* template: "MyQWebTemplate",
|
||||
* var MyWidget = openerp.base.Widget.extend({
|
||||
* // the name of the QWeb template to use for rendering
|
||||
* template: "MyQWebTemplate",
|
||||
*
|
||||
* init: function(parent) {
|
||||
* this._super(parent);
|
||||
* // stuff that you want to init before the rendering
|
||||
* },
|
||||
* start: function() {
|
||||
* // stuff you want to make after the rendering, `this.$el` holds a correct value
|
||||
* this.$(".my_button").click(/* an example of event binding * /);
|
||||
* init: function(parent) {
|
||||
* this._super(parent);
|
||||
* // stuff that you want to init before the rendering
|
||||
* },
|
||||
* start: function() {
|
||||
* // stuff you want to make after the rendering, `this.$el` holds a correct value
|
||||
* this.$(".my_button").click(/* an example of event binding * /);
|
||||
*
|
||||
* // if you have some asynchronous operations, it's a good idea to return
|
||||
* // a promise in start()
|
||||
* var promise = this._rpc(...);
|
||||
* return promise;
|
||||
* }
|
||||
* });
|
||||
* // if you have some asynchronous operations, it's a good idea to return
|
||||
* // a promise in start()
|
||||
* var promise = this._rpc(...);
|
||||
* return promise;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* Now this class can simply be used with the following syntax:
|
||||
* Now this class can simply be used with the following syntax::
|
||||
*
|
||||
* var my_widget = new MyWidget(this);
|
||||
* my_widget.appendTo($(".some-div"));
|
||||
* var my_widget = new MyWidget(this);
|
||||
* my_widget.appendTo($(".some-div"));
|
||||
*
|
||||
* With these two lines, the MyWidget instance was inited, rendered, it was inserted into the
|
||||
* DOM inside the ".some-div" div and its events were binded.
|
||||
* With these two lines, the MyWidget instance was initialized, rendered,
|
||||
* inserted into the DOM inside the ``.some-div`` div and its events were
|
||||
* bound.
|
||||
*
|
||||
* And of course, when you don't need that widget anymore, just do:
|
||||
* And of course, when you don't need that widget anymore, just do::
|
||||
*
|
||||
* my_widget.destroy();
|
||||
* my_widget.destroy();
|
||||
*
|
||||
* That will kill the widget in a clean way and erase its content from the dom.
|
||||
*/
|
||||
@@ -67,7 +68,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
||||
* The name of the QWeb template that will be used for rendering. Must be
|
||||
* redefined in subclasses or the default render() method can not be used.
|
||||
*
|
||||
* @type string
|
||||
* @type {String}
|
||||
*/
|
||||
template: null,
|
||||
/**
|
||||
@@ -82,8 +83,6 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
||||
/**
|
||||
* Constructs the widget and sets its parent if a parent is given.
|
||||
*
|
||||
* @constructs openerp.Widget
|
||||
*
|
||||
* @param {openerp.Widget} parent Binds the current instance to the given Widget instance.
|
||||
* When that widget is destroyed by calling destroy(), the current instance will be
|
||||
* destroyed too. Can be null.
|
||||
@@ -238,7 +237,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
||||
* (if any) by the new one in the DOM.
|
||||
*
|
||||
* @param {HTMLElement | jQuery} $el
|
||||
* @returns {*} this
|
||||
* @returns {Widget} this
|
||||
*/
|
||||
replaceElement: function ($el) {
|
||||
var $oldel = this.$el;
|
||||
@@ -257,13 +256,14 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
|
||||
* Re-sets the widget's root element (el/$el/$el).
|
||||
*
|
||||
* Includes:
|
||||
*
|
||||
* * re-delegating events
|
||||
* * re-binding sub-elements
|
||||
* * if the widget already had a root element, replacing the pre-existing
|
||||
* element in the DOM
|
||||
*
|
||||
* @param {HTMLElement | jQuery} element new root element for the widget
|
||||
* @return {*} this
|
||||
* @return {Widget} this
|
||||
*/
|
||||
setElement: function (element) {
|
||||
// NB: completely useless, as WidgetMixin#init creates a $el
|
||||
|
||||
@@ -59,6 +59,8 @@ var AbstractField = Widget.extend({
|
||||
specialData: false,
|
||||
/**
|
||||
* to override to indicate which field types are supported by the widget
|
||||
*
|
||||
* @type Array<String>
|
||||
*/
|
||||
supportedFieldTypes: [],
|
||||
|
||||
@@ -196,7 +198,7 @@ var AbstractField = Widget.extend({
|
||||
* will have the cursor at the very end.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {boolean} [noselect=false] if false and the input
|
||||
* @param {boolean} [options.noselect=false] if false and the input
|
||||
* is of type text or textarea, the content will also be selected
|
||||
* @param {Event} [options.event] the event which fired this activation
|
||||
* @returns {boolean} true if the widget was activated, false if the
|
||||
|
||||
@@ -1741,10 +1741,9 @@ var FieldPercentPie = AbstractField.extend({
|
||||
});
|
||||
|
||||
/**
|
||||
* FieldProgressBar
|
||||
* parameters
|
||||
* - title: title of the bar, displayed on top of the bar
|
||||
* options
|
||||
* Node options:
|
||||
*
|
||||
* - title: title of the bar, displayed on top of the bar options
|
||||
* - editable: boolean if value is editable
|
||||
* - current_value: get the current_value from the field that must be present in the view
|
||||
* - max_value: get the max_value from the field that must be present in the view
|
||||
@@ -2099,8 +2098,9 @@ var FieldDomain = AbstractField.extend({
|
||||
"click .o_field_domain_dialog_button": "_onDialogEditButtonClick",
|
||||
}),
|
||||
custom_events: _.extend({}, AbstractField.prototype.custom_events, {
|
||||
"domain_changed": "_onDomainSelectorValueChange",
|
||||
"domain_selected": "_onDomainSelectorDialogValueChange",
|
||||
domain_changed: "_onDomainSelectorValueChange",
|
||||
domain_selected: "_onDomainSelectorDialogValueChange",
|
||||
open_record: "_onOpenRecord",
|
||||
}),
|
||||
/**
|
||||
* @constructor
|
||||
@@ -2281,6 +2281,15 @@ var FieldDomain = AbstractField.extend({
|
||||
_onDomainSelectorDialogValueChange: function (e) {
|
||||
this._setValue(Domain.prototype.arrayToString(e.data.domain));
|
||||
},
|
||||
/**
|
||||
* Stops the propagation of the 'open_record' event, as we don't want the
|
||||
* user to be able to open records from the list opened in a dialog.
|
||||
*
|
||||
* @param {OdooEvent} event
|
||||
*/
|
||||
_onOpenRecord: function (event) {
|
||||
event.stopPropagation();
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -80,7 +80,7 @@ function formatChar(value, field, options) {
|
||||
* Returns a string representing a date. If the value is false, then we return
|
||||
* an empty string. Note that this is dependant on the localization settings
|
||||
*
|
||||
* @param {Moment|false}
|
||||
* @param {Moment|false} value
|
||||
* @param {Object} [field]
|
||||
* a description of the field (note: this parameter is ignored)
|
||||
* @param {Object} [options] additional options
|
||||
@@ -258,7 +258,7 @@ function formatX2Many(value) {
|
||||
* @param {Object} [options]
|
||||
* additional options to override the values in the python description of
|
||||
* the field.
|
||||
* @param {Object} [options.currency] - the description of the currency to use
|
||||
* @param {Object} [options.currency] the description of the currency to use
|
||||
* @param {integer} [options.currency_id]
|
||||
* the id of the 'res.currency' to use (ignored if options.currency)
|
||||
* @param {string} [options.currency_field]
|
||||
@@ -336,7 +336,7 @@ function formatSelection(value, field, options) {
|
||||
* Create an Date object
|
||||
* The method toJSON return the formated value to send value server side
|
||||
*
|
||||
* @param {string}
|
||||
* @param {string} value
|
||||
* @param {Object} [field]
|
||||
* a description of the field (note: this parameter is ignored)
|
||||
* @param {Object} [options] additional options
|
||||
@@ -375,7 +375,7 @@ function parseDate(value, field, options) {
|
||||
* Create an Date object
|
||||
* The method toJSON return the formated value to send value server side
|
||||
*
|
||||
* @param {string}
|
||||
* @param {string} value
|
||||
* @param {Object} [field]
|
||||
* a description of the field (note: this parameter is ignored)
|
||||
* @param {Object} [options] additional options
|
||||
|
||||
@@ -10,10 +10,10 @@ var _t = core._t;
|
||||
|
||||
|
||||
/**
|
||||
* This widget is intended to display a warning near a label of a 'timezone' field
|
||||
* indicating if the browser timezone is identical (or not) to the selected timezone.
|
||||
* This widget depends on a field given with the param 'tz_offset_field', which contains
|
||||
* the time difference between UTC time and local time, in minutes.
|
||||
* This widget is intended to display a warning near a label of a 'timezone' field
|
||||
* indicating if the browser timezone is identical (or not) to the selected timezone.
|
||||
* This widget depends on a field given with the param 'tz_offset_field', which contains
|
||||
* the time difference between UTC time and local time, in minutes.
|
||||
*/
|
||||
var FieldTimezoneMismatch = FieldSelection.extend({
|
||||
/**
|
||||
|
||||
@@ -112,7 +112,7 @@ var AbstractFieldUpgrade = {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent}
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
_onInputClicked: function (event) {
|
||||
if ($(event.currentTarget).prop("checked")) {
|
||||
|
||||
@@ -16,23 +16,31 @@ var config = {
|
||||
/**
|
||||
* debug is a boolean flag. It is only considered true if the flag is set
|
||||
* in the url
|
||||
*
|
||||
* @type Boolean
|
||||
*/
|
||||
debug: ($.deparam($.param.querystring()).debug !== undefined),
|
||||
device: {
|
||||
/**
|
||||
* touch is a boolean, true if the device supports touch interaction
|
||||
*
|
||||
* @type Boolean
|
||||
*/
|
||||
touch: 'ontouchstart' in window || 'onmsgesturechange' in window,
|
||||
/**
|
||||
* size_class is an integer: 0, 1, 2 or 3, depending on the (current)
|
||||
* size of the device. This is a dynamic property, updated whenever the
|
||||
* browser is resized
|
||||
*
|
||||
* @type Number
|
||||
*/
|
||||
size_class: null,
|
||||
/**
|
||||
* A frequent use case is to have a different render in 'mobile' mode,
|
||||
* meaning when the screen is small. This flag (boolean) is true when
|
||||
* the size is not 3. It is also updated dynamically.
|
||||
*
|
||||
* @type Boolean
|
||||
*/
|
||||
isMobile: null,
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user