[MERGE] forward port branch saas-18 up to d15a31ae6d

This commit is contained in:
Christophe Simonis
2017-09-19 13:05:04 +02:00
174 changed files with 10516 additions and 3849 deletions
+14 -1
View File
@@ -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,
};
});
});
+2 -18
View File
@@ -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
+2 -2
View File
@@ -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'],
};
+7 -6
View File
@@ -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();
+1 -1
View File
@@ -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
+2
View File
@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
+8 -1
View File
@@ -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',
],
}
+8 -17
View File
@@ -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
+38 -56
View File
@@ -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>
+7
View File
@@ -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
+43
View File
@@ -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
+23
View File
@@ -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
+10
View File
@@ -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>
+130
View File
@@ -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>&amp;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>
+16
View File
@@ -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>
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+19
View File
@@ -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>
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+20
View File
@@ -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;">&amp;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 &lt; 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>
+1 -1
View File
@@ -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': [
+8 -32
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+19
View File
@@ -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>
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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) {
+5 -5
View File
@@ -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;
+4
View File
@@ -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 -2
View File
@@ -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">
+1 -1
View File
@@ -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>
+2 -2
View File
@@ -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">
-3
View File
@@ -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({})
+7 -14
View File
@@ -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
+4 -1
View File
@@ -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>
+2 -1
View File
@@ -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) {
+16 -14
View File
@@ -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({
+1 -1
View File
@@ -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>
+2 -1
View File
@@ -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>
+1 -1
View File
@@ -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]
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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>
+2 -1
View File
@@ -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>
+1 -1
View File
@@ -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){
+1
View File
@@ -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')
+95 -22
View File
@@ -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
+6 -30
View File
@@ -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>
+162 -39
View File
@@ -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):
+16 -20
View File
@@ -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})
+1 -1
View File
@@ -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
*/
+1 -1
View File
@@ -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
+2
View File
@@ -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() {
+29 -28
View File
@@ -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
*
+8 -7
View File
@@ -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) {
+7 -7
View File
@@ -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]
+6 -2
View File
@@ -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
+39 -25
View File
@@ -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);
+5 -5
View File
@@ -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(
+1 -3
View File
@@ -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
+6 -6
View File
@@ -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;
+34 -34
View File
@@ -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