summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2015-01-19 17:15:56 +0100
committerChristophe Simonis <chs@odoo.com>2015-01-19 17:15:56 +0100
commit18e22be138f4d273ce8f63c0ff2c8a437cf6ce5d (patch)
tree63bd658de7b6fa0e91d521ca19322dd68bdc1492 /addons
parentf6e5bf847b1a7107c09b009a757823b0f9514fa4 (diff)
parent0aab81cd3ac6881b2b64ccedb901b55badd421d4 (diff)
[MERGE] forward port of branch 8.0 up to 0aab81c
Diffstat (limited to 'addons')
-rw-r--r--addons/account/__openerp__.py1
-rw-r--r--addons/account/account_bank_statement.py2
-rw-r--r--addons/account/account_view.xml2
-rw-r--r--addons/account/static/src/css/account_bank_statement_reconciliation.css4
-rw-r--r--addons/account/static/src/css/account_bank_statement_reconciliation.scss7
-rw-r--r--addons/account/static/src/js/account_widgets.js110
-rw-r--r--addons/account/static/src/xml/account_bank_statement_reconciliation.xml1
-rw-r--r--addons/account/tests/__init__.py6
-rw-r--r--addons/account_analytic_analysis/__openerp__.py1
-rw-r--r--addons/account_analytic_default/__openerp__.py1
-rw-r--r--addons/account_analytic_plans/__openerp__.py1
-rw-r--r--addons/account_anglo_saxon/__openerp__.py1
-rw-r--r--addons/account_budget/__openerp__.py1
-rw-r--r--addons/account_cancel/__openerp__.py1
-rw-r--r--addons/account_followup/__openerp__.py1
-rw-r--r--addons/account_followup/tests/__init__.py3
-rw-r--r--addons/account_payment/__openerp__.py1
-rw-r--r--addons/account_sequence/__openerp__.py1
-rw-r--r--addons/account_voucher/__openerp__.py1
-rw-r--r--addons/account_voucher/account_voucher_view.xml23
-rw-r--r--addons/account_voucher/voucher_payment_receipt_view.xml2
-rw-r--r--addons/analytic_user_function/__openerp__.py1
-rw-r--r--addons/anonymization/__openerp__.py1
-rw-r--r--addons/association/__openerp__.py1
-rw-r--r--addons/auth_ldap/__openerp__.py1
-rw-r--r--addons/auth_oauth/controllers/main.py4
-rw-r--r--addons/base_action_rule/__openerp__.py1
-rw-r--r--addons/base_action_rule/tests/__init__.py3
-rw-r--r--addons/base_geolocalize/__openerp__.py1
-rw-r--r--addons/base_iban/__openerp__.py1
-rw-r--r--addons/base_import/tests/__init__.py2
-rw-r--r--addons/base_setup/__openerp__.py1
-rw-r--r--addons/base_setup/res_config.py2
-rw-r--r--addons/base_vat/__openerp__.py1
-rw-r--r--addons/board/__openerp__.py1
-rw-r--r--addons/calendar/__openerp__.py1
-rw-r--r--addons/calendar/calendar.py23
-rw-r--r--addons/claim_from_delivery/__openerp__.py1
-rw-r--r--addons/contacts/__openerp__.py1
-rw-r--r--addons/crm/__openerp__.py11
-rw-r--r--addons/crm/wizard/crm_lead_to_opportunity.py17
-rw-r--r--addons/crm_claim/__openerp__.py5
-rw-r--r--addons/crm_helpdesk/__openerp__.py1
-rw-r--r--addons/crm_helpdesk/crm_helpdesk_view.xml2
-rw-r--r--addons/crm_partner_assign/__openerp__.py1
-rw-r--r--addons/decimal_precision/__openerp__.py1
-rw-r--r--addons/decimal_precision/tests/__init__.py6
-rw-r--r--addons/delivery/__openerp__.py1
-rw-r--r--addons/delivery/stock.py6
-rw-r--r--addons/document/__openerp__.py1
-rw-r--r--addons/event/__openerp__.py1
-rw-r--r--addons/fetchmail/__openerp__.py1
-rw-r--r--addons/fleet/__openerp__.py3
-rw-r--r--addons/fleet/fleet_view.xml2
-rw-r--r--addons/gamification/tests/__init__.py4
-rw-r--r--addons/hr/__openerp__.py6
-rw-r--r--addons/hr_attendance/__openerp__.py1
-rw-r--r--addons/hr_contract/__openerp__.py1
-rw-r--r--addons/hr_evaluation/__openerp__.py3
-rw-r--r--addons/hr_expense/__openerp__.py1
-rw-r--r--addons/hr_holidays/__openerp__.py1
-rw-r--r--addons/hr_holidays/hr_holidays.py12
-rw-r--r--addons/hr_holidays/tests/__init__.py5
-rw-r--r--addons/hr_payroll/__openerp__.py6
-rw-r--r--addons/hr_payroll_account/__openerp__.py1
-rw-r--r--addons/hr_recruitment/__openerp__.py2
-rw-r--r--addons/hr_timesheet/__openerp__.py1
-rw-r--r--addons/hr_timesheet_invoice/__openerp__.py1
-rw-r--r--addons/hr_timesheet_sheet/__openerp__.py1
-rw-r--r--addons/hr_timesheet_sheet/hr_timesheet_sheet.py16
-rw-r--r--addons/hw_posbox_homepage/controllers/main.py4
-rw-r--r--addons/hw_proxy/controllers/main.py2
-rw-r--r--addons/knowledge/__openerp__.py1
-rw-r--r--addons/l10n_ar/__openerp__.py1
-rw-r--r--addons/l10n_be/__openerp__.py1
-rw-r--r--addons/l10n_be_coda/__openerp__.py1
-rw-r--r--addons/l10n_be_coda/wizard/account_coda_import.py7
-rw-r--r--addons/l10n_bo/__openerp__.py3
-rw-r--r--addons/l10n_ca/__openerp__.py1
-rw-r--r--addons/l10n_ch/__openerp__.py1
-rw-r--r--addons/l10n_cl/__openerp__.py1
-rw-r--r--addons/l10n_cn/__openerp__.py1
-rw-r--r--addons/l10n_co/__openerp__.py1
-rw-r--r--addons/l10n_cr/__openerp__.py1
-rw-r--r--addons/l10n_de/__openerp__.py1
-rw-r--r--addons/l10n_ec/__openerp__.py1
-rw-r--r--addons/l10n_fr/__openerp__.py1
-rw-r--r--addons/l10n_gr/__openerp__.py1
-rw-r--r--addons/l10n_gt/__openerp__.py1
-rw-r--r--addons/l10n_in/__openerp__.py1
-rw-r--r--addons/l10n_it/__openerp__.py1
-rw-r--r--addons/l10n_lu/__openerp__.py1
-rw-r--r--addons/l10n_ma/__openerp__.py1
-rw-r--r--addons/l10n_nl/__openerp__.py1
-rw-r--r--addons/l10n_pa/__openerp__.py1
-rw-r--r--addons/l10n_pe/__openerp__.py1
-rw-r--r--addons/l10n_th/__openerp__.py1
-rw-r--r--addons/l10n_tr/__openerp__.py1
-rw-r--r--addons/l10n_uk/__openerp__.py1
-rw-r--r--addons/l10n_uy/__openerp__.py1
-rw-r--r--addons/l10n_ve/__openerp__.py2
-rw-r--r--addons/lunch/__openerp__.py2
-rw-r--r--addons/lunch/tests/__init__.py3
-rw-r--r--addons/mail/__openerp__.py8
-rw-r--r--addons/mail/mail_thread.py38
-rw-r--r--addons/mail/static/src/js/mail.js10
-rw-r--r--addons/mail/tests/__init__.py11
-rw-r--r--addons/mail/tests/test_mail_gateway.py12
-rw-r--r--addons/mail/wizard/mail_compose_message.py2
-rw-r--r--addons/marketing/__openerp__.py1
-rw-r--r--addons/marketing_campaign/__openerp__.py1
-rw-r--r--addons/marketing_campaign_crm_demo/__openerp__.py1
-rw-r--r--addons/mass_mailing/tests/__init__.py6
-rw-r--r--addons/membership/__openerp__.py1
-rw-r--r--addons/mrp/__openerp__.py1
-rw-r--r--addons/mrp/mrp.py53
-rw-r--r--addons/mrp_byproduct/__openerp__.py1
-rw-r--r--addons/mrp_operations/__openerp__.py1
-rw-r--r--addons/mrp_repair/__openerp__.py1
-rw-r--r--addons/multi_company/__openerp__.py1
-rw-r--r--addons/note/__openerp__.py5
-rw-r--r--addons/note/tests/__init__.py3
-rw-r--r--addons/pad/__openerp__.py1
-rw-r--r--addons/payment/models/payment_acquirer.py3
-rw-r--r--addons/payment_adyen/tests/__init__.py6
-rw-r--r--addons/payment_buckaroo/tests/__init__.py6
-rw-r--r--addons/payment_ogone/tests/__init__.py6
-rw-r--r--addons/payment_paypal/tests/__init__.py6
-rw-r--r--addons/point_of_sale/__openerp__.py1
-rw-r--r--addons/point_of_sale/controllers/main.py3
-rw-r--r--addons/point_of_sale/point_of_sale.py81
-rw-r--r--addons/point_of_sale/point_of_sale_view.xml2
-rw-r--r--addons/point_of_sale/security/ir.model.access.csv5
-rw-r--r--addons/portal/tests/__init__.py3
-rw-r--r--addons/portal_project/tests/__init__.py3
-rw-r--r--addons/procurement/__openerp__.py1
-rw-r--r--addons/product/__openerp__.py1
-rw-r--r--addons/product/product.py7
-rw-r--r--addons/product/product_view.xml2
-rw-r--r--addons/product/tests/__init__.py5
-rw-r--r--addons/product_email_template/data/product_demo.xml2
-rw-r--r--addons/product_expiry/__openerp__.py1
-rw-r--r--addons/product_margin/__openerp__.py1
-rw-r--r--addons/product_visible_discount/__openerp__.py1
-rw-r--r--addons/project/__openerp__.py10
-rw-r--r--addons/project/tests/__init__.py3
-rw-r--r--addons/project_issue/__openerp__.py1
-rw-r--r--addons/project_issue_sheet/__openerp__.py1
-rw-r--r--addons/project_timesheet/__openerp__.py1
-rw-r--r--addons/purchase/__openerp__.py1
-rw-r--r--addons/purchase/purchase.py17
-rw-r--r--addons/purchase_analytic_plans/__openerp__.py1
-rw-r--r--addons/purchase_double_validation/__openerp__.py1
-rw-r--r--addons/purchase_requisition/__openerp__.py1
-rw-r--r--addons/purchase_requisition/purchase_requisition.py2
-rw-r--r--addons/report/static/src/js/qwebactionmanager.js15
-rw-r--r--addons/report_intrastat/__openerp__.py1
-rw-r--r--addons/report_webkit/__openerp__.py1
-rw-r--r--addons/resource/__openerp__.py1
-rw-r--r--addons/resource/tests/__init__.py5
-rw-r--r--addons/sale/__openerp__.py1
-rw-r--r--addons/sale/wizard/sale_line_invoice.py47
-rw-r--r--addons/sale/wizard/sale_make_invoice_advance.py3
-rw-r--r--addons/sale_analytic_plans/__openerp__.py1
-rw-r--r--addons/sale_crm/__openerp__.py1
-rw-r--r--addons/sale_journal/__openerp__.py1
-rw-r--r--addons/sale_margin/__openerp__.py1
-rw-r--r--addons/sale_mrp/__openerp__.py1
-rw-r--r--addons/sale_mrp/tests/__init__.py3
-rw-r--r--addons/sale_order_dates/__openerp__.py1
-rw-r--r--addons/sale_service/__openerp__.py1
-rw-r--r--addons/sale_stock/__openerp__.py1
-rw-r--r--addons/sale_stock/sale_stock.py4
-rw-r--r--addons/share/__openerp__.py1
-rw-r--r--addons/share/wizard/share_wizard.py4
-rw-r--r--addons/stock/__openerp__.py1
-rw-r--r--addons/stock/product_view.xml2
-rw-r--r--addons/stock/security/ir.model.access.csv2
-rw-r--r--addons/stock/stock.py15
-rw-r--r--addons/stock/stock_view.xml5
-rw-r--r--addons/stock/tests/__init__.py3
-rw-r--r--addons/stock/tests/common.py97
-rw-r--r--addons/stock/tests/test_stock_flow.py1097
-rw-r--r--addons/stock/views/report_stockpicking.xml55
-rw-r--r--addons/stock_account/__openerp__.py1
-rw-r--r--addons/stock_account/stock.py4
-rw-r--r--addons/stock_account/wizard/stock_valuation_history.py27
-rw-r--r--addons/stock_account/wizard/stock_valuation_history_view.xml2
-rw-r--r--addons/stock_dropshipping/__openerp__.py1
-rw-r--r--addons/stock_invoice_directly/__openerp__.py1
-rw-r--r--addons/stock_landed_costs/__openerp__.py1
-rw-r--r--addons/subscription/__openerp__.py1
-rw-r--r--addons/survey/__openerp__.py1
-rw-r--r--addons/survey/controllers/main.py6
-rw-r--r--addons/survey/static/src/js/survey.js57
-rw-r--r--addons/survey/survey.py2
-rw-r--r--addons/survey/test/__init__.py4
-rw-r--r--addons/warning/__openerp__.py1
-rw-r--r--addons/warning/warning.py25
-rw-r--r--addons/web/controllers/main.py29
-rw-r--r--addons/web/static/src/js/view_form.js28
-rw-r--r--addons/web/static/src/js/view_list_editable.js5
-rw-r--r--addons/web/static/src/js/view_tree.js22
-rw-r--r--addons/web/static/src/xml/base.xml9
-rw-r--r--addons/web/views/database_manager.html4
-rw-r--r--addons/web/views/database_selector.html2
-rw-r--r--addons/website/models/website.py3
-rw-r--r--addons/website/static/src/css/snippets.css5
-rw-r--r--addons/website/static/src/css/snippets.sass5
-rw-r--r--addons/website_crm/controllers/main.py10
-rw-r--r--addons/website_forum/models/forum.py10
-rw-r--r--addons/website_forum/views/forum.xml1
-rw-r--r--addons/website_mail/tests/__init__.py3
-rw-r--r--addons/website_project_issue/tests/__init__.py3
-rw-r--r--addons/website_sale/controllers/main.py5
-rw-r--r--addons/website_sale/models/product.py15
-rw-r--r--addons/website_sale/static/src/js/website_sale_payment.js2
-rw-r--r--addons/website_sale/views/templates.xml2
-rw-r--r--addons/website_twitter/static/src/js/website.twitter.animation.js3
219 files changed, 1789 insertions, 613 deletions
diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py
index 3abd46bc02f..6a426c6e774 100644
--- a/addons/account/__openerp__.py
+++ b/addons/account/__openerp__.py
@@ -48,7 +48,6 @@ Processes like maintaining general ledgers are done through the defined Financia
for a particular financial year and for preparation of vouchers there is a module named account_voucher.
""",
'website': 'https://www.odoo.com/page/billing',
- 'images' : ['images/accounts.jpeg','images/bank_statement.jpeg','images/cash_register.jpeg','images/chart_of_accounts.jpeg','images/customer_invoice.jpeg','images/journal_entries.jpeg'],
'depends' : ['base_setup', 'product', 'analytic', 'board', 'edi', 'report'],
'data': [
'security/account_security.xml',
diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py
index d06f85e5115..630af63708b 100644
--- a/addons/account/account_bank_statement.py
+++ b/addons/account/account_bank_statement.py
@@ -873,7 +873,7 @@ class account_bank_statement_line(osv.osv):
# Unfortunately, that spawns a "no access rights" error ; it shouldn't.
def _needaction_domain_get(self, cr, uid, context=None):
user = self.pool.get("res.users").browse(cr, uid, uid)
- return ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id]), ('journal_entry_ids', '=', False)]
+ return ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id]), ('journal_entry_ids', '=', False), ('account_id', '=', False)]
_order = "statement_id desc, sequence"
_name = "account.bank.statement.line"
diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml
index 83fcebb45ce..b0dbb55bd51 100644
--- a/addons/account/account_view.xml
+++ b/addons/account/account_view.xml
@@ -2554,7 +2554,7 @@
<field name="sign"/>
<field name="style_overwrite"/>
</group>
- <notebook attrs="{'invisible': [('type','not in',['accounts','account_type'])]}">
+ <notebook attrs="{'invisible': [('type','not in',['accounts','account_type', 'account_report'])]}">
<page string="Report">
<group>
<field name="display_detail" attrs="{'invisible': [('type','not in',['accounts','account_type'])]}"/>
diff --git a/addons/account/static/src/css/account_bank_statement_reconciliation.css b/addons/account/static/src/css/account_bank_statement_reconciliation.css
index 3543360fb26..a5710c2e0b1 100644
--- a/addons/account/static/src/css/account_bank_statement_reconciliation.css
+++ b/addons/account/static/src/css/account_bank_statement_reconciliation.css
@@ -55,6 +55,10 @@
top: 2px;
z-index: 10;
text-shadow: -1px -1px 0 #f5f5f5, 1px -1px 0 #f5f5f5, -1px 1px 0 #f5f5f5, 1px 1px 0 #f5f5f5; }
+ .openerp .oe_bank_statement_reconciliation .show_more_container {
+ text-align: center; }
+ .openerp .oe_bank_statement_reconciliation .show_more_container .show_more {
+ display: none; }
.openerp .oe_bank_statement_reconciliation .notification_area {
margin: 0 15px; }
.openerp .oe_bank_statement_reconciliation .interface_options {
diff --git a/addons/account/static/src/css/account_bank_statement_reconciliation.scss b/addons/account/static/src/css/account_bank_statement_reconciliation.scss
index 1b4acdb2ee5..a773e20b4f4 100644
--- a/addons/account/static/src/css/account_bank_statement_reconciliation.scss
+++ b/addons/account/static/src/css/account_bank_statement_reconciliation.scss
@@ -92,6 +92,13 @@ $aestetic_animation_speed: 300ms;
}
}
+ .show_more_container {
+ text-align: center;
+
+ .show_more {
+ display: none;
+ }
+ }
.notification_area {
margin: 0 $actionColWidth;
}
diff --git a/addons/account/static/src/js/account_widgets.js b/addons/account/static/src/js/account_widgets.js
index 126ceaa6d94..136c571f495 100644
--- a/addons/account/static/src/js/account_widgets.js
+++ b/addons/account/static/src/js/account_widgets.js
@@ -15,11 +15,13 @@ openerp.account = function (instance) {
"click .statement_name span": "statementNameClickHandler",
"keyup .change_statement_name_field": "changeStatementNameFieldHandler",
"click .change_statement_name_button": "changeStatementButtonClickHandler",
+ "click .show_more": "showMoreButtonClickHandler",
},
init: function(parent, context) {
this._super(parent);
- this.max_reconciliations_displayed = 10;
+ // Number of reconciliations loaded initially and by clicking 'show more'
+ this.num_reconciliations_fetched_in_batch = 10;
if (context.context.statement_id) this.statement_ids = [context.context.statement_id];
if (context.context.statement_ids) this.statement_ids = context.context.statement_ids;
this.single_statement = this.statement_ids !== undefined && this.statement_ids.length === 1;
@@ -237,7 +239,7 @@ openerp.account = function (instance) {
total_lines: self.already_reconciled_lines+self.st_lines.length
}));
self.updateProgressbar();
- var reconciliations_to_show = self.st_lines.slice(0, self.max_reconciliations_displayed);
+ var reconciliations_to_show = self.st_lines.slice(0, self.num_reconciliations_fetched_in_batch);
self.last_displayed_reconciliation_index = reconciliations_to_show.length;
self.$(".reconciliation_lines_container").css("opacity", 0);
@@ -251,6 +253,7 @@ openerp.account = function (instance) {
$.when.apply($, child_promises).then(function(){
self.$(".reconciliation_lines_container").animate({opacity: 1}, self.aestetic_animation_speed);
self.getChildren()[0].set("mode", "match");
+ self.updateShowMoreButton();
if (self.notifications) {
self.displayNotifications(self.notifications);
}
@@ -340,7 +343,7 @@ openerp.account = function (instance) {
var child = reconciliations[i];
data.push([child.st_line_id, child.makeMoveLineDicts()]);
}
- var deferred_animation = self.$(".reconciliation_lines_container").fadeOut(self.aestetic_animation_speed);
+ var deferred_animation = self.$(".reconciliation_lines_container, .show_more_container").fadeOut(self.aestetic_animation_speed);
deferred_rpc = self.model_bank_statement_line.call("process_reconciliations", [data]);
return $.when(deferred_animation, deferred_rpc)
.done(function() {
@@ -359,48 +362,25 @@ openerp.account = function (instance) {
// Display new line if there are left
if (self.last_displayed_reconciliation_index < self.st_lines.length) {
- var begin = self.last_displayed_reconciliation_index;
- var end = Math.min((begin+self.max_reconciliations_displayed), self.st_lines.length);
- var reconciliations_to_show = self.st_lines.slice(begin, end);
-
- return self.model_bank_statement_line
- .call("get_data_for_reconciliations", [reconciliations_to_show])
- .then(function (data) {
- var child_promises = [];
- var datum;
- while ((datum = data.shift()) !== undefined) {
- var context = {
- st_line_id: datum.st_line.id,
- mode: 'inactive',
- animate_entrance: false,
- initial_data_provided: true,
- st_line: datum.st_line,
- reconciliation_proposition: datum.reconciliation_proposition,
- };
- var widget = new instance.web.account.bankStatementReconciliationLine(self, context);
- child_promises.push(widget.appendTo(self.$(".reconciliation_lines_container")));
+ return self.displayReconciliations(self.num_reconciliations_fetched_in_batch).then(function() {
+ // Put the first line in match mode
+ if (self.reconciled_lines !== self.st_lines.length) {
+ var first_child = self.getChildren()[0];
+ if (first_child.get("mode") === "inactive") {
+ first_child.set("mode", "match");
}
- self.last_displayed_reconciliation_index += reconciliations_to_show.length;
- return $.when.apply($, child_promises).then(function() {
- // Put the first line in match mode
- if (self.reconciled_lines !== self.st_lines.length) {
- var first_child = self.getChildren()[0];
- if (first_child.get("mode") === "inactive") {
- first_child.set("mode", "match");
- }
- }
- self.$(".reconciliation_lines_container").fadeIn(self.aestetic_animation_speed);
- });
- });
+ }
+ self.$(".reconciliation_lines_container, .show_more_container").fadeIn(self.aestetic_animation_speed);
+ });
} else if (self.reconciled_lines === self.st_lines.length) {
// Congratulate the user if the work is done
self.displayDoneMessage();
} else {
// Some lines weren't persisted because they were't valid
- self.$(".reconciliation_lines_container").fadeIn(self.aestetic_animation_speed);
+ self.$(".reconciliation_lines_container, .show_more_container").fadeIn(self.aestetic_animation_speed);
}
}).fail(function() {
- self.$(".reconciliation_lines_container").fadeIn(self.aestetic_animation_speed);
+ self.$(".reconciliation_lines_container, .show_more_container").fadeIn(self.aestetic_animation_speed);
});
},
@@ -433,7 +413,8 @@ openerp.account = function (instance) {
// Update children if needed
_.each(self.getChildren(), function(child){
- if ((child.partner_id === partner_id || child.st_line.has_no_partner) && child !== source_child) {
+ if (child === source_child || child.st_line === undefined) return;
+ if (child.partner_id === partner_id || child.st_line.has_no_partner) {
if (contains_lines(child.get("mv_lines_selected"), line_ids)) {
child.set("mv_lines_selected", _.filter(child.get("mv_lines_selected"), function(o){ return line_ids.indexOf(o.id) === -1 }));
} else if (contains_lines(child.mv_lines_deselected, line_ids)) {
@@ -457,6 +438,7 @@ openerp.account = function (instance) {
// Update children if needed
_.each(self.getChildren(), function(child){
+ if (child.st_line === undefined) return;
if (child.partner_id === partner_id && child !== source_child && (child.get("mode") === "match" || child.$el.hasClass("no_match")))
child.updateMatches();
if (child.st_line.has_no_partner && child.get("mode") === "match" || child.$el.hasClass("no_match"))
@@ -464,6 +446,42 @@ openerp.account = function (instance) {
});
},
+ displayReconciliations: function(number) {
+ var self = this;
+ var begin = self.last_displayed_reconciliation_index;
+ var end = Math.min((begin+number), self.st_lines.length);
+ var reconciliations_to_show = self.st_lines.slice(begin, end);
+
+ // Get ids of selected move lines (to exclude them from reconciliation proposition)
+ var excluded_move_lines_ids = [];
+ _.each(self.excluded_move_lines_ids, function(o){
+ excluded_move_lines_ids = excluded_move_lines_ids.concat(o);
+ });
+
+ return self.model_bank_statement_line
+ .call("get_data_for_reconciliations", [reconciliations_to_show, excluded_move_lines_ids])
+ .then(function (data) {
+ var child_promises = [];
+ var datum;
+ while ((datum = data.shift()) !== undefined) {
+ var context = {
+ st_line_id: datum.st_line.id,
+ mode: 'inactive',
+ animate_entrance: false,
+ initial_data_provided: true,
+ st_line: datum.st_line,
+ reconciliation_proposition: datum.reconciliation_proposition,
+ };
+ var widget = new instance.web.account.bankStatementReconciliationLine(self, context);
+ child_promises.push(widget.appendTo(self.$(".reconciliation_lines_container")));
+ }
+ self.last_displayed_reconciliation_index += reconciliations_to_show.length;
+ return $.when.apply($, child_promises).then(function(){
+ self.updateShowMoreButton();
+ });
+ });
+ },
+
displayReconciliation: function(st_line_id, mode, animate_entrance, initial_data_provided, st_line, reconciliation_proposition) {
var self = this;
animate_entrance = (animate_entrance === undefined ? true : animate_entrance);
@@ -489,7 +507,7 @@ openerp.account = function (instance) {
self.doReloadMenuReconciliation();
// Display new line if there are left
- if (self.last_displayed_reconciliation_index < self.st_lines.length) {
+ if (self.last_displayed_reconciliation_index < self.st_lines.length && self.getChildren().length < self.num_reconciliations_fetched_in_batch) {
self.displayReconciliation(self.st_lines[self.last_displayed_reconciliation_index++], 'inactive');
}
// Congratulate the user if the work is done
@@ -504,6 +522,7 @@ openerp.account = function (instance) {
first_child.set("mode", "match");
}
}
+ self.updateShowMoreButton();
},
goBackToStatementsTreeView: function() {
@@ -557,6 +576,7 @@ openerp.account = function (instance) {
// Render it
self.$(".protip").hide();
+ self.updateShowMoreButton();
self.$(".oe_form_sheet").append(QWeb.render("bank_statement_reconciliation_done_message", {
title: title,
time_taken: time_taken,
@@ -606,7 +626,19 @@ openerp.account = function (instance) {
});
}
},
+
+ showMoreButtonClickHandler: function() {
+ this.displayReconciliations(this.num_reconciliations_fetched_in_batch);
+ },
+ updateShowMoreButton: function() {
+ var items_remaining = this.st_lines.length - this.last_displayed_reconciliation_index;
+ if (items_remaining > 0)
+ this.$(".show_more").show().find(".num_items_remaining").text(items_remaining);
+ else
+ this.$(".show_more").hide();
+ },
+
updateProgressbar: function() {
var self = this;
var done = self.already_reconciled_lines + self.reconciled_lines;
diff --git a/addons/account/static/src/xml/account_bank_statement_reconciliation.xml b/addons/account/static/src/xml/account_bank_statement_reconciliation.xml
index 415be5dde94..9d3ec4a67a4 100644
--- a/addons/account/static/src/xml/account_bank_statement_reconciliation.xml
+++ b/addons/account/static/src/xml/account_bank_statement_reconciliation.xml
@@ -34,6 +34,7 @@
<div class="oe_clear"></div>
<div class="notification_area"></div>
<div class="reconciliation_lines_container"></div>
+ <div class="show_more_container"><button class="show_more">Show more... (<span class="num_items_remaining"></span> remaining)</button></div>
<p class='protip'>Tip : Hit ctrl-enter to validate the whole sheet.</p>
</div></div>
</t>
diff --git a/addons/account/tests/__init__.py b/addons/account/tests/__init__.py
index e09bcc6ee99..8a0a9394c3a 100644
--- a/addons/account/tests/__init__.py
+++ b/addons/account/tests/__init__.py
@@ -1,9 +1,3 @@
from . import test_tax
from . import test_search
from . import test_reconciliation
-
-fast_suite = [
- test_tax,
- test_search,
- test_reconciliation,
-]
diff --git a/addons/account_analytic_analysis/__openerp__.py b/addons/account_analytic_analysis/__openerp__.py
index 3e376c526ca..6fe9d954345 100644
--- a/addons/account_analytic_analysis/__openerp__.py
+++ b/addons/account_analytic_analysis/__openerp__.py
@@ -32,7 +32,6 @@ Adds menu to show relevant information to each manager.You can also view the rep
""",
'author': 'Camptocamp / Odoo',
'website': 'https://www.odoo.com/page/billing',
- 'images': ['images/bill_tasks_works.jpeg','images/overpassed_accounts.jpeg'],
'depends': ['hr_timesheet_invoice', 'sale'], #although sale is technically not required to install this module, all menuitems are located under 'Sales' application
'data': [
'security/ir.model.access.csv',
diff --git a/addons/account_analytic_default/__openerp__.py b/addons/account_analytic_default/__openerp__.py
index 74ae986d163..6aa65c4a6c4 100644
--- a/addons/account_analytic_default/__openerp__.py
+++ b/addons/account_analytic_default/__openerp__.py
@@ -37,7 +37,6 @@ Allows to automatically select analytic accounts based on criterions:
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/accounting',
- 'images': ['images/analytic_defaults.jpeg'],
'depends': ['sale_stock'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/account_analytic_plans/__openerp__.py b/addons/account_analytic_plans/__openerp__.py
index d4c99aa8fd3..9b5e467e69f 100644
--- a/addons/account_analytic_plans/__openerp__.py
+++ b/addons/account_analytic_plans/__openerp__.py
@@ -64,7 +64,6 @@ The analytic plan validates the minimum and maximum percentage at the time of cr
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/accounting',
- 'images': ['images/analytic_plan.jpeg'],
'depends': ['account', 'account_analytic_default'],
'data': [
'security/account_analytic_plan_security.xml',
diff --git a/addons/account_anglo_saxon/__openerp__.py b/addons/account_anglo_saxon/__openerp__.py
index 89c7768ce6b..ee4f7437785 100644
--- a/addons/account_anglo_saxon/__openerp__.py
+++ b/addons/account_anglo_saxon/__openerp__.py
@@ -39,7 +39,6 @@ account when the invoice is created to transfer this amount to the
debtor or creditor account. Secondly, price differences between actual
purchase price and fixed product standard price are booked on a separate
account.""",
- 'images': ['images/account_anglo_saxon.jpeg'],
'depends': ['product', 'purchase'],
'category': 'Accounting & Finance',
'demo': [],
diff --git a/addons/account_budget/__openerp__.py b/addons/account_budget/__openerp__.py
index 116e84fc960..352914eaa21 100644
--- a/addons/account_budget/__openerp__.py
+++ b/addons/account_budget/__openerp__.py
@@ -48,7 +48,6 @@ Three reports are available:
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/accounting',
- 'images': ['images/budget.jpeg','images/budgetary_position.jpeg'],
'depends': ['account'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/account_cancel/__openerp__.py b/addons/account_cancel/__openerp__.py
index f27901c6c55..7af5d2bd7f0 100644
--- a/addons/account_cancel/__openerp__.py
+++ b/addons/account_cancel/__openerp__.py
@@ -32,7 +32,6 @@ This module adds 'Allow Canceling Entries' field on form view of account journal
If set to true it allows user to cancel entries & invoices.
""",
'website': 'https://www.odoo.com/page/accounting',
- 'images': ['images/account_cancel.jpeg'],
'depends' : ['account'],
'data': ['account_cancel_view.xml' ],
'demo': [],
diff --git a/addons/account_followup/__openerp__.py b/addons/account_followup/__openerp__.py
index fa5d97a3a46..c612d8f6b63 100644
--- a/addons/account_followup/__openerp__.py
+++ b/addons/account_followup/__openerp__.py
@@ -45,7 +45,6 @@ Note that if you want to check the follow-up level for a given partner/account e
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/billing',
- 'images': ['images/follow_ups.jpeg','images/send_followups.jpeg'],
'depends': ['account_accountant', 'mail'],
'data': [
'security/account_followup_security.xml',
diff --git a/addons/account_followup/tests/__init__.py b/addons/account_followup/tests/__init__.py
index abad074cc09..84a004e7a11 100644
--- a/addons/account_followup/tests/__init__.py
+++ b/addons/account_followup/tests/__init__.py
@@ -20,6 +20,3 @@
##############################################################################
from . import test_account_followup
-checks = [
- test_account_followup,
-]
diff --git a/addons/account_payment/__openerp__.py b/addons/account_payment/__openerp__.py
index 83940178180..6c04b162e33 100644
--- a/addons/account_payment/__openerp__.py
+++ b/addons/account_payment/__openerp__.py
@@ -42,7 +42,6 @@ when you get the confirmation from your bank that your order has been accepted
that you can book it in your accounting. To help you with that operation, you
have a new option to import payment orders as bank statement lines.
""",
- 'images': ['images/payment_mode.jpeg','images/payment_order.jpeg'],
'depends': ['account','account_voucher'],
'data': [
'security/account_payment_security.xml',
diff --git a/addons/account_sequence/__openerp__.py b/addons/account_sequence/__openerp__.py
index 8f12e4850b5..409fb111825 100644
--- a/addons/account_sequence/__openerp__.py
+++ b/addons/account_sequence/__openerp__.py
@@ -39,7 +39,6 @@ You can customize the following attributes of the sequence:
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com',
- 'images': ['images/internal_sequence_number.jpeg'],
'depends': ['account'],
'data': [
'account_sequence_data.xml',
diff --git a/addons/account_voucher/__openerp__.py b/addons/account_voucher/__openerp__.py
index 5dcfb7f28a6..09279f5eea3 100644
--- a/addons/account_voucher/__openerp__.py
+++ b/addons/account_voucher/__openerp__.py
@@ -42,7 +42,6 @@ This module manages:
'category': 'Accounting & Finance',
'sequence': 4,
'website' : 'https://www.odoo.com/page/billing',
- 'images' : ['images/customer_payment.jpeg','images/journal_voucher.jpeg','images/sales_receipt.jpeg','images/supplier_voucher.jpeg','images/customer_invoice.jpeg','images/customer_refunds.jpeg'],
'depends' : ['account'],
'demo' : [],
'data' : [
diff --git a/addons/account_voucher/account_voucher_view.xml b/addons/account_voucher/account_voucher_view.xml
index 353f725e52b..2ec6464d418 100644
--- a/addons/account_voucher/account_voucher_view.xml
+++ b/addons/account_voucher/account_voucher_view.xml
@@ -19,6 +19,24 @@
</tree>
</field>
</record>
+ <record model="ir.ui.view" id="view_voucher_tree_nocreate">
+ <field name="name">account.voucher.tree_nocreate</field>
+ <field name="model">account.voucher</field>
+ <field name="arch" type="xml">
+ <tree colors="blue:state == 'draft';gray:state == 'cancel';red:audit" string="Voucher Entries" create="0" edit="0">
+ <field name="date"/>
+ <field name="number"/>
+ <field name="reference"/>
+ <field name="partner_id"/>
+ <field name="journal_id" groups="account.group_account_user"/>
+ <field name="period_id" invisible="context.get('visible', True)"/>
+ <field name="type" invisible="context.get('visible', True)"/>
+ <field name="amount" sum="Total Amount"/>
+ <field name="audit" invisible="1"/>
+ <field name="state"/>
+ </tree>
+ </field>
+ </record>
<record model="ir.ui.view" id="view_voucher_line_form">
<field name="name">account.voucher.line.form</field>
<field name="model">account.voucher.line</field>
@@ -40,7 +58,7 @@
<field name="name">account.voucher.form</field>
<field name="model">account.voucher</field>
<field name="arch" type="xml">
- <form string="Accounting Voucher">
+ <form string="Accounting Voucher" create="0" edit="0">
<header>
<button name="proforma_voucher" string="Post" states="draft" class="oe_highlight"/>
<button name="cancel_voucher" string="Cancel Voucher" type="object" states="posted" confirm="Are you sure you want to unreconcile this record?"/>
@@ -160,12 +178,13 @@
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
<field name="context">{}</field>
- <field name="view_id" eval="view_voucher_tree"/>
+ <field name="view_id" eval="view_voucher_tree_nocreate"/>
<field name="search_view_id" ref="view_voucher_filter"/>
</record>
<record id="action_journal_voucher_tree" model="ir.actions.act_window.view">
<field eval="1" name="sequence"/>
<field name="view_mode">tree</field>
+ <field name="view_id" ref="view_voucher_tree_nocreate"/>
<field name="act_window_id" ref="action_voucher_list"/>
</record>
<record id="action_journal_voucher_form" model="ir.actions.act_window.view">
diff --git a/addons/account_voucher/voucher_payment_receipt_view.xml b/addons/account_voucher/voucher_payment_receipt_view.xml
index 8024942730e..e6c564177cb 100644
--- a/addons/account_voucher/voucher_payment_receipt_view.xml
+++ b/addons/account_voucher/voucher_payment_receipt_view.xml
@@ -396,7 +396,7 @@
</page>
</notebook>
<footer>
- <button name="button_proforma_voucher" string="Pay" class="oe_highlight" type="object"/> or
+ <button name="button_proforma_voucher" string="Register Payment" class="oe_highlight" type="object"/> or
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
</form>
diff --git a/addons/analytic_user_function/__openerp__.py b/addons/analytic_user_function/__openerp__.py
index d8fa800335c..630eb9ee6dc 100644
--- a/addons/analytic_user_function/__openerp__.py
+++ b/addons/analytic_user_function/__openerp__.py
@@ -39,7 +39,6 @@ compatible with older configurations.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/employees',
- 'images': ['images/analytic_user_function.jpeg'],
'depends': ['hr_timesheet_sheet'],
'data': ['analytic_user_function_view.xml', 'security/ir.model.access.csv'],
'demo': [],
diff --git a/addons/anonymization/__openerp__.py b/addons/anonymization/__openerp__.py
index 4415781dd94..89306819bcd 100644
--- a/addons/anonymization/__openerp__.py
+++ b/addons/anonymization/__openerp__.py
@@ -48,5 +48,4 @@ anonymization process to recover your previous data.
],
'installable': True,
'auto_install': False,
- 'images': ['images/anonymization1.jpeg','images/anonymization2.jpeg','images/anonymization3.jpeg'],
}
diff --git a/addons/association/__openerp__.py b/addons/association/__openerp__.py
index 7ed4c0d11f2..84b45367bf3 100644
--- a/addons/association/__openerp__.py
+++ b/addons/association/__openerp__.py
@@ -37,6 +37,5 @@ membership products (schemes).
'demo': [],
'installable': True,
'auto_install': False,
- 'images': ['images/association1.jpeg'],
'website': 'https://www.odoo.com'
}
diff --git a/addons/auth_ldap/__openerp__.py b/addons/auth_ldap/__openerp__.py
index 5373db4f475..e709ab96995 100644
--- a/addons/auth_ldap/__openerp__.py
+++ b/addons/auth_ldap/__openerp__.py
@@ -22,7 +22,6 @@
'name' : 'Authentication via LDAP',
'version' : '1.0',
'depends' : ['base'],
- 'images' : ['images/ldap_configuration.jpeg'],
'author' : 'OpenERP SA',
#'description': < auto-loaded from README file
'website' : 'https://www.odoo.com',
diff --git a/addons/auth_oauth/controllers/main.py b/addons/auth_oauth/controllers/main.py
index 55068bf2742..a5c3a3423e5 100644
--- a/addons/auth_oauth/controllers/main.py
+++ b/addons/auth_oauth/controllers/main.py
@@ -70,9 +70,9 @@ class OAuthLogin(Home):
return providers
def get_state(self, provider):
- redirect = request.params.get('redirect', 'web')
+ redirect = request.params.get('redirect') or 'web'
if not redirect.startswith(('//', 'http://', 'https://')):
- redirect = '%s%s' % (request.httprequest.url_root, redirect)
+ redirect = '%s%s' % (request.httprequest.url_root, redirect[1:] if redirect[0] == '/' else redirect)
state = dict(
d=request.session.db,
p=provider['id'],
diff --git a/addons/base_action_rule/__openerp__.py b/addons/base_action_rule/__openerp__.py
index f824468c52e..53857cf014b 100644
--- a/addons/base_action_rule/__openerp__.py
+++ b/addons/base_action_rule/__openerp__.py
@@ -44,5 +44,4 @@ trigger an automatic reminder email.
'demo': [],
'installable': True,
'auto_install': False,
- 'images': ['images/base_action_rule1.jpeg','images/base_action_rule2.jpeg','images/base_action_rule3.jpeg'],
}
diff --git a/addons/base_action_rule/tests/__init__.py b/addons/base_action_rule/tests/__init__.py
index 7e6a3e21939..7090de46a47 100644
--- a/addons/base_action_rule/tests/__init__.py
+++ b/addons/base_action_rule/tests/__init__.py
@@ -20,6 +20,3 @@
##############################################################################
from . import base_action_rule_test
-checks = [
- base_action_rule_test,
-]
diff --git a/addons/base_geolocalize/__openerp__.py b/addons/base_geolocalize/__openerp__.py
index ac2ac9b9e81..fe8e7b99e17 100644
--- a/addons/base_geolocalize/__openerp__.py
+++ b/addons/base_geolocalize/__openerp__.py
@@ -38,5 +38,4 @@ Partners geolocalization
'test': [],
'installable': True,
'auto_install': False,
- 'images': [],
}
diff --git a/addons/base_iban/__openerp__.py b/addons/base_iban/__openerp__.py
index acf0081d58c..b7196edbded 100644
--- a/addons/base_iban/__openerp__.py
+++ b/addons/base_iban/__openerp__.py
@@ -35,5 +35,4 @@ with a single statement.
'data': ['base_iban_data.xml' , 'base_iban_view.xml'],
'installable': True,
'auto_install': False,
- 'images': ['images/base_iban1.jpeg'],
}
diff --git a/addons/base_import/tests/__init__.py b/addons/base_import/tests/__init__.py
index abef28c8ca1..54ecbe9baab 100644
--- a/addons/base_import/tests/__init__.py
+++ b/addons/base_import/tests/__init__.py
@@ -1,3 +1 @@
from . import test_cases
-
-checks = [test_cases]
diff --git a/addons/base_setup/__openerp__.py b/addons/base_setup/__openerp__.py
index c8756384054..1276a4eff9c 100644
--- a/addons/base_setup/__openerp__.py
+++ b/addons/base_setup/__openerp__.py
@@ -44,5 +44,4 @@ Shows you a list of applications features to install from.
'demo': [],
'installable': True,
'auto_install': False,
- 'images': ['images/base_setup1.jpeg','images/base_setup2.jpeg','images/base_setup3.jpeg','images/base_setup4.jpeg',],
}
diff --git a/addons/base_setup/res_config.py b/addons/base_setup/res_config.py
index fd075e4330c..7742f19d77e 100644
--- a/addons/base_setup/res_config.py
+++ b/addons/base_setup/res_config.py
@@ -32,7 +32,7 @@ class base_config_settings(osv.osv_memory):
help='Work in multi-company environments, with appropriate security access between companies.\n'
'-This installs the module multi_company.'),
'module_share': fields.boolean('Allow documents sharing',
- help="""Share or embbed any screen of openerp."""),
+ help="""Share or embbed any screen of Odoo."""),
'module_portal': fields.boolean('Activate the customer portal',
help="""Give your customers access to their documents."""),
'module_auth_oauth': fields.boolean('Use external authentication providers, sign in with google, facebook, ...'),
diff --git a/addons/base_vat/__openerp__.py b/addons/base_vat/__openerp__.py
index 8f9f0c8784d..076eef77638 100644
--- a/addons/base_vat/__openerp__.py
+++ b/addons/base_vat/__openerp__.py
@@ -58,5 +58,4 @@ only the country code will be validated.
'data': ['base_vat_view.xml'],
'installable': True,
'auto_install': False,
- 'images': ['images/1_partner_vat.jpeg'],
}
diff --git a/addons/board/__openerp__.py b/addons/board/__openerp__.py
index f7b7d47a5ff..d6a98f62e76 100644
--- a/addons/board/__openerp__.py
+++ b/addons/board/__openerp__.py
@@ -41,5 +41,4 @@ Allows users to create custom dashboard.
'qweb': ['static/src/xml/*.xml'],
'installable': True,
'auto_install': False,
- 'images': ['images/1_dashboard_definition.jpeg','images/2_publish_note.jpeg','images/3_admin_dashboard.jpeg',],
}
diff --git a/addons/calendar/__openerp__.py b/addons/calendar/__openerp__.py
index 74dab47d99d..16ef09dd103 100644
--- a/addons/calendar/__openerp__.py
+++ b/addons/calendar/__openerp__.py
@@ -55,5 +55,4 @@ If you need to manage your meetings, you should install the CRM module.
'installable': True,
'application': True,
'auto_install': False,
- 'images': ['images/calendar1.jpeg', 'images/calendar2.jpeg', 'images/calendar3.jpeg', 'images/calendar4.jpeg'],
}
diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py
index e7c784d3865..6d950e32541 100644
--- a/addons/calendar/calendar.py
+++ b/addons/calendar/calendar.py
@@ -852,10 +852,11 @@ class calendar_event(osv.Model):
if values.get('start_datetime') or values.get('start_date') or values.get('start') \
or values.get('stop_datetime') or values.get('stop_date') or values.get('stop'):
allday = values.get("allday", None)
+ event = self.browse(cr, uid, id, context=context)
if allday is None:
if id:
- allday = self.read(cr, uid, [id], ['allday'], context=context)[0].get('allday')
+ allday = event.allday
else:
allday = False
_logger.debug("Calendar - All day is not specified, arbitrarily set to False")
@@ -872,10 +873,16 @@ class calendar_event(osv.Model):
values[fld] = values['%s_%s' % (fld, key)]
diff = False
- if allday and values.get('stop_date') and values.get('start_date'):
- diff = datetime.strptime(values['stop_date'].split(' ')[0], DEFAULT_SERVER_DATE_FORMAT) - datetime.strptime(values['start_date'].split(' ')[0], DEFAULT_SERVER_DATE_FORMAT)
- elif values.get('stop_datetime') and values.get('start_datetime'):
- diff = datetime.strptime(values['stop_datetime'].split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT) - datetime.strptime(values['start_datetime'].split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT)
+ if allday and (values.get('stop_date') or values.get('start_date')):
+ stop_date = values.get('stop_date') or event.stop_date
+ start_date = values.get('start_date') or event.start_date
+ if stop_date and start_date:
+ diff = datetime.strptime(stop_date.split(' ')[0], DEFAULT_SERVER_DATE_FORMAT) - datetime.strptime(start_date.split(' ')[0], DEFAULT_SERVER_DATE_FORMAT)
+ elif values.get('stop_datetime') or values.get('start_datetime'):
+ stop_datetime = values.get('stop_datetime') or event.stop_datetime
+ start_datetime = values.get('start_datetime') or event.start_datetime
+ if stop_datetime and start_datetime:
+ diff = datetime.strptime(stop_datetime.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT) - datetime.strptime(start_datetime.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT)
if diff:
duration = float(diff.days) * 24 + (float(diff.seconds) / 3600)
values['duration'] = round(duration, 2)
@@ -965,12 +972,14 @@ class calendar_event(osv.Model):
def _check_closing_date(self, cr, uid, ids, context=None):
for event in self.browse(cr, uid, ids, context=context):
- if event.stop < event.start:
+ if event.start_datetime and event.stop_datetime < event.start_datetime:
+ return False
+ if event.start_date and event.stop_date < event.start_date:
return False
return True
_constraints = [
- (_check_closing_date, 'Error ! End date cannot be set before start date.', ['start', 'stop'])
+ (_check_closing_date, 'Error ! End date cannot be set before start date.', ['start_datetime', 'stop_datetime', 'start_date', 'stop_date'])
]
def onchange_allday(self, cr, uid, ids, start=False, end=False, starttime=False, endtime=False, startdatetime=False, enddatetime=False, checkallday=False, context=None):
diff --git a/addons/claim_from_delivery/__openerp__.py b/addons/claim_from_delivery/__openerp__.py
index cfbe1e69bc1..270d5479e31 100644
--- a/addons/claim_from_delivery/__openerp__.py
+++ b/addons/claim_from_delivery/__openerp__.py
@@ -35,5 +35,4 @@ Adds a Claim link to the delivery order.
'claim_delivery_data.xml',],
'auto_install': False,
'installable': True,
- 'images': ['images/1_claim_link_delivery_order.jpeg'],
}
diff --git a/addons/contacts/__openerp__.py b/addons/contacts/__openerp__.py
index 2163fba3949..57733aa8661 100644
--- a/addons/contacts/__openerp__.py
+++ b/addons/contacts/__openerp__.py
@@ -36,7 +36,6 @@ You can track your suppliers, customers and other contacts.
'data': [
'contacts_view.xml',
],
- 'images': ['images/contacts.jpeg'],
'installable': True,
'application': True,
'auto_install': True,
diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py
index 737551cb424..b91c8e18759 100644
--- a/addons/crm/__openerp__.py
+++ b/addons/crm/__openerp__.py
@@ -119,15 +119,4 @@ Dashboard for CRM will include:
'installable': True,
'application': True,
'auto_install': False,
- 'images': [
- 'images/customers.png',
- 'images/leads.png',
- 'images/opportunities_kanban.png',
- 'images/opportunities_form.png',
- 'images/opportunities_calendar.png',
- 'images/opportunities_graph.png',
- 'images/logged_calls.png',
- 'images/scheduled_calls.png',
- 'images/stages.png',
- ],
}
diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py
index 49e65151612..ac06e41b1c4 100644
--- a/addons/crm/wizard/crm_lead_to_opportunity.py
+++ b/addons/crm/wizard/crm_lead_to_opportunity.py
@@ -113,10 +113,11 @@ class crm_lead2opportunity_partner(osv.osv_memory):
res = False
lead_ids = vals.get('lead_ids', [])
team_id = vals.get('team_id', False)
+ partner_id = vals.get('partner_id')
data = self.browse(cr, uid, ids, context=context)[0]
leads = lead.browse(cr, uid, lead_ids, context=context)
for lead_id in leads:
- partner_id = self._create_partner(cr, uid, lead_id.id, data.action, lead_id.partner_id.id, context=context)
+ partner_id = self._create_partner(cr, uid, lead_id.id, data.action, partner_id or lead_id.partner_id.id, context=context)
res = lead.convert_opportunity(cr, uid, [lead_id.id], partner_id, [], False, context=context)
user_ids = vals.get('user_ids', False)
if context.get('no_force_assignation'):
@@ -139,18 +140,26 @@ class crm_lead2opportunity_partner(osv.osv_memory):
w = self.browse(cr, uid, ids, context=context)[0]
opp_ids = [o.id for o in w.opportunity_ids]
+ vals = {
+ 'team_id': w.team_id.id,
+ }
+ if w.partner_id:
+ vals['partner_id'] = w.partner_id.id
if w.name == 'merge':
lead_id = lead_obj.merge_opportunity(cr, uid, opp_ids, context=context)
lead_ids = [lead_id]
lead = lead_obj.read(cr, uid, lead_id, ['type', 'user_id'], context=context)
if lead['type'] == "lead":
context = dict(context, active_ids=lead_ids)
- self._convert_opportunity(cr, uid, ids, {'lead_ids': lead_ids, 'user_ids': [w.user_id.id], 'team_id': w.team_id.id}, context=context)
+ vals.update({'lead_ids': lead_ids, 'user_ids': [w.user_id.id]})
+ self._convert_opportunity(cr, uid, ids, vals, context=context)
elif not context.get('no_force_assignation') or not lead['user_id']:
- lead_obj.write(cr, uid, lead_id, {'user_id': w.user_id.id, 'team_id': w.team_id.id}, context=context)
+ vals.update({'user_id': w.user_id.id})
+ lead_obj.write(cr, uid, lead_id, vals, context=context)
else:
lead_ids = context.get('active_ids', [])
- self._convert_opportunity(cr, uid, ids, {'lead_ids': lead_ids, 'user_ids': [w.user_id.id], 'team_id': w.team_id.id}, context=context)
+ vals.update({'lead_ids': lead_ids, 'user_ids': [w.user_id.id]})
+ self._convert_opportunity(cr, uid, ids, vals, context=context)
return self.pool.get('crm.lead').redirect_opportunity_view(cr, uid, lead_ids[0], context=context)
diff --git a/addons/crm_claim/__openerp__.py b/addons/crm_claim/__openerp__.py
index 79f8470423b..d88f8756320 100644
--- a/addons/crm_claim/__openerp__.py
+++ b/addons/crm_claim/__openerp__.py
@@ -51,9 +51,4 @@ automatically new claims based on incoming emails.
],
'installable': True,
'auto_install': False,
- 'images': [
- 'images/claim_categories.jpeg',
- 'images/claim_stages.jpeg',
- 'images/claims.jpeg'
- ],
}
diff --git a/addons/crm_helpdesk/__openerp__.py b/addons/crm_helpdesk/__openerp__.py
index be2ee411f2a..1f0605a36ae 100644
--- a/addons/crm_helpdesk/__openerp__.py
+++ b/addons/crm_helpdesk/__openerp__.py
@@ -47,5 +47,4 @@ and categorize your interventions with a channel and a priority level.
'test': ['test/process/help-desk.yml'],
'installable': True,
'auto_install': False,
- 'images': ['images/helpdesk_analysis.jpeg','images/helpdesk_categories.jpeg','images/helpdesk_requests.jpeg'],
}
diff --git a/addons/crm_helpdesk/crm_helpdesk_view.xml b/addons/crm_helpdesk/crm_helpdesk_view.xml
index a15c4f8ade8..e9d223e932b 100644
--- a/addons/crm_helpdesk/crm_helpdesk_view.xml
+++ b/addons/crm_helpdesk/crm_helpdesk_view.xml
@@ -105,8 +105,8 @@
</notebook>
</sheet>
<div class="oe_chatter">
- <field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
diff --git a/addons/crm_partner_assign/__openerp__.py b/addons/crm_partner_assign/__openerp__.py
index 376107ec76f..97d28164184 100644
--- a/addons/crm_partner_assign/__openerp__.py
+++ b/addons/crm_partner_assign/__openerp__.py
@@ -54,5 +54,4 @@ to an appropriate local partner, based on the distance and the weight that was a
'test': ['test/partner_assign.yml'],
'installable': True,
'auto_install': False,
- 'images': ['images/partner_geo_localization.jpeg','images/partner_grade.jpeg'],
}
diff --git a/addons/decimal_precision/__openerp__.py b/addons/decimal_precision/__openerp__.py
index 817576347c6..851684064ed 100644
--- a/addons/decimal_precision/__openerp__.py
+++ b/addons/decimal_precision/__openerp__.py
@@ -37,5 +37,4 @@ The decimal precision is configured per company.
],
'demo': [],
'installable': True,
- 'images': ['images/1_decimal_accuracy_form.jpeg','images/1_decimal_accuracy_list.jpeg'],
}
diff --git a/addons/decimal_precision/tests/__init__.py b/addons/decimal_precision/tests/__init__.py
index feee24c8f15..b9343853f75 100644
--- a/addons/decimal_precision/tests/__init__.py
+++ b/addons/decimal_precision/tests/__init__.py
@@ -1,6 +1,2 @@
# -*- coding: utf-8 -*-
-import test_qweb_float
-
-checks = [
- test_qweb_float
-]
+from . import test_qweb_float
diff --git a/addons/delivery/__openerp__.py b/addons/delivery/__openerp__.py
index 710a2193608..1d1c59652c4 100644
--- a/addons/delivery/__openerp__.py
+++ b/addons/delivery/__openerp__.py
@@ -44,5 +44,4 @@ invoices from picking, OpenERP is able to add and compute the shipping line.
'test': ['test/delivery_cost.yml'],
'installable': True,
'auto_install': False,
- 'images': ['images/1_delivery_method.jpeg','images/2_delivery_pricelist.jpeg'],
}
diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py
index 2ccb1c41915..3f28e553a29 100644
--- a/addons/delivery/stock.py
+++ b/addons/delivery/stock.py
@@ -54,7 +54,7 @@ class stock_picking(osv.osv):
_columns = {
'carrier_id':fields.many2one("delivery.carrier","Carrier"),
- 'volume': fields.float('Volume'),
+ 'volume': fields.float('Volume', copy=False),
'weight': fields.function(_cal_weight, type='float', string='Weight', digits_compute= dp.get_precision('Stock Weight'), multi='_cal_weight',
store={
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 40),
@@ -65,8 +65,8 @@ class stock_picking(osv.osv):
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 40),
'stock.move': (_get_picking_line, ['picking_id', 'product_id','product_uom_qty','product_uom'], 40),
}),
- 'carrier_tracking_ref': fields.char('Carrier Tracking Ref'),
- 'number_of_packages': fields.integer('Number of Packages'),
+ 'carrier_tracking_ref': fields.char('Carrier Tracking Ref', copy=False),
+ 'number_of_packages': fields.integer('Number of Packages', copy=False),
'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of measurement for Weight",),
}
diff --git a/addons/document/__openerp__.py b/addons/document/__openerp__.py
index 25c82d3c84c..7a413f2423e 100644
--- a/addons/document/__openerp__.py
+++ b/addons/document/__openerp__.py
@@ -51,5 +51,4 @@ This is a complete document management system.
'test': ['test/document_test2.yml'],
'installable': True,
'auto_install': False,
- 'images': ['images/1_directories.jpeg','images/2_storage_media.jpeg','images/3_directories_structure.jpeg'],
}
diff --git a/addons/event/__openerp__.py b/addons/event/__openerp__.py
index 029933c1755..7ac32912056 100644
--- a/addons/event/__openerp__.py
+++ b/addons/event/__openerp__.py
@@ -37,5 +37,4 @@ Key Features
],
'installable': True,
'auto_install': False,
- 'images': ['images/1_event_type_list.jpeg', 'images/2_events.jpeg', 'images/3_registrations.jpeg', 'images/events_kanban.jpeg'],
}
diff --git a/addons/fetchmail/__openerp__.py b/addons/fetchmail/__openerp__.py
index be6a6a09a38..1c6ee8bf762 100644
--- a/addons/fetchmail/__openerp__.py
+++ b/addons/fetchmail/__openerp__.py
@@ -64,5 +64,4 @@ For more specific needs, you may also assign custom-defined actions
'demo': [],
'installable': True,
'auto_install': True,
- 'images': ['images/1_email_servers.jpeg'],
}
diff --git a/addons/fleet/__openerp__.py b/addons/fleet/__openerp__.py
index e2cc6595d46..dbe93618ee1 100644
--- a/addons/fleet/__openerp__.py
+++ b/addons/fleet/__openerp__.py
@@ -29,7 +29,7 @@
'description' : """
Vehicle, leasing, insurances, cost
==================================
-With this module, OpenERP helps you managing all your vehicles, the
+With this module, Odoo helps you managing all your vehicles, the
contracts associated to those vehicle as well as services, fuel log
entries, costs and many other features necessary to the management
of your fleet of vehicle(s)
@@ -56,7 +56,6 @@ Main Features
'fleet_data.xml',
'fleet_board_view.xml',
],
- 'images': ['images/costs_analysis.jpeg','images/indicative_costs_analysis.jpeg','images/vehicles.jpeg','images/vehicles_contracts.jpeg','images/vehicles_fuel.jpeg','images/vehicles_odometer.jpeg','images/vehicles_services.jpeg'],
'demo': ['fleet_demo.xml'],
diff --git a/addons/fleet/fleet_view.xml b/addons/fleet/fleet_view.xml
index ed22f2f922d..e1d66457e88 100644
--- a/addons/fleet/fleet_view.xml
+++ b/addons/fleet/fleet_view.xml
@@ -285,8 +285,8 @@
</group>
</sheet>
<div class="oe_chatter">
- <field name="message_ids" widget="mail_thread" options='{"thread_level": 1}'/>
<field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread" options='{"thread_level": 1}'/>
</div>
</form>
</field>
diff --git a/addons/gamification/tests/__init__.py b/addons/gamification/tests/__init__.py
index 9cafd242b28..49ae47e17c2 100644
--- a/addons/gamification/tests/__init__.py
+++ b/addons/gamification/tests/__init__.py
@@ -20,7 +20,3 @@
##############################################################################
from . import test_challenge
-
-checks = [
- test_challenge,
-]
diff --git a/addons/hr/__openerp__.py b/addons/hr/__openerp__.py
index 8239cfa4ebd..ba9cb8798e0 100644
--- a/addons/hr/__openerp__.py
+++ b/addons/hr/__openerp__.py
@@ -42,12 +42,6 @@ You can manage:
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/employees',
- 'images': [
- 'images/hr_department.jpeg',
- 'images/hr_employee.jpeg',
- 'images/hr_job_position.jpeg',
- 'static/src/img/default_image.png',
- ],
'depends': ['base_setup','mail', 'resource', 'board'],
'data': [
'security/hr_security.xml',
diff --git a/addons/hr_attendance/__openerp__.py b/addons/hr_attendance/__openerp__.py
index 146ca202a83..1574129fc81 100644
--- a/addons/hr_attendance/__openerp__.py
+++ b/addons/hr_attendance/__openerp__.py
@@ -33,7 +33,6 @@ actions(Sign in/Sign out) performed by them.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/employees',
- 'images': ['images/hr_attendances.jpeg'],
'depends': ['hr', 'report'],
'data': [
'security/ir_rule.xml',
diff --git a/addons/hr_contract/__openerp__.py b/addons/hr_contract/__openerp__.py
index 7aaeaa7014f..90c650d101e 100644
--- a/addons/hr_contract/__openerp__.py
+++ b/addons/hr_contract/__openerp__.py
@@ -37,7 +37,6 @@ You can assign several contracts per employee.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/employees',
- 'images': ['images/hr_contract.jpeg'],
'depends': ['base_action_rule', 'hr'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/hr_evaluation/__openerp__.py b/addons/hr_evaluation/__openerp__.py
index b00b37c4d51..7b0a458a8b2 100644
--- a/addons/hr_evaluation/__openerp__.py
+++ b/addons/hr_evaluation/__openerp__.py
@@ -27,9 +27,6 @@
'sequence': 31,
'website': 'https://www.odoo.com/page/appraisal',
'summary': 'Periodical Evaluations, Appraisals, Surveys',
- 'images': ['images/hr_evaluation_analysis.jpeg',
- 'images/hr_evaluation.jpeg',
- 'images/hr_interview_requests.jpeg'],
'depends': ['hr', 'calendar', 'survey'],
'description': """
Periodical Employees evaluation and appraisals
diff --git a/addons/hr_expense/__openerp__.py b/addons/hr_expense/__openerp__.py
index 77a1974abfe..05a75c83e85 100644
--- a/addons/hr_expense/__openerp__.py
+++ b/addons/hr_expense/__openerp__.py
@@ -45,7 +45,6 @@ This module also uses analytic accounting and is compatible with the invoice on
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/expenses',
- 'images': ['images/hr_expenses_analysis.jpeg', 'images/hr_expenses.jpeg'],
'depends': ['hr', 'account_accountant', 'report'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/hr_holidays/__openerp__.py b/addons/hr_holidays/__openerp__.py
index 07ba46b13b5..4f1b2e2265d 100644
--- a/addons/hr_holidays/__openerp__.py
+++ b/addons/hr_holidays/__openerp__.py
@@ -44,7 +44,6 @@ You can keep track of leaves in different ways by following reports:
A synchronization with an internal agenda (Meetings of the CRM module) is also possible in order to automatically create a meeting when a holiday request is accepted by setting up a type of meeting in Leave Type.
""",
- 'images': ['images/hr_allocation_requests.jpeg', 'images/hr_leave_requests.jpeg', 'images/leaves_analysis.jpeg'],
'depends': ['hr', 'calendar', 'resource'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py
index b8ec3793d71..a7f7d72b6c8 100644
--- a/addons/hr_holidays/hr_holidays.py
+++ b/addons/hr_holidays/hr_holidays.py
@@ -53,14 +53,14 @@ class hr_holidays_status(osv.osv):
# note: add only validated allocation even for the virtual
# count; otherwise pending then refused allocation allow
# the employee to create more leaves than possible
- status_dict['virtual_remaining_leaves'] += holiday.number_of_days
- status_dict['max_leaves'] += holiday.number_of_days
- status_dict['remaining_leaves'] += holiday.number_of_days
+ status_dict['virtual_remaining_leaves'] += holiday.number_of_days_temp
+ status_dict['max_leaves'] += holiday.number_of_days_temp
+ status_dict['remaining_leaves'] += holiday.number_of_days_temp
elif holiday.type == 'remove': # number of days is negative
- status_dict['virtual_remaining_leaves'] += holiday.number_of_days
+ status_dict['virtual_remaining_leaves'] -= holiday.number_of_days_temp
if holiday.state == 'validate':
- status_dict['leaves_taken'] -= holiday.number_of_days
- status_dict['remaining_leaves'] += holiday.number_of_days
+ status_dict['leaves_taken'] += holiday.number_of_days_temp
+ status_dict['remaining_leaves'] -= holiday.number_of_days_temp
return result
def _user_left_days(self, cr, uid, ids, name, args, context=None):
diff --git a/addons/hr_holidays/tests/__init__.py b/addons/hr_holidays/tests/__init__.py
index 08642bb5645..2d8d5ee4cdf 100644
--- a/addons/hr_holidays/tests/__init__.py
+++ b/addons/hr_holidays/tests/__init__.py
@@ -19,8 +19,5 @@
#
##############################################################################
-from openerp.addons.hr_holidays.tests import test_holidays_flow
+from . import test_holidays_flow
-checks = [
- test_holidays_flow,
-]
diff --git a/addons/hr_payroll/__openerp__.py b/addons/hr_payroll/__openerp__.py
index f444cbb5731..f5d0a7c37ae 100644
--- a/addons/hr_payroll/__openerp__.py
+++ b/addons/hr_payroll/__openerp__.py
@@ -40,12 +40,6 @@ Generic Payroll system.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/employees',
- 'images': [
- 'images/hr_company_contributions.jpeg',
- 'images/hr_salary_heads.jpeg',
- 'images/hr_salary_structure.jpeg',
- 'images/hr_employee_payslip.jpeg'
- ],
'depends': [
'hr',
'hr_contract',
diff --git a/addons/hr_payroll_account/__openerp__.py b/addons/hr_payroll_account/__openerp__.py
index dedc9f260dd..1bd591c8706 100644
--- a/addons/hr_payroll_account/__openerp__.py
+++ b/addons/hr_payroll_account/__openerp__.py
@@ -32,7 +32,6 @@ Generic Payroll system Integrated with Accounting.
* Company Contribution Management
""",
'author':'OpenERP SA',
- 'images': ['images/hr_employee_payslip.jpeg'],
'website': 'https://www.odoo.com/page/employees',
'depends': [
'hr_payroll',
diff --git a/addons/hr_recruitment/__openerp__.py b/addons/hr_recruitment/__openerp__.py
index 20745f84c01..3ab147ddf2f 100644
--- a/addons/hr_recruitment/__openerp__.py
+++ b/addons/hr_recruitment/__openerp__.py
@@ -37,8 +37,6 @@ You can define the different phases of interviews and easily rate the applicant
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/recruitment',
- 'images': ['images/hr_recruitment_analysis.jpeg',
- 'images/hr_recruitment_applicants.jpeg'],
'depends': [
'decimal_precision',
'hr',
diff --git a/addons/hr_timesheet/__openerp__.py b/addons/hr_timesheet/__openerp__.py
index 7a0e9a66139..bb554756d09 100644
--- a/addons/hr_timesheet/__openerp__.py
+++ b/addons/hr_timesheet/__openerp__.py
@@ -40,7 +40,6 @@ up a management by affair.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/employees',
- 'images': ['images/hr_timesheet_lines.jpeg'],
'depends': ['account', 'hr', 'base', 'hr_attendance'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/hr_timesheet_invoice/__openerp__.py b/addons/hr_timesheet_invoice/__openerp__.py
index 8fceff84d08..1eb003b1e4f 100644
--- a/addons/hr_timesheet_invoice/__openerp__.py
+++ b/addons/hr_timesheet_invoice/__openerp__.py
@@ -34,7 +34,6 @@ You can define price lists in analytic account, make some theoretical revenue
reports.""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/employees',
- 'images': ['images/hr_bill_task_work.jpeg','images/hr_type_of_invoicing.jpeg'],
'depends': ['account', 'hr_timesheet', 'report'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/hr_timesheet_sheet/__openerp__.py b/addons/hr_timesheet_sheet/__openerp__.py
index 287b011719b..4c3f861a399 100644
--- a/addons/hr_timesheet_sheet/__openerp__.py
+++ b/addons/hr_timesheet_sheet/__openerp__.py
@@ -45,7 +45,6 @@ The validation can be configured in the company:
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/employees',
- 'images': ['images/hr_my_current_timesheet.jpeg','images/hr_timesheet_analysis.jpeg','images/hr_timesheet_sheet_analysis.jpeg','images/hr_timesheet_activity.jpeg'],
'depends': ['hr_timesheet', 'hr_timesheet_invoice'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
index a10cf4e84a3..7a8b2d1f727 100644
--- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
+++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
@@ -228,7 +228,7 @@ class hr_timesheet_sheet(osv.osv):
def _sheet_date(self, cr, uid, ids, forced_user_id=False, context=None):
for sheet in self.browse(cr, uid, ids, context=context):
- new_user_id = forced_user_id or sheet.user_id and sheet.user_id.id
+ new_user_id = forced_user_id or sheet.employee_id.user_id and sheet.employee_id.user_id.id
if new_user_id:
cr.execute('SELECT id \
FROM hr_timesheet_sheet_sheet \
@@ -591,16 +591,24 @@ class hr_timesheet_sheet_sheet_day(osv.osv):
) union (
select
-min(a.id) as id,
- a.name::date as name,
+ (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))::date as name,
s.id as sheet_id,
0.0 as total_timesheet,
- SUM(((EXTRACT(hour FROM a.name) * 60) + EXTRACT(minute FROM a.name)) * (CASE WHEN a.action = 'sign_in' THEN -1 ELSE 1 END)) as total_attendance
+ SUM(((EXTRACT(hour FROM (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))) * 60) + EXTRACT(minute FROM (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC')))) * (CASE WHEN a.action = 'sign_in' THEN -1 ELSE 1 END)) as total_attendance
from
hr_attendance a
LEFT JOIN hr_timesheet_sheet_sheet s
ON s.id = a.sheet_id
+ JOIN hr_employee e
+ ON a.employee_id = e.id
+ JOIN resource_resource r
+ ON e.resource_id = r.id
+ LEFT JOIN res_users u
+ ON r.user_id = u.id
+ LEFT JOIN res_partner p
+ ON u.partner_id = p.id
WHERE action in ('sign_in', 'sign_out')
- group by a.name::date, s.id
+ group by (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))::date, s.id
)) AS foo
GROUP BY name, sheet_id
)) AS bar""")
diff --git a/addons/hw_posbox_homepage/controllers/main.py b/addons/hw_posbox_homepage/controllers/main.py
index 6cf639348bf..25e33c409fa 100644
--- a/addons/hw_posbox_homepage/controllers/main.py
+++ b/addons/hw_posbox_homepage/controllers/main.py
@@ -31,8 +31,8 @@ index_template = """
<p>
The PosBox is an hardware adapter that allows you to use
receipt printers and barcode scanners with Odoo's Point of
- Sale, <b>version 8.0 or later</b>. You can start an <a href='https://www.openerp.com/start'>online free trial</a>,
- or <a href='https://www.openerp.com/start?download'>download and install</a> it yourself.
+ Sale, <b>version 8.0 or later</b>. You can start an <a href='https://www.odoo.com/start'>online free trial</a>,
+ or <a href='https://www.odoo.com/start?download'>download and install</a> it yourself.
</p>
<p>
For more information on how to setup the Point of Sale with
diff --git a/addons/hw_proxy/controllers/main.py b/addons/hw_proxy/controllers/main.py
index 56e94d88a6d..64f6cf0765c 100644
--- a/addons/hw_proxy/controllers/main.py
+++ b/addons/hw_proxy/controllers/main.py
@@ -98,7 +98,7 @@ class Proxy(http.Controller):
<p>
Copy and paste your printer's device description in the form below. You can find
your printer's description in the device list above. If you find that your printer works
- well, please send your printer's description to <a href='mailto:support@openerp.com'>
+ well, please send your printer's description to <a href='mailto:support@odoo.com'>
support@openerp.com</a> so that we can add it to the default list of supported devices.
</p>
<form action='/hw_proxy/escpos/add_supported_device' method='GET'>
diff --git a/addons/knowledge/__openerp__.py b/addons/knowledge/__openerp__.py
index df403fdd15b..10c251ee198 100644
--- a/addons/knowledge/__openerp__.py
+++ b/addons/knowledge/__openerp__.py
@@ -43,5 +43,4 @@ document and Wiki based Hidden.
'demo': ['knowledge_demo.xml'],
'installable': True,
'auto_install': False,
- 'images': ['images/1_config_knowledge.jpeg'],
}
diff --git a/addons/l10n_ar/__openerp__.py b/addons/l10n_ar/__openerp__.py
index 2ba89d58431..9b692a89a08 100644
--- a/addons/l10n_ar/__openerp__.py
+++ b/addons/l10n_ar/__openerp__.py
@@ -41,5 +41,4 @@ Plan contable argentino e impuestos de acuerdo a disposiciones vigentes
'demo': [],
'active': False,
'installable': True,
- 'images': ['images/config_chart_l10n_ar.jpeg','images/l10n_ar_chart.jpeg'],
}
diff --git a/addons/l10n_be/__openerp__.py b/addons/l10n_be/__openerp__.py
index b38001e5fe3..01750190fe6 100644
--- a/addons/l10n_be/__openerp__.py
+++ b/addons/l10n_be/__openerp__.py
@@ -83,5 +83,4 @@ Wizards provided by this module:
'demo': [],
'installable': True,
'website': 'https://www.odoo.com/page/accounting',
- 'images': ['images/1_config_chart_l10n_be.jpeg','images/2_l10n_be_chart.jpeg'],
}
diff --git a/addons/l10n_be_coda/__openerp__.py b/addons/l10n_be_coda/__openerp__.py
index 192f2dc45f9..334cadae180 100644
--- a/addons/l10n_be_coda/__openerp__.py
+++ b/addons/l10n_be_coda/__openerp__.py
@@ -71,7 +71,6 @@ description provided by the CODA configuration tables is based upon the CODA
V2.2 specifications.
If required, you can manually adjust the descriptions via the CODA configuration menu.
''',
- 'images': ['images/coda_logs.jpeg', 'images/import_coda_logs.jpeg'],
'depends': ['account_voucher', 'base_iban', 'l10n_be_invoice_bba', 'account_bank_statement_import'],
'demo': ['l10n_be_coda_demo.xml'],
'data': [
diff --git a/addons/l10n_be_coda/wizard/account_coda_import.py b/addons/l10n_be_coda/wizard/account_coda_import.py
index bca1f989853..476f4bf8a0e 100644
--- a/addons/l10n_be_coda/wizard/account_coda_import.py
+++ b/addons/l10n_be_coda/wizard/account_coda_import.py
@@ -57,6 +57,7 @@ class account_bank_statement_import(osv.TransientModel):
statement['version'] = line[127]
if statement['version'] not in ['1', '2']:
raise UserError(_('Error') + ' R001: ' + _('CODA V%s statements are not supported, please contact your bank') % statement['version'])
+ statement['globalisation_stack'] = []
statement['lines'] = []
statement['date'] = time.strftime(tools.DEFAULT_SERVER_DATE_FORMAT, time.strptime(rmspaces(line[5:11]), '%d%m%y'))
statement['separateApplication'] = rmspaces(line[83:88])
@@ -116,7 +117,11 @@ class account_bank_statement_import(osv.TransientModel):
statementLine['type'] = 'normal'
statementLine['globalisation'] = int(line[124])
if statementLine['globalisation'] > 0:
- statementLine['type'] = 'globalisation'
+ if statementLine['globalisation'] in statement['globalisation_stack']:
+ statement['globalisation_stack'].remove(statementLine['globalisation'])
+ else:
+ statementLine['type'] = 'globalisation'
+ statement['globalisation_stack'].append(statementLine['globalisation'])
globalisation_comm[statementLine['ref_move']] = statementLine['communication']
if not statementLine.get('communication'):
statementLine['communication'] = globalisation_comm.get(statementLine['ref_move'], '')
diff --git a/addons/l10n_bo/__openerp__.py b/addons/l10n_bo/__openerp__.py
index 2928017bcdb..71487288ba2 100644
--- a/addons/l10n_bo/__openerp__.py
+++ b/addons/l10n_bo/__openerp__.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-##############################################################################
+##############################################################################
{
"name": "Bolivia Localization Chart Account",
"version": "1.0",
@@ -46,6 +46,5 @@ Plan contable boliviano e impuestos de acuerdo a disposiciones vigentes
"active": False,
"installable": True,
"certificate" : "",
- 'images': ['images/config_chart_l10n_bo.jpeg','images/l10n_bo_chart.jpeg'],
}
diff --git a/addons/l10n_ca/__openerp__.py b/addons/l10n_ca/__openerp__.py
index fa5c3a79667..3f8667056d5 100644
--- a/addons/l10n_ca/__openerp__.py
+++ b/addons/l10n_ca/__openerp__.py
@@ -75,5 +75,4 @@ position.
],
'demo': [],
'installable': True,
- 'images': ['images/config_chart_l10n_ca.jpeg','images/l10n_ca_chart.jpeg'],
}
diff --git a/addons/l10n_ch/__openerp__.py b/addons/l10n_ch/__openerp__.py
index 38500842145..31ed150c9aa 100644
--- a/addons/l10n_ch/__openerp__.py
+++ b/addons/l10n_ch/__openerp__.py
@@ -70,5 +70,4 @@ https://github.com/OCA/l10n-switzerland
'test': [],
'auto_install': False,
'installable': True,
- 'images': ['images/config_chart_l10n_ch.jpeg', 'images/l10n_ch_chart.jpeg']
}
diff --git a/addons/l10n_cl/__openerp__.py b/addons/l10n_cl/__openerp__.py
index b4bf884182d..1e16e486508 100644
--- a/addons/l10n_cl/__openerp__.py
+++ b/addons/l10n_cl/__openerp__.py
@@ -40,5 +40,4 @@ Plan contable chileno e impuestos de acuerdo a disposiciones vigentes
'demo': [],
'active': False,
'installable': True,
- 'images': ['images/config_chart_l10n_cl.jpeg','images/l10n_cl_chart.jpeg'],
}
diff --git a/addons/l10n_cn/__openerp__.py b/addons/l10n_cn/__openerp__.py
index ca2b8f9fcad..45507b64c75 100644
--- a/addons/l10n_cn/__openerp__.py
+++ b/addons/l10n_cn/__openerp__.py
@@ -49,5 +49,4 @@
'license': 'GPL-3',
'auto_install': False,
'installable': True,
- 'images': ['images/config_chart_l10n_cn.jpeg','images/l10n_cn_chart.jpeg'],
}
diff --git a/addons/l10n_co/__openerp__.py b/addons/l10n_co/__openerp__.py
index fd47a7329ce..8a1f89a2dc0 100644
--- a/addons/l10n_co/__openerp__.py
+++ b/addons/l10n_co/__openerp__.py
@@ -45,5 +45,4 @@
],
'demo': [],
'installable': True,
- 'images': [],
}
diff --git a/addons/l10n_cr/__openerp__.py b/addons/l10n_cr/__openerp__.py
index 63fa88118f5..e2f75c6bc0f 100644
--- a/addons/l10n_cr/__openerp__.py
+++ b/addons/l10n_cr/__openerp__.py
@@ -68,5 +68,4 @@ please go to http://translations.launchpad.net/openerp-costa-rica.
'license': 'Other OSI approved licence',
'installable': True,
'auto_install': False,
- 'images': ['images/config_chart_l10n_cr.jpeg','images/l10n_cr_chart.jpeg'],
}
diff --git a/addons/l10n_de/__openerp__.py b/addons/l10n_de/__openerp__.py
index 2ee625bd866..503b1f141ce 100644
--- a/addons/l10n_de/__openerp__.py
+++ b/addons/l10n_de/__openerp__.py
@@ -148,5 +148,4 @@ German accounting chart and localization.
'l10n_de_wizard.xml',
],
'installable': True,
- 'images': ['images/config_chart_l10n_de.jpeg','images/l10n_de_chart.jpeg'],
}
diff --git a/addons/l10n_ec/__openerp__.py b/addons/l10n_ec/__openerp__.py
index ee97c6ca771..376f373e66a 100644
--- a/addons/l10n_ec/__openerp__.py
+++ b/addons/l10n_ec/__openerp__.py
@@ -42,5 +42,4 @@ Accounting chart and localization for Ecuador.
],
'demo': [],
'installable': True,
- 'images': ['images/config_chart_l10n_ec.jpeg','images/l10n_ec_chart.jpeg'],
}
diff --git a/addons/l10n_fr/__openerp__.py b/addons/l10n_fr/__openerp__.py
index f094fbc10a8..5d19af3f237 100644
--- a/addons/l10n_fr/__openerp__.py
+++ b/addons/l10n_fr/__openerp__.py
@@ -73,5 +73,4 @@ configuration of their taxes and fiscal positions manually.
'demo': [],
'auto_install': False,
'installable': True,
- 'images': ['images/config_chart_l10n_fr.jpeg','images/l10n_fr_chart.jpeg'],
}
diff --git a/addons/l10n_gr/__openerp__.py b/addons/l10n_gr/__openerp__.py
index 8ea37a8688f..9224dddc835 100644
--- a/addons/l10n_gr/__openerp__.py
+++ b/addons/l10n_gr/__openerp__.py
@@ -41,5 +41,4 @@ Greek accounting chart and localization.
'l10n_gr_wizard.xml'
],
'installable': True,
- 'images': ['images/config_chart_l10n_gr.jpeg','images/l10n_gr_chart.jpeg'],
}
diff --git a/addons/l10n_gt/__openerp__.py b/addons/l10n_gt/__openerp__.py
index 7c13170b576..54356e70cd8 100644
--- a/addons/l10n_gt/__openerp__.py
+++ b/addons/l10n_gt/__openerp__.py
@@ -54,5 +54,4 @@ taxes and the Quetzal currency.""",
],
'demo': [],
'installable': True,
- 'images': ['images/config_chart_l10n_gt.jpeg','images/l10n_gt_chart.jpeg'],
}
diff --git a/addons/l10n_in/__openerp__.py b/addons/l10n_in/__openerp__.py
index a45a6d9cf02..a105f97b931 100644
--- a/addons/l10n_in/__openerp__.py
+++ b/addons/l10n_in/__openerp__.py
@@ -51,5 +51,4 @@ Sheet, now only Vertical format has been permitted Which is Supported By OpenERP
],
'auto_install': False,
'installable': True,
- 'images': ['images/config_chart_l10n_in.jpeg','images/l10n_in_chart.jpeg'],
}
diff --git a/addons/l10n_it/__openerp__.py b/addons/l10n_it/__openerp__.py
index 686fb5ff0d4..807f78cfd5f 100644
--- a/addons/l10n_it/__openerp__.py
+++ b/addons/l10n_it/__openerp__.py
@@ -52,5 +52,4 @@ Italian accounting chart and localization.
'demo': [],
'installable': True,
'auto_install': False,
- 'images': ['images/config_chart_l10n_it.jpeg','images/l10n_it_chart.jpeg'],
}
diff --git a/addons/l10n_lu/__openerp__.py b/addons/l10n_lu/__openerp__.py
index a147cfe02e1..8f928f3a589 100644
--- a/addons/l10n_lu/__openerp__.py
+++ b/addons/l10n_lu/__openerp__.py
@@ -56,5 +56,4 @@ This is the base module to manage the accounting chart for Luxembourg.
'demo': [],
'installable': True,
'auto_install': False,
- 'images': ['images/config_chart_l10n_lu.jpeg','images/l10n_lu_chart.jpeg'],
}
diff --git a/addons/l10n_ma/__openerp__.py b/addons/l10n_ma/__openerp__.py
index 422300f5fd4..d3824a249a3 100644
--- a/addons/l10n_ma/__openerp__.py
+++ b/addons/l10n_ma/__openerp__.py
@@ -46,5 +46,4 @@ Seddik au cours du troisième trimestre 2010.""",
'demo' : [],
'auto_install': False,
'installable': True,
- 'images': ['images/config_chart_l10n_ma.jpeg','images/l10n_ma_chart.jpeg'],
}
diff --git a/addons/l10n_nl/__openerp__.py b/addons/l10n_nl/__openerp__.py
index 68012a2a8a1..83b3ee1ed96 100644
--- a/addons/l10n_nl/__openerp__.py
+++ b/addons/l10n_nl/__openerp__.py
@@ -130,5 +130,4 @@ De extra cijfers worden dan achter het rekeningnummer aangevult met 'nullen'.
],
'demo' : [],
'installable': True,
- 'images': ['images/config_chart_l10n_nl.jpeg','images/l10n_nl_chart.jpeg'],
}
diff --git a/addons/l10n_pa/__openerp__.py b/addons/l10n_pa/__openerp__.py
index be04eec3cd2..18f0f545792 100644
--- a/addons/l10n_pa/__openerp__.py
+++ b/addons/l10n_pa/__openerp__.py
@@ -47,5 +47,4 @@ Con la Colaboración de
"active": False,
"installable": True,
"certificate" : "",
- 'images': ['images/config_chart_l10n_cl.jpeg','images/l10n_cl_chart.jpeg'],
}
diff --git a/addons/l10n_pe/__openerp__.py b/addons/l10n_pe/__openerp__.py
index 5b3b76fe684..ebb96778eb7 100644
--- a/addons/l10n_pe/__openerp__.py
+++ b/addons/l10n_pe/__openerp__.py
@@ -42,5 +42,4 @@ SUNAT 2011 (PCGE 2010).
'demo': [],
'active': False,
'installable': True,
- 'images': ['images/config_chart_l10n_pe.jpeg','images/l10n_pe_chart.jpeg'],
}
diff --git a/addons/l10n_th/__openerp__.py b/addons/l10n_th/__openerp__.py
index cf25e7cbccc..1b4e39fe7e8 100644
--- a/addons/l10n_th/__openerp__.py
+++ b/addons/l10n_th/__openerp__.py
@@ -34,5 +34,4 @@ Thai accounting chart and localization.
'depends': ['account_chart'],
'data': [ 'account_data.xml' ],
'installable': True,
- 'images': ['images/config_chart_l10n_th.jpeg','images/l10n_th_chart.jpeg'],
}
diff --git a/addons/l10n_tr/__openerp__.py b/addons/l10n_tr/__openerp__.py
index 302076a7344..b4b43c7d403 100644
--- a/addons/l10n_tr/__openerp__.py
+++ b/addons/l10n_tr/__openerp__.py
@@ -48,5 +48,4 @@ Bu modül kurulduktan sonra, Muhasebe yapılandırma sihirbazı çalışır
],
'demo': [],
'installable': True,
- 'images': ['images/chart_l10n_tr_1.jpg','images/chart_l10n_tr_2.jpg','images/chart_l10n_tr_3.jpg'],
}
diff --git a/addons/l10n_uk/__openerp__.py b/addons/l10n_uk/__openerp__.py
index c50deea2e96..1d08b62846f 100644
--- a/addons/l10n_uk/__openerp__.py
+++ b/addons/l10n_uk/__openerp__.py
@@ -44,5 +44,4 @@ This is the latest UK OpenERP localisation necessary to run OpenERP accounting f
],
'demo' : ['demo/demo.xml'],
'installable': 'True',
- 'images': ['images/config_chart_l10n_uk.jpeg','images/l10n_uk_chart.jpeg'],
}
diff --git a/addons/l10n_uy/__openerp__.py b/addons/l10n_uy/__openerp__.py
index 5e967529225..ff4ac166877 100644
--- a/addons/l10n_uy/__openerp__.py
+++ b/addons/l10n_uy/__openerp__.py
@@ -46,5 +46,4 @@ Provide Templates for Chart of Accounts, Taxes for Uruguay.
'demo': [],
'auto_install': False,
'installable': True,
- 'images': ['images/config_chart_l10n_uy.jpeg','images/l10n_uy_chart.jpeg'],
}
diff --git a/addons/l10n_ve/__openerp__.py b/addons/l10n_ve/__openerp__.py
index 2b865fdf1f3..06da8b8bc33 100644
--- a/addons/l10n_ve/__openerp__.py
+++ b/addons/l10n_ve/__openerp__.py
@@ -71,6 +71,4 @@ but you will need set manually account defaults for taxes.
],
'auto_install': False,
'installable': True,
- 'images': ['images/config_chart_l10n_ve.jpeg',
- 'images/l10n_ve_chart.jpeg'],
}
diff --git a/addons/lunch/__openerp__.py b/addons/lunch/__openerp__.py
index 951f4c3a4bf..aec7b17ff71 100644
--- a/addons/lunch/__openerp__.py
+++ b/addons/lunch/__openerp__.py
@@ -52,11 +52,9 @@ If you want to save your employees' time and avoid them to always have coins in
'views/report_lunchorder.xml',
'views/lunch.xml',
],
- 'images': ['images/new_order.jpeg','images/lunch_account.jpeg','images/order_by_supplier_analysis.jpeg','images/alert.jpeg'],
'demo': ['lunch_demo.xml',],
'installable': True,
'website': 'https://www.odoo.com/page/employees',
'application' : True,
'certificate' : '001292377792581874189',
- 'images': [],
}
diff --git a/addons/lunch/tests/__init__.py b/addons/lunch/tests/__init__.py
index 34446b6d88a..fd218c34e65 100644
--- a/addons/lunch/tests/__init__.py
+++ b/addons/lunch/tests/__init__.py
@@ -20,6 +20,3 @@
##############################################################################
from . import test_lunch
-checks = [
- test_lunch,
-]
diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py
index 642aeb1bca8..a7a96617119 100644
--- a/addons/mail/__openerp__.py
+++ b/addons/mail/__openerp__.py
@@ -75,14 +75,6 @@ Main Features
],
'installable': True,
'application': True,
- 'images': [
- 'images/inbox.jpeg',
- 'images/messages_form.jpeg',
- 'images/messages_list.jpeg',
- 'images/email.jpeg',
- 'images/join_a_group.jpeg',
- 'images/share_a_message.jpeg',
- ],
'qweb': [
'static/src/xml/mail.xml',
'static/src/xml/mail_followers.xml',
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index 254cd193285..4e3e77f1dd8 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -1443,18 +1443,34 @@ class mail_thread(osv.AbstractModel):
if follower.email == email_address:
partner_id = follower.id
# second try: check in partners that are also users
+ # Escape special SQL characters in email_address to avoid invalid matches
+ email_address = (email_address.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_'))
+ email_brackets = "<%s>" % email_address
if not partner_id:
- ids = partner_obj.search(cr, SUPERUSER_ID, [
- ('email', 'ilike', email_address),
- ('user_ids', '!=', False)
- ], limit=1, context=context)
+ # exact, case-insensitive match
+ ids = partner_obj.search(cr, SUPERUSER_ID,
+ [('email', '=ilike', email_address),
+ ('user_ids', '!=', False)],
+ limit=1, context=context)
+ if not ids:
+ # if no match with addr-spec, attempt substring match within name-addr pair
+ ids = partner_obj.search(cr, SUPERUSER_ID,
+ [('email', 'ilike', email_brackets),
+ ('user_ids', '!=', False)],
+ limit=1, context=context)
if ids:
partner_id = ids[0]
# third try: check in partners
if not partner_id:
- ids = partner_obj.search(cr, SUPERUSER_ID, [
- ('email', 'ilike', email_address)
- ], limit=1, context=context)
+ # exact, case-insensitive match
+ ids = partner_obj.search(cr, SUPERUSER_ID,
+ [('email', '=ilike', email_address)],
+ limit=1, context=context)
+ if not ids:
+ # if no match with addr-spec, attempt substring match within name-addr pair
+ ids = partner_obj.search(cr, SUPERUSER_ID,
+ [('email', 'ilike', email_brackets)],
+ limit=1, context=context)
if ids:
partner_id = ids[0]
partner_ids.append(partner_id)
@@ -1474,13 +1490,15 @@ class mail_thread(osv.AbstractModel):
partner_id = partner_ids[idx]
partner_info = {'full_name': email_address, 'partner_id': partner_id}
result.append(partner_info)
-
# link mail with this from mail to the new partner id
if link_mail and partner_info['partner_id']:
+ # Escape special SQL characters in email_address to avoid invalid matches
+ email_address = (email_address.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_'))
+ email_brackets = "<%s>" % email_address
message_ids = mail_message_obj.search(cr, SUPERUSER_ID, [
'|',
- ('email_from', '=', email_address),
- ('email_from', 'ilike', '<%s>' % email_address),
+ ('email_from', '=ilike', email_address),
+ ('email_from', 'ilike', email_brackets),
('author_id', '=', False)
], context=context)
if message_ids:
diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js
index 59c1eaecd89..d54ad6176b1 100644
--- a/addons/mail/static/src/js/mail.js
+++ b/addons/mail/static/src/js/mail.js
@@ -369,6 +369,16 @@ openerp.mail = function (session) {
this.is_log = false;
this.recipients = [];
this.recipient_ids = [];
+ session.web.bus.on('clear_uncommitted_changes', this, function(e) {
+ if (this.show_composer && !e.isDefaultPrevented()){
+ if (!confirm(_t("You are currently composing a message, your message will be discarded.\n\nAre you sure you want to leave this page ?"))) {
+ e.preventDefault();
+ }
+ else{
+ this.on_cancel();
+ }
+ }
+ });
},
start: function () {
diff --git a/addons/mail/tests/__init__.py b/addons/mail/tests/__init__.py
index e5592468cb4..23162ee1c2f 100644
--- a/addons/mail/tests/__init__.py
+++ b/addons/mail/tests/__init__.py
@@ -20,14 +20,3 @@
##############################################################################
from . import test_mail_group, test_mail_message, test_mail_features, test_mail_gateway, test_message_read, test_invite, test_mail, test_ir_actions
-
-checks = [
- test_mail_group,
- test_mail_message,
- test_mail_features,
- test_mail_gateway,
- test_message_read,
- test_invite,
- test_mail,
- test_ir_actions,
-]
diff --git a/addons/mail/tests/test_mail_gateway.py b/addons/mail/tests/test_mail_gateway.py
index 5de41cc5d7a..cc732b007a2 100644
--- a/addons/mail/tests/test_mail_gateway.py
+++ b/addons/mail/tests/test_mail_gateway.py
@@ -68,7 +68,7 @@ MAIL_TEMPLATE_PLAINTEXT = """Return-Path: <whatever-2a840@postmaster.twitter.com
To: {to}
Received: by mail1.openerp.com (Postfix, from userid 10002)
id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
-From: Sylvie Lelitre <sylvie.lelitre@agrolait.com>
+From: Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>
Subject: {subject}
MIME-Version: 1.0
Content-Type: text/plain
@@ -525,6 +525,16 @@ class TestMailgateway(TestMail):
self.assertEqual(frog_group.message_ids[0].author_id.id, extra_partner_id,
'message_process: email_from -> author_id wrong')
+ # Do: post a new message with a non-existant email that is a substring of a partner email
+ format_and_process(MAIL_TEMPLATE, email_from='Not really Lombrik Lubrik <oul@email.com>',
+ subject='Re: news (2)',
+ msg_id='<zzzbbbaaaa@agrolait.com>',
+ extra='In-Reply-To: <1198923581.41972151344608186760.JavaMail@agrolait.com>\n')
+ frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'Frogs')])
+ frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
+ # Test: author must not be set, otherwise the system is confusing different users
+ self.assertFalse(frog_group.message_ids[0].author_id, 'message_process: email_from -> mismatching author_id')
+
# Do: post a new message, with a known partner -> duplicate emails -> user
frog_group.message_unsubscribe([extra_partner_id])
self.res_users.write(cr, uid, self.user_raoul_id, {'email': 'test_raoul@email.com'})
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index 49eaa599888..485869dd6fe 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -286,7 +286,7 @@ class mail_compose_message(osv.TransientModel):
batch_mail_mail_ids.append(mail_mail_id)
else:
subtype = 'mail.mt_comment'
- if context.get('mail_compose_log') or (wizard.composition_mode == 'mass_post' and not wizard.notify): # log a note: subtype is False
+ if wizard.is_log or (wizard.composition_mode == 'mass_post' and not wizard.notify): # log a note: subtype is False
subtype = False
if wizard.composition_mode == 'mass_post':
context = dict(context,
diff --git a/addons/marketing/__openerp__.py b/addons/marketing/__openerp__.py
index fda4d8d950b..556ca74d2ad 100644
--- a/addons/marketing/__openerp__.py
+++ b/addons/marketing/__openerp__.py
@@ -21,5 +21,4 @@ Contains the installer for marketing-related modules.
'demo': ['marketing_demo.xml'],
'installable': True,
'auto_install': False,
- 'images': ['images/config_marketing.jpeg'],
}
diff --git a/addons/marketing_campaign/__openerp__.py b/addons/marketing_campaign/__openerp__.py
index f2512a74ff2..c0eac4eff41 100644
--- a/addons/marketing_campaign/__openerp__.py
+++ b/addons/marketing_campaign/__openerp__.py
@@ -67,5 +67,4 @@ input segments, workflow.
'test': ['test/marketing_campaign.yml'],
'installable': True,
'auto_install': False,
- 'images': ['images/campaign.png', 'images/campaigns.jpeg','images/email_account.jpeg','images/email_templates.jpeg','images/segments.jpeg'],
}
diff --git a/addons/marketing_campaign_crm_demo/__openerp__.py b/addons/marketing_campaign_crm_demo/__openerp__.py
index 21953d88424..703b7b58536 100644
--- a/addons/marketing_campaign_crm_demo/__openerp__.py
+++ b/addons/marketing_campaign_crm_demo/__openerp__.py
@@ -39,5 +39,4 @@ Creates demo data like leads, campaigns and segments for the module marketing_ca
'demo': ['marketing_campaign_demo.xml'],
'installable': True,
'auto_install': False,
- 'images': ['images/campaigns.jpeg','images/email_templates.jpeg'],
}
diff --git a/addons/mass_mailing/tests/__init__.py b/addons/mass_mailing/tests/__init__.py
index f8610905393..0bc1d8747a8 100644
--- a/addons/mass_mailing/tests/__init__.py
+++ b/addons/mass_mailing/tests/__init__.py
@@ -19,8 +19,4 @@
#
##############################################################################
-from openerp.addons.mass_mailing.tests import test_mail
-
-checks = [
- test_mail,
-]
+from . import test_mail
diff --git a/addons/membership/__openerp__.py b/addons/membership/__openerp__.py
index ed4ecee78f5..f259bac2aac 100644
--- a/addons/membership/__openerp__.py
+++ b/addons/membership/__openerp__.py
@@ -54,5 +54,4 @@ invoice and send propositions for membership renewal.
'test': ['test/test_membership.yml'],
'installable': True,
'auto_install': False,
- 'images': ['images/members.jpeg','images/membership_list.jpeg', 'images/membership_products.jpeg'],
}
diff --git a/addons/mrp/__openerp__.py b/addons/mrp/__openerp__.py
index 7a1b7c9e706..32e6f22e25b 100644
--- a/addons/mrp/__openerp__.py
+++ b/addons/mrp/__openerp__.py
@@ -28,7 +28,6 @@
'category': 'Manufacturing',
'sequence': 18,
'summary': 'Manufacturing Orders, Bill of Materials, Routing',
- 'images': ['images/bill_of_materials.jpeg', 'images/manufacturing_order.jpeg', 'images/planning_manufacturing_order.jpeg', 'images/manufacturing_analysis.jpeg', 'images/routings.jpeg','images/work_centers.jpeg'],
'depends': ['product', 'procurement', 'stock_account', 'resource', 'report'],
'description': """
Manage the Manufacturing process in OpenERP
diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py
index 74fdbaea709..dd089e728f7 100644
--- a/addons/mrp/mrp.py
+++ b/addons/mrp/mrp.py
@@ -22,7 +22,7 @@
import time
import openerp.addons.decimal_precision as dp
from openerp.osv import fields, osv
-from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
from openerp.tools.translate import _
from openerp import tools, SUPERUSER_ID
from openerp.addons.product import _common
@@ -217,8 +217,8 @@ class mrp_bom(osv.osv):
else:
# neither product nor template, makes no sense to search
return False
- domain = domain + [ '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
- '|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
+ domain = domain + [ '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATE_FORMAT)),
+ '|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT))]
# order to prioritize bom with product_id over the one without
ids = self.search(cr, uid, domain, order='product_id', context=context)
# Search a BoM which has all properties specified, or if you can not find one, you could
@@ -277,8 +277,8 @@ class mrp_bom(osv.osv):
})
for bom_line_id in bom.bom_line_ids:
- if bom_line_id.date_start and bom_line_id.date_start > time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) or \
- bom_line_id.date_stop and bom_line_id.date_stop < time.strftime(DEFAULT_SERVER_DATETIME_FORMAT):
+ if bom_line_id.date_start and bom_line_id.date_start > time.strftime(DEFAULT_SERVER_DATE_FORMAT) or \
+ bom_line_id.date_stop and bom_line_id.date_stop < time.strftime(DEFAULT_SERVER_DATE_FORMAT):
continue
# all bom_line_id variant values must be in the product
if bom_line_id.attribute_value_ids:
@@ -678,6 +678,28 @@ class mrp_production(osv.osv):
return {'value': result}
+ def _prepare_lines(self, cr, uid, production, properties=None, context=None):
+ # search BoM structure and route
+ bom_obj = self.pool.get('mrp.bom')
+ uom_obj = self.pool.get('product.uom')
+ bom_point = production.bom_id
+ bom_id = production.bom_id.id
+ if not bom_point:
+ bom_id = bom_obj._bom_find(cr, uid, product_id=production.product_id.id, properties=properties, context=context)
+ if bom_id:
+ bom_point = bom_obj.browse(cr, uid, bom_id)
+ routing_id = bom_point.routing_id.id or False
+ self.write(cr, uid, [production.id], {'bom_id': bom_id, 'routing_id': routing_id})
+
+ if not bom_id:
+ raise UserError(_("Cannot find a bill of material for this product."))
+
+ # get components and workcenter_lines from BoM structure
+ factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, bom_point.product_uom.id)
+ # product_lines, workcenter_lines
+ return bom_obj._bom_explode(cr, uid, bom_point, production.product_id, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id, context=context)
+
+
def _action_compute_lines(self, cr, uid, ids, properties=None, context=None):
""" Compute product_lines and workcenter_lines from BoM structure
@return: product_lines
@@ -685,8 +707,6 @@ class mrp_production(osv.osv):
if properties is None:
properties = []
results = []
- bom_obj = self.pool.get('mrp.bom')
- uom_obj = self.pool.get('product.uom')
prod_line_obj = self.pool.get('mrp.production.product.line')
workcenter_line_obj = self.pool.get('mrp.production.workcenter.line')
for production in self.browse(cr, uid, ids, context=context):
@@ -694,23 +714,10 @@ class mrp_production(osv.osv):
prod_line_obj.unlink(cr, SUPERUSER_ID, [line.id for line in production.product_lines], context=context)
#unlink workcenter_lines
workcenter_line_obj.unlink(cr, SUPERUSER_ID, [line.id for line in production.workcenter_lines], context=context)
- # search BoM structure and route
- bom_point = production.bom_id
- bom_id = production.bom_id.id
- if not bom_point:
- bom_id = bom_obj._bom_find(cr, uid, product_id=production.product_id.id, properties=properties, context=context)
- if bom_id:
- bom_point = bom_obj.browse(cr, uid, bom_id)
- routing_id = bom_point.routing_id.id or False
- self.write(cr, uid, [production.id], {'bom_id': bom_id, 'routing_id': routing_id})
-
- if not bom_id:
- raise UserError(_("Cannot find a bill of material for this product."))
- # get components and workcenter_lines from BoM structure
- factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, bom_point.product_uom.id)
- # product_lines, workcenter_lines
- results, results2 = bom_obj._bom_explode(cr, uid, bom_point, production.product_id, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id, context=context)
+ res = self._prepare_lines(cr, uid, production, properties=properties, context=context)
+ results = res[0] # product_lines
+ results2 = res[1] # workcenter_lines
# reset product_lines in production order
for line in results:
diff --git a/addons/mrp_byproduct/__openerp__.py b/addons/mrp_byproduct/__openerp__.py
index 4cb598265c1..8bf6ab02c4c 100644
--- a/addons/mrp_byproduct/__openerp__.py
+++ b/addons/mrp_byproduct/__openerp__.py
@@ -40,7 +40,6 @@ With this module:
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/manufacturing',
- 'images': ['images/bom_byproduct.jpeg'],
'depends': ['base', 'mrp'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/mrp_operations/__openerp__.py b/addons/mrp_operations/__openerp__.py
index 96cfc97c20e..1154df9bf40 100644
--- a/addons/mrp_operations/__openerp__.py
+++ b/addons/mrp_operations/__openerp__.py
@@ -54,7 +54,6 @@ So, that we can compare the theoretic delay and real delay.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/manufacturing',
- 'images': ['images/work_order_analysis.jpeg','images/work_order_planning.jpeg','images/work_order.jpeg'],
'depends': ['mrp'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/mrp_repair/__openerp__.py b/addons/mrp_repair/__openerp__.py
index e64e76fbd9a..f7506d7bce3 100644
--- a/addons/mrp_repair/__openerp__.py
+++ b/addons/mrp_repair/__openerp__.py
@@ -38,7 +38,6 @@ The following topics should be covered by this module:
* Notes for the technician and for the final customer
""",
'author': 'OpenERP SA',
- 'images': ['images/repair_order.jpeg'],
'depends': ['mrp', 'sale', 'account'],
'website': 'https://www.odoo.com/page/manufacturing',
'data': [
diff --git a/addons/multi_company/__openerp__.py b/addons/multi_company/__openerp__.py
index b6ab2dea550..803b311dad9 100644
--- a/addons/multi_company/__openerp__.py
+++ b/addons/multi_company/__openerp__.py
@@ -41,5 +41,4 @@ This module is the base module for other multi-company modules.
'demo': ['multi_company_demo.xml'],
'installable': True,
'auto_install': False,
- 'images': ['images/companies.jpeg','images/default_company_per_object_form.jpeg', 'images/default_company_per_object_list.jpeg'],
}
diff --git a/addons/note/__openerp__.py b/addons/note/__openerp__.py
index a890a91d6d9..63e293ed751 100644
--- a/addons/note/__openerp__.py
+++ b/addons/note/__openerp__.py
@@ -55,11 +55,6 @@ Notes can be found in the 'Home' menu.
],
'test': [
],
- 'images': [
- 'images/note_kanban.jpeg',
- 'images/note.jpeg',
- 'images/categories_tree.jpeg'
- ],
'installable': True,
'application': True,
'auto_install': False,
diff --git a/addons/note/tests/__init__.py b/addons/note/tests/__init__.py
index cb56655f546..48ddba22f76 100644
--- a/addons/note/tests/__init__.py
+++ b/addons/note/tests/__init__.py
@@ -20,6 +20,3 @@
##############################################################################
from . import test_note
-checks = [
- test_note,
-]
diff --git a/addons/pad/__openerp__.py b/addons/pad/__openerp__.py
index f9ba238e126..b4bbc722f8a 100644
--- a/addons/pad/__openerp__.py
+++ b/addons/pad/__openerp__.py
@@ -22,5 +22,4 @@ pads (by default, http://ietherpad.com/).
'auto_install': False,
'web': True,
'qweb' : ['static/src/xml/*.xml'],
- 'images': ['static/src/img/pad_link_companies.jpeg'],
}
diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py
index 002fa1344f3..7a5f541a8f8 100644
--- a/addons/payment/models/payment_acquirer.py
+++ b/addons/payment/models/payment_acquirer.py
@@ -62,7 +62,8 @@ class PaymentAcquirer(osv.Model):
'name': fields.char('Name', required=True),
'provider': fields.selection(_provider_selection, string='Provider', required=True),
'company_id': fields.many2one('res.company', 'Company', required=True),
- 'pre_msg': fields.html('Message', help='Message displayed to explain and help the payment process.'),
+ 'pre_msg': fields.html('Message', translate=True,
+ help='Message displayed to explain and help the payment process.'),
'post_msg': fields.html('Thanks Message', help='Message displayed after having done the payment process.'),
'validation': fields.selection(
[('manual', 'Manual'), ('automatic', 'Automatic')],
diff --git a/addons/payment_adyen/tests/__init__.py b/addons/payment_adyen/tests/__init__.py
index 8c8e8249184..40a96afc6ff 100644
--- a/addons/payment_adyen/tests/__init__.py
+++ b/addons/payment_adyen/tests/__init__.py
@@ -1,7 +1 @@
# -*- coding: utf-8 -*-
-
-# from . import test_adyen
-
-checks = [
- # test_adyen,
-]
diff --git a/addons/payment_buckaroo/tests/__init__.py b/addons/payment_buckaroo/tests/__init__.py
index d245ab8339d..22dc419fcfe 100644
--- a/addons/payment_buckaroo/tests/__init__.py
+++ b/addons/payment_buckaroo/tests/__init__.py
@@ -1,7 +1,3 @@
# -*- coding: utf-8 -*-
-from openerp.addons.payment_buckaroo.tests import test_buckaroo
-
-checks = [
- test_buckaroo,
-]
+from . import test_buckaroo
diff --git a/addons/payment_ogone/tests/__init__.py b/addons/payment_ogone/tests/__init__.py
index 5b920121911..40a96afc6ff 100644
--- a/addons/payment_ogone/tests/__init__.py
+++ b/addons/payment_ogone/tests/__init__.py
@@ -1,7 +1 @@
# -*- coding: utf-8 -*-
-
-# from . import test_ogone
-
-checks = [
- # test_ogone,
-]
diff --git a/addons/payment_paypal/tests/__init__.py b/addons/payment_paypal/tests/__init__.py
index 4c38b20e76a..40a96afc6ff 100644
--- a/addons/payment_paypal/tests/__init__.py
+++ b/addons/payment_paypal/tests/__init__.py
@@ -1,7 +1 @@
# -*- coding: utf-8 -*-
-
-# from . import test_paypal
-
-checks = [
- # test_paypal,
-]
diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py
index 8e3d9859cc1..68680102328 100644
--- a/addons/point_of_sale/__openerp__.py
+++ b/addons/point_of_sale/__openerp__.py
@@ -47,7 +47,6 @@ Main Features
* Refund previous sales
""",
'author': 'OpenERP SA',
- 'images': ['images/pos_touch_screen.jpeg', 'images/pos_session.jpeg', 'images/pos_analysis.jpeg','images/sale_order_pos.jpeg','images/product_pos.jpeg'],
'depends': ['sale_stock', 'barcodes'],
'data': [
'data/report_paperformat.xml',
diff --git a/addons/point_of_sale/controllers/main.py b/addons/point_of_sale/controllers/main.py
index 0f26ba5317e..6657341c230 100644
--- a/addons/point_of_sale/controllers/main.py
+++ b/addons/point_of_sale/controllers/main.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import logging
+import werkzeug.utils
from openerp import http
from openerp.http import request
@@ -19,6 +20,8 @@ class PosController(http.Controller):
PosSession = request.registry['pos.session']
pos_session_ids = PosSession.search(cr, uid, [('state','=','opened'),('user_id','=',session.uid)], context=context)
+ if not pos_session_ids:
+ return werkzeug.utils.redirect('/web#action=point_of_sale.action_pos_session_opening')
PosSession.login(cr, uid, pos_session_ids, context=context)
return request.render('point_of_sale.index')
diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py
index a4a0807411e..d644850b798 100644
--- a/addons/point_of_sale/point_of_sale.py
+++ b/addons/point_of_sale/point_of_sale.py
@@ -414,7 +414,7 @@ class pos_session(osv.osv):
if not pos_config.journal_id:
jid = jobj.default_get(cr, uid, ['journal_id'], context=context)['journal_id']
if jid:
- jobj.write(cr, uid, [pos_config.id], {'journal_id': jid}, context=context)
+ jobj.write(cr, openerp.SUPERUSER_ID, [pos_config.id], {'journal_id': jid}, context=context)
else:
raise UserError(_("Unable to open the session. You have to assign a sale journal to your point of sale."))
@@ -427,8 +427,8 @@ class pos_session(osv.osv):
if not cashids:
cashids = journal_proxy.search(cr, uid, [('journal_user','=',True)], context=context)
- journal_proxy.write(cr, uid, cashids, {'journal_user': True})
- jobj.write(cr, uid, [pos_config.id], {'journal_ids': [(6,0, cashids)]})
+ journal_proxy.write(cr, openerp.SUPERUSER_ID, cashids, {'journal_user': True})
+ jobj.write(cr, openerp.SUPERUSER_ID, [pos_config.id], {'journal_ids': [(6,0, cashids)]})
pos_config = jobj.browse(cr, uid, config_id, context=context)
@@ -589,6 +589,32 @@ class pos_order(osv.osv):
'journal': ui_paymentline['journal_id'],
}
+ def _process_order(self, cr, uid, order, context=None):
+ order_id = self.create(cr, uid, self._order_fields(cr, uid, order, context=context),context)
+
+ for payments in order['statement_ids']:
+ self.add_payment(cr, uid, order_id, self._payment_fields(cr, uid, payments[2], context=context), context=context)
+
+ session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context)
+ if session.sequence_number <= order['sequence_number']:
+ session.write({'sequence_number': order['sequence_number'] + 1})
+ session.refresh()
+
+ if order['amount_return']:
+ cash_journal = session.cash_journal_id
+ if not cash_journal:
+ cash_journal_ids = filter(lambda st: st.journal_id.type=='cash', session.statement_ids)
+ if not len(cash_journal_ids):
+ raise UserError(_("No cash statement found for this session. Unable to record returned cash."))
+ cash_journal = cash_journal_ids[0].journal_id
+ self.add_payment(cr, uid, order_id, {
+ 'amount': -order['amount_return'],
+ 'payment_date': time.strftime('%Y-%m-%d %H:%M:%S'),
+ 'payment_name': _('return'),
+ 'journal': cash_journal.id,
+ }, context=context)
+ return order_id
+
def create_from_ui(self, cr, uid, orders, context=None):
# Keep only new orders
submitted_references = [o['data']['name'] for o in orders]
@@ -602,29 +628,7 @@ class pos_order(osv.osv):
for tmp_order in orders_to_save:
to_invoice = tmp_order['to_invoice']
order = tmp_order['data']
- order_id = self.create(cr, uid, self._order_fields(cr, uid, order, context=context),context)
-
- for payments in order['statement_ids']:
- self.add_payment(cr, uid, order_id, self._payment_fields(cr, uid, payments[2], context=context), context=context)
-
- session = self.pool.get('pos.session').browse(cr, uid, order['pos_session_id'], context=context)
- if session.sequence_number <= order['sequence_number']:
- session.write({'sequence_number': order['sequence_number'] + 1})
- session.refresh()
-
- if order['amount_return']:
- cash_journal = session.cash_journal_id
- if not cash_journal:
- cash_journal_ids = filter(lambda st: st.journal_id.type=='cash', session.statement_ids)
- if not len(cash_journal_ids):
- raise UserError(_("No cash statement found for this session. Unable to record returned cash."))
- cash_journal = cash_journal_ids[0].journal_id
- self.add_payment(cr, uid, order_id, {
- 'amount': -order['amount_return'],
- 'payment_date': time.strftime('%Y-%m-%d %H:%M:%S'),
- 'payment_name': _('return'),
- 'journal': cash_journal.id,
- }, context=context)
+ order_id = self._process_order(cr, uid, order, context=context)
order_ids.append(order_id)
try:
@@ -1097,7 +1101,7 @@ class pos_order(osv.osv):
})
if data_type == 'product':
- key = ('product', values['partner_id'], (values['product_id'], values['name']), values['debit'] > 0)
+ key = ('product', values['partner_id'], (values['product_id'], values['name']), values['analytic_account_id'], values['debit'] > 0)
elif data_type == 'tax':
key = ('tax', values['partner_id'], values['tax_code_id'], values['debit'] > 0)
elif data_type == 'counter_part':
@@ -1371,15 +1375,14 @@ class pos_category(osv.osv):
]
def name_get(self, cr, uid, ids, context=None):
- if not len(ids):
- return []
- reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
res = []
- for record in reads:
- name = record['name']
- if record['parent_id']:
- name = record['parent_id'][1]+' / '+name
- res.append((record['id'], name))
+ for cat in self.browse(cr, uid, ids, context=context):
+ names = [cat.name]
+ pcat = cat.parent_id
+ while pcat:
+ names.append(pcat.name)
+ pcat = pcat.parent_id
+ res.append((cat.id, ' / '.join(reversed(names))))
return res
def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
@@ -1442,6 +1445,14 @@ class product_template(osv.osv):
'available_in_pos': True,
}
+ def unlink(self, cr, uid, ids, context=None):
+ product_ctx = dict(context or {}, active_test=False)
+ if self.search_count(cr, uid, [('id', 'in', ids), ('available_in_pos', '=', True)], context=product_ctx):
+ if self.pool['pos.session'].search_count(cr, uid, [('state', '!=', 'closed')], context=context):
+ raise osv.except_osv(_('Error!'),
+ _('You cannot delete a product saleable in point of sale while a session is still opened.'))
+ return super(product_template, self).unlink(cr, uid, ids, context=context)
+
class res_partner(osv.osv):
_inherit = 'res.partner'
diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml
index 53e22be6202..7b1a44c2ffb 100644
--- a/addons/point_of_sale/point_of_sale_view.xml
+++ b/addons/point_of_sale/point_of_sale_view.xml
@@ -686,7 +686,7 @@
<field name="currency_id" invisible="1"/>
</group>
<separator string="Available Payment Methods" colspan="4"/>
- <field name="journal_ids" colspan="4" nolabel="1">
+ <field name="journal_ids" colspan="4" nolabel="1" context="{'default_journal_user': 1}">
<tree string="Journals">
<field name="code" />
<field name="name" />
diff --git a/addons/point_of_sale/security/ir.model.access.csv b/addons/point_of_sale/security/ir.model.access.csv
index 4d831c7d9b1..4bf5a5ed3a5 100644
--- a/addons/point_of_sale/security/ir.model.access.csv
+++ b/addons/point_of_sale/security/ir.model.access.csv
@@ -37,7 +37,7 @@ access_product_template_pos_manager,product.template pos manager,product.model_p
access_account_move_line,account.move.line,account.model_account_move_line,group_pos_user,1,1,1,0
access_account_move_line_manager,account.move.line manager,account.model_account_move_line,group_pos_manager,1,1,1,1
access_account_move,account.move,account.model_account_move,group_pos_manager,1,0,0,0
-access_account_journal,account.journal,account.model_account_journal,group_pos_user,1,1,1,0
+access_account_journal,account.journal,account.model_account_journal,group_pos_manager,1,1,1,0
access_account_journal_period_user,account.journal.period user,account.model_account_journal_period,group_pos_user,1,1,1,1
access_account_journal_period_manager,account.journal.period manager,account.model_account_journal_period,group_pos_manager,1,0,0,0
access_account_analytic_line,account.analytic.line,analytic.model_account_analytic_line,group_pos_user,1,1,1,0
@@ -54,7 +54,8 @@ access_res_partner_manager,res.partner manager,base.model_res_partner,group_pos_
access_product_category_manager,product.category manager,product.model_product_category,group_pos_manager,1,1,1,1
access_product_pricelist_manager,product.pricelist manager,product.model_product_pricelist,group_pos_manager,1,0,0,0
access_pos_session_user,pos.session user,model_pos_session,group_pos_user,1,1,1,0
-access_pos_config_user,pos.config user,model_pos_config,group_pos_user,1,1,1,0
+access_pos_config_user,pos.config user,model_pos_config,group_pos_user,1,0,0,0
+access_pos_config_manager,pos.config user,model_pos_config,group_pos_manager,1,1,1,0
access_ir_sequence_manager,ir.sequence manager,base.model_ir_sequence,group_pos_manager,1,1,1,1
access_product_category_pos_manager,pos.category manager,model_pos_category,group_pos_manager,1,1,1,1
access_product_category_pos_user,pos.category user,model_pos_category,group_pos_user,1,0,0,0
diff --git a/addons/portal/tests/__init__.py b/addons/portal/tests/__init__.py
index 928719d5ef6..c2e8b115a09 100644
--- a/addons/portal/tests/__init__.py
+++ b/addons/portal/tests/__init__.py
@@ -20,6 +20,3 @@
##############################################################################
from . import test_portal
-checks = [
- test_portal,
-]
diff --git a/addons/portal_project/tests/__init__.py b/addons/portal_project/tests/__init__.py
index 77bc10416a0..4f3637eece0 100644
--- a/addons/portal_project/tests/__init__.py
+++ b/addons/portal_project/tests/__init__.py
@@ -21,6 +21,3 @@
from . import test_access_rights
-checks = [
- test_access_rights,
-]
diff --git a/addons/procurement/__openerp__.py b/addons/procurement/__openerp__.py
index fa3b58486b5..80232385300 100644
--- a/addons/procurement/__openerp__.py
+++ b/addons/procurement/__openerp__.py
@@ -60,5 +60,4 @@ Procurements in exception should be checked manually and can be re-run.
'test': ['test/procurement.yml'],
'installable': True,
'auto_install': True,
- 'images': ['images/compute_schedulers.jpeg','images/config_companies_sched.jpeg', 'images/minimum_stock_rules.jpeg'],
}
diff --git a/addons/product/__openerp__.py b/addons/product/__openerp__.py
index cfff35732be..16124d1eff3 100644
--- a/addons/product/__openerp__.py
+++ b/addons/product/__openerp__.py
@@ -68,5 +68,4 @@ Print product labels with barcode.
],
'installable': True,
'auto_install': False,
- 'images': ['images/product_uom.jpeg','images/product_pricelists.jpeg','images/products_categories.jpeg', 'images/products_form.jpeg'],
}
diff --git a/addons/product/product.py b/addons/product/product.py
index c3e340618c1..14480db2b4b 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -504,7 +504,8 @@ class product_template(osv.osv):
'list_price': fields.float('Sale Price', digits_compute=dp.get_precision('Product Price'), help="Base price to compute the customer price. Sometimes called the catalog price."),
'lst_price' : fields.related('list_price', type="float", string='Public Price', digits_compute=dp.get_precision('Product Price')),
'standard_price': fields.property(type = 'float', digits_compute=dp.get_precision('Product Price'),
- help="Cost price of the product template used for standard stock valuation in accounting and used as a base price on purchase orders.",
+ help="Cost price of the product template used for standard stock valuation in accounting and used as a base price on purchase orders. "
+ "Expressed in the default unit of measure of the product.",
groups="base.group_user", string="Cost Price"),
'volume': fields.float('Volume', help="The volume in m3."),
'weight': fields.float('Gross Weight', digits_compute=dp.get_precision('Stock Weight'), help="The gross weight in Kg."),
@@ -1027,7 +1028,9 @@ class product_product(osv.osv):
sellers = filter(lambda x: x.name.id in partner_ids, product.seller_ids)
if sellers:
for s in sellers:
- seller_variant = s.product_name and "%s (%s)" % (s.product_name, variant) or False
+ seller_variant = s.product_name and (
+ variant and "%s (%s)" % (s.product_name, variant) or s.product_name
+ ) or False
mydict = {
'id': product.id,
'name': seller_variant or name,
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index a62c47a9616..0b01f805f1d 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -260,7 +260,7 @@
<p class="oe_grey">
<strong>Warning</strong>: adding or deleting attributes
will delete and recreate existing variants and lead
- to the loss of its eventual customizations.
+ to the loss of their possible customizations.
</p>
<field name="attribute_line_ids" widget="one2many_list" context="{'show_attribute': False}">
<tree string="Variants" editable="bottom">
diff --git a/addons/product/tests/__init__.py b/addons/product/tests/__init__.py
index 4a1ce43351b..7c05b1bf791 100644
--- a/addons/product/tests/__init__.py
+++ b/addons/product/tests/__init__.py
@@ -1,6 +1 @@
from . import test_uom, test_pricelist
-
-fast_suite = [
- test_uom,
- test_pricelist
-]
diff --git a/addons/product_email_template/data/product_demo.xml b/addons/product_email_template/data/product_demo.xml
index 2ce56226ed7..ccb880d512f 100644
--- a/addons/product_email_template/data/product_demo.xml
+++ b/addons/product_email_template/data/product_demo.xml
@@ -620,7 +620,7 @@
</tr>
<tr>
<td>
- It would be very difficult to cover all of the possibilities of OpenERP and I believe the major aspects were covered very well
+ It would be very difficult to cover all of the possibilities of Odoo and I believe the major aspects were covered very well
</td>
<td>
The trainer has a very good knowledge of the subject, he understands the issues of the trainees very fast and provided answers with the right level of illustration.
diff --git a/addons/product_expiry/__openerp__.py b/addons/product_expiry/__openerp__.py
index 5943abc5563..4f8b8a5b684 100644
--- a/addons/product_expiry/__openerp__.py
+++ b/addons/product_expiry/__openerp__.py
@@ -41,5 +41,4 @@ Also implements the removal strategy First Expiry First Out (FEFO) widely used,
'data' : ['product_expiry_view.xml', 'product_expiry_data.xml'],
'auto_install': False,
'installable': True,
- 'images': ['images/production_lots_dates.jpeg','images/products_dates.jpeg'],
}
diff --git a/addons/product_margin/__openerp__.py b/addons/product_margin/__openerp__.py
index 804b48d7bf7..3850f754bcc 100644
--- a/addons/product_margin/__openerp__.py
+++ b/addons/product_margin/__openerp__.py
@@ -42,5 +42,4 @@ The wizard to launch the report has several options to help you get the data you
'demo': [],
'installable': True,
'auto_install': False,
- 'images': ['images/open_margins.jpeg','images/product_margins_form.jpeg', 'images/product_margins_list.jpeg'],
}
diff --git a/addons/product_visible_discount/__openerp__.py b/addons/product_visible_discount/__openerp__.py
index 33151a609ab..df48ad14202 100644
--- a/addons/product_visible_discount/__openerp__.py
+++ b/addons/product_visible_discount/__openerp__.py
@@ -41,5 +41,4 @@ To this end, a new check box named 'Visible Discount' is added to the pricelist
'data': ['product_visible_discount_view.xml'],
'auto_install': False,
'installable': True,
- 'images': ['images/pricelists_visible_discount.jpeg'],
}
diff --git a/addons/project/__openerp__.py b/addons/project/__openerp__.py
index 67307ca90ea..e20101b371b 100644
--- a/addons/project/__openerp__.py
+++ b/addons/project/__openerp__.py
@@ -27,16 +27,6 @@
'category': 'Project Management',
'sequence': 8,
'summary': 'Projects, Tasks',
- 'images': [
- 'images/gantt.png',
- 'images/project_task_tree.jpeg',
- 'images/project_task.jpeg',
- 'images/project.jpeg',
- 'images/task_analysis.jpeg',
- 'images/project_kanban.jpeg',
- 'images/task_kanban.jpeg',
- 'images/task_stages.jpeg'
- ],
'depends': [
'base_setup',
'product',
diff --git a/addons/project/tests/__init__.py b/addons/project/tests/__init__.py
index 2a0efe3614b..ac890eeab6d 100644
--- a/addons/project/tests/__init__.py
+++ b/addons/project/tests/__init__.py
@@ -21,6 +21,3 @@
from . import test_project_flow
-checks = [
- test_project_flow,
-]
diff --git a/addons/project_issue/__openerp__.py b/addons/project_issue/__openerp__.py
index d0a1d48476f..78a93ef877d 100644
--- a/addons/project_issue/__openerp__.py
+++ b/addons/project_issue/__openerp__.py
@@ -35,7 +35,6 @@ It allows the manager to quickly check the issues, assign them and decide on the
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/project-management',
- 'images': ['images/issue_analysis.jpeg','images/project_issue.jpeg'],
'depends': [
'sales_team',
'project',
diff --git a/addons/project_issue_sheet/__openerp__.py b/addons/project_issue_sheet/__openerp__.py
index 5a82e5692dc..eb264d865b3 100644
--- a/addons/project_issue_sheet/__openerp__.py
+++ b/addons/project_issue_sheet/__openerp__.py
@@ -32,7 +32,6 @@ Worklogs can be maintained to signify number of hours spent by users to handle a
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/project-management',
- 'images': ['images/project_issue_sheet_worklog.jpeg'],
'depends': [
'project_issue',
'hr_timesheet_sheet',
diff --git a/addons/project_timesheet/__openerp__.py b/addons/project_timesheet/__openerp__.py
index 383872955d4..4327d7a1871 100644
--- a/addons/project_timesheet/__openerp__.py
+++ b/addons/project_timesheet/__openerp__.py
@@ -33,7 +33,6 @@ with the effect of creating, editing and deleting either ways.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/project-management',
- 'images': ['images/invoice_task_work.jpeg', 'images/my_timesheet.jpeg', 'images/working_hour.jpeg'],
'depends': ['resource', 'project', 'hr_timesheet', 'hr_timesheet_invoice', 'account_analytic_analysis', 'procurement'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/purchase/__openerp__.py b/addons/purchase/__openerp__.py
index bc64adfadd6..2740000bf44 100644
--- a/addons/purchase/__openerp__.py
+++ b/addons/purchase/__openerp__.py
@@ -45,7 +45,6 @@ Dashboard / Reports for Purchase Management will include:
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/purchase',
- 'images': ['images/purchase_order.jpeg', 'images/purchase_analysis.jpeg', 'images/request_for_quotation.jpeg'],
'depends': ['stock_account', 'report'],
'data': [
'security/purchase_security.xml',
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index 33badfa98b4..088688fbb97 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -61,15 +61,14 @@ class purchase_order(osv.osv):
if not value: return False
if type(ids)!=type([]):
ids=[ids]
+ pol_obj = self.pool.get('purchase.order.line')
for po in self.browse(cr, uid, ids, context=context):
if po.order_line:
- cr.execute("""update purchase_order_line set
- date_planned=%s
- where
- order_id=%s and
- (date_planned=%s or date_planned<%s)""", (value,po.id,po.minimum_planned_date,value))
- cr.execute("""update purchase_order set
- minimum_planned_date=%s where id=%s""", (value, po.id))
+ pol_ids = pol_obj.search(cr, uid, [
+ ('order_id', '=', po.id), '|', ('date_planned', '=', po.minimum_planned_date), ('date_planned', '<', value)
+ ], context=context)
+ pol_obj.write(cr, uid, pol_ids, {'date_planned': value}, context=context)
+ self.pool.get('purchase.order').write(cr, uid, po.id, {'minimum_planned_date': value}, context=context)
self.invalidate_cache(cr, uid, context=context)
return True
@@ -715,7 +714,7 @@ class purchase_order(osv.osv):
'location_id': order.partner_id.property_stock_supplier.id,
'location_dest_id': order.location_id.id,
'picking_id': picking_id,
- 'partner_id': order.dest_address_id.id or order.partner_id.id,
+ 'partner_id': order.dest_address_id.id,
'move_dest_id': False,
'state': 'draft',
'purchase_line_id': order_line.id,
@@ -823,7 +822,7 @@ class purchase_order(osv.osv):
for order in self.browse(cr, uid, ids):
picking_vals = {
'picking_type_id': order.picking_type_id.id,
- 'partner_id': order.dest_address_id.id or order.partner_id.id,
+ 'partner_id': order.partner_id.id,
'date': max([l.date_planned for l in order.order_line]),
'origin': order.name
}
diff --git a/addons/purchase_analytic_plans/__openerp__.py b/addons/purchase_analytic_plans/__openerp__.py
index d29fda9fcd0..5d93127fd8d 100644
--- a/addons/purchase_analytic_plans/__openerp__.py
+++ b/addons/purchase_analytic_plans/__openerp__.py
@@ -33,7 +33,6 @@ on a supplier purchase order into several accounts and analytic plans.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/purchase',
- 'images': ['images/purchase_analytic_distribution.jpeg'],
'depends': ['purchase', 'account_analytic_plans'],
'data': ['purchase_analytic_plans_view.xml'],
'demo': [],
diff --git a/addons/purchase_double_validation/__openerp__.py b/addons/purchase_double_validation/__openerp__.py
index d1c0caa394b..faa02ff4854 100644
--- a/addons/purchase_double_validation/__openerp__.py
+++ b/addons/purchase_double_validation/__openerp__.py
@@ -23,7 +23,6 @@
'name' : 'Double Validation on Purchases',
'version' : '1.1',
'category': 'Purchase Management',
- 'images' : ['images/purchase_validation.jpeg'],
'depends' : ['base','purchase'],
'author' : 'OpenERP SA',
'description': """
diff --git a/addons/purchase_requisition/__openerp__.py b/addons/purchase_requisition/__openerp__.py
index 61e7195d528..85ead748637 100644
--- a/addons/purchase_requisition/__openerp__.py
+++ b/addons/purchase_requisition/__openerp__.py
@@ -22,7 +22,6 @@
'version': '0.1',
'author': 'OpenERP SA',
'category': 'Purchase Management',
- 'images': ['images/purchase_requisitions.jpeg'],
'website': 'https://www.odoo.com/page/purchase',
'description': """
This module allows you to manage your Purchase Requisition.
diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py
index 7fd33c367d9..277b3945798 100644
--- a/addons/purchase_requisition/purchase_requisition.py
+++ b/addons/purchase_requisition/purchase_requisition.py
@@ -303,7 +303,7 @@ class purchase_requisition_line(osv.osv):
_rec_name = 'product_id'
_columns = {
- 'product_id': fields.many2one('product.product', 'Product'),
+ 'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok', '=', True)]),
'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure'),
'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure')),
'requisition_id': fields.many2one('purchase.requisition', 'Call for Tenders', ondelete='cascade'),
diff --git a/addons/report/static/src/js/qwebactionmanager.js b/addons/report/static/src/js/qwebactionmanager.js
index 088c40e800a..74de70d03dd 100644
--- a/addons/report/static/src/js/qwebactionmanager.js
+++ b/addons/report/static/src/js/qwebactionmanager.js
@@ -1,14 +1,19 @@
openerp.report = function(instance) {
var wkhtmltopdf_state;
- var trigger_download = function(session, response, c) {
+ var trigger_download = function(session, response, c, action, options) {
session.get_file({
url: '/report/download',
data: {data: JSON.stringify(response)},
complete: openerp.web.unblockUI,
- error: c.rpc_error.bind(c)
+ error: c.rpc_error.bind(c),
+ success: function(){
+ if (action && options && !action.dialog) {
+ options.on_close();
+ }
+ },
});
- }
+ };
instance.web.ActionManager = instance.web.ActionManager.extend({
ir_actions_report_xml: function(action, options) {
@@ -79,10 +84,10 @@ workers to print a pdf version of the reports.'), true);
support for table-breaking between pages.<br><br><a href="http://wkhtmltopdf.org/" \
target="_blank">wkhtmltopdf.org</a>'), true);
}
- return trigger_download(self.session, response, c);
+ return trigger_download(self.session, response, c, action, options);
});
} else if (action.report_type === 'controller') {
- return trigger_download(self.session, response, c);
+ return trigger_download(self.session, response, c, action, options);
}
} else {
return self._super(action, options);
diff --git a/addons/report_intrastat/__openerp__.py b/addons/report_intrastat/__openerp__.py
index 3f14f096e99..be4b75282e4 100644
--- a/addons/report_intrastat/__openerp__.py
+++ b/addons/report_intrastat/__openerp__.py
@@ -43,5 +43,4 @@ European Union.""",
'test': ['test/report_intrastat_report.yml'],
'installable': True,
'auto_install': False,
- 'images': ['images/country_intrastat_code.jpeg','images/intrastat_code.jpeg'],
}
diff --git a/addons/report_webkit/__openerp__.py b/addons/report_webkit/__openerp__.py
index 1c6d34c5aa5..923006965eb 100644
--- a/addons/report_webkit/__openerp__.py
+++ b/addons/report_webkit/__openerp__.py
@@ -98,5 +98,4 @@ TODO:
],
'installable': True,
'auto_install': False,
- 'images': ['images/companies_webkit.jpeg','images/header_html.jpeg','images/header_img.jpeg'],
}
diff --git a/addons/resource/__openerp__.py b/addons/resource/__openerp__.py
index 3ed86a595c8..e60453e7723 100644
--- a/addons/resource/__openerp__.py
+++ b/addons/resource/__openerp__.py
@@ -48,5 +48,4 @@ associated to every resource. It also manages the leaves of every resource.
],
'installable': True,
'auto_install': False,
- 'images': ['images/resource_leaves_calendar.jpeg','images/resource_leaves_form.jpeg'],
}
diff --git a/addons/resource/tests/__init__.py b/addons/resource/tests/__init__.py
index 55e60eb7930..7c4e0b0887a 100644
--- a/addons/resource/tests/__init__.py
+++ b/addons/resource/tests/__init__.py
@@ -19,8 +19,5 @@
#
##############################################################################
-from openerp.addons.resource.tests import test_resource
+from . import test_resource
-checks = [
- test_resource,
-]
diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py
index afc84eccc6d..f11263eb5d6 100644
--- a/addons/sale/__openerp__.py
+++ b/addons/sale/__openerp__.py
@@ -58,7 +58,6 @@ The Dashboard for the Sales Manager will include
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/crm',
- 'images': ['images/Sale_order_line_to_invoice.jpeg','images/sale_order.jpeg','images/sales_analysis.jpeg'],
'depends': ['sales_team','account_voucher', 'procurement', 'report'],
'data': [
'wizard/sale_make_invoice_advance.xml',
diff --git a/addons/sale/wizard/sale_line_invoice.py b/addons/sale/wizard/sale_line_invoice.py
index 3b81374c721..1ac1c41a9cf 100644
--- a/addons/sale/wizard/sale_line_invoice.py
+++ b/addons/sale/wizard/sale_line_invoice.py
@@ -27,6 +27,31 @@ from openerp.exceptions import UserError
class sale_order_line_make_invoice(osv.osv_memory):
_name = "sale.order.line.make.invoice"
_description = "Sale OrderLine Make_invoice"
+
+ def _prepare_invoice(self, cr, uid, order, lines, context=None):
+ a = order.partner_id.property_account_receivable.id
+ if order.partner_id and order.partner_id.property_payment_term.id:
+ pay_term = order.partner_id.property_payment_term.id
+ else:
+ pay_term = False
+ return {
+ 'name': order.client_order_ref or '',
+ 'origin': order.name,
+ 'type': 'out_invoice',
+ 'reference': "P%dSO%d" % (order.partner_id.id, order.id),
+ 'account_id': a,
+ 'partner_id': order.partner_invoice_id.id,
+ 'invoice_line': [(6, 0, lines)],
+ 'currency_id' : order.pricelist_id.currency_id.id,
+ 'comment': order.note,
+ 'payment_term': pay_term,
+ 'fiscal_position': order.fiscal_position.id or order.partner_id.property_account_position.id,
+ 'user_id': order.user_id and order.user_id.id or False,
+ 'company_id': order.company_id and order.company_id.id or False,
+ 'date_invoice': fields.date.today(),
+ 'section_id': order.section_id.id,
+ }
+
def make_invoices(self, cr, uid, ids, context=None):
"""
@@ -56,27 +81,7 @@ class sale_order_line_make_invoice(osv.osv_memory):
@return:
"""
- a = order.partner_id.property_account_receivable.id
- if order.partner_id and order.partner_id.property_payment_term.id:
- pay_term = order.partner_id.property_payment_term.id
- else:
- pay_term = False
- inv = {
- 'name': order.client_order_ref or '',
- 'origin': order.name,
- 'type': 'out_invoice',
- 'reference': "P%dSO%d" % (order.partner_id.id, order.id),
- 'account_id': a,
- 'partner_id': order.partner_invoice_id.id,
- 'invoice_line': [(6, 0, lines)],
- 'currency_id' : order.pricelist_id.currency_id.id,
- 'comment': order.note,
- 'payment_term': pay_term,
- 'fiscal_position': order.fiscal_position.id or order.partner_id.property_account_position.id,
- 'user_id': order.user_id and order.user_id.id or False,
- 'company_id': order.company_id and order.company_id.id or False,
- 'date_invoice': fields.date.today(),
- }
+ inv = self._prepare_invoice(cr, uid, order, lines)
inv_id = self.pool.get('account.invoice').create(cr, uid, inv)
return inv_id
diff --git a/addons/sale/wizard/sale_make_invoice_advance.py b/addons/sale/wizard/sale_make_invoice_advance.py
index c62f6eb3aa6..dcd92e48d4d 100644
--- a/addons/sale/wizard/sale_make_invoice_advance.py
+++ b/addons/sale/wizard/sale_make_invoice_advance.py
@@ -145,7 +145,8 @@ class sale_advance_payment_inv(osv.osv_memory):
'currency_id': sale.pricelist_id.currency_id.id,
'comment': '',
'payment_term': sale.payment_term.id,
- 'fiscal_position': sale.fiscal_position.id or sale.partner_id.property_account_position.id
+ 'fiscal_position': sale.fiscal_position.id or sale.partner_id.property_account_position.id,
+ 'section_id': sale.section_id.id,
}
result.append((sale.id, inv_values))
return result
diff --git a/addons/sale_analytic_plans/__openerp__.py b/addons/sale_analytic_plans/__openerp__.py
index c9276d0935a..44e60c6e2da 100644
--- a/addons/sale_analytic_plans/__openerp__.py
+++ b/addons/sale_analytic_plans/__openerp__.py
@@ -31,7 +31,6 @@ Using this module you will be able to link analytic accounts to sales orders.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/crm',
- 'images': ['images/sale_order_analytic_account.jpeg', 'images/sales_order_line.jpeg'],
'depends': ['sale', 'account_analytic_plans'],
'data': ['sale_analytic_plans_view.xml'],
'demo': [],
diff --git a/addons/sale_crm/__openerp__.py b/addons/sale_crm/__openerp__.py
index 90d89bb4758..c13509b8a3a 100644
--- a/addons/sale_crm/__openerp__.py
+++ b/addons/sale_crm/__openerp__.py
@@ -36,7 +36,6 @@ modules.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/crm',
- 'images': ['images/crm_statistics_dashboard.jpeg', 'images/opportunity_to_quote.jpeg'],
'depends': ['sale', 'crm', 'web_kanban_gauge'],
'data': [
'wizard/crm_make_sale_view.xml',
diff --git a/addons/sale_journal/__openerp__.py b/addons/sale_journal/__openerp__.py
index 07a6b53925e..68d8966de86 100644
--- a/addons/sale_journal/__openerp__.py
+++ b/addons/sale_journal/__openerp__.py
@@ -50,7 +50,6 @@ Some statistics by journals are provided.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/billing',
- 'images': ['images/invoice_type.jpeg'],
'depends': ['sale_stock'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/sale_margin/__openerp__.py b/addons/sale_margin/__openerp__.py
index 81c7fdb499c..5125b9b8c4f 100644
--- a/addons/sale_margin/__openerp__.py
+++ b/addons/sale_margin/__openerp__.py
@@ -30,7 +30,6 @@ This gives the profitability by calculating the difference between the Unit
Price and Cost Price.
""",
'author':'OpenERP SA',
- 'images':['images/sale_margin.jpeg'],
'depends':['sale'],
'demo':['sale_margin_demo.xml'],
'test': ['test/sale_margin.yml'],
diff --git a/addons/sale_mrp/__openerp__.py b/addons/sale_mrp/__openerp__.py
index bd5b1caa1ea..6fe723427fb 100644
--- a/addons/sale_mrp/__openerp__.py
+++ b/addons/sale_mrp/__openerp__.py
@@ -33,7 +33,6 @@ from sales order. It adds sales name and sales Reference on production order.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/manufacturing',
- 'images': ['images/SO_to_MO.jpeg'],
'depends': ['mrp', 'sale_stock'],
'data': [
'security/ir.model.access.csv',
diff --git a/addons/sale_mrp/tests/__init__.py b/addons/sale_mrp/tests/__init__.py
index 45d5119d43c..7ed715e54fb 100644
--- a/addons/sale_mrp/tests/__init__.py
+++ b/addons/sale_mrp/tests/__init__.py
@@ -21,6 +21,3 @@
from . import test_move_explode
-checks = [
- test_move_explode,
-]
diff --git a/addons/sale_order_dates/__openerp__.py b/addons/sale_order_dates/__openerp__.py
index 6edc18142d1..d309900f19d 100644
--- a/addons/sale_order_dates/__openerp__.py
+++ b/addons/sale_order_dates/__openerp__.py
@@ -36,7 +36,6 @@ You can add the following additional dates to a sales order:
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/crm',
- 'images': ['images/sale_order_dates.jpeg'],
'depends': ['sale_stock'],
'data': ['sale_order_dates_view.xml'],
'demo': [],
diff --git a/addons/sale_service/__openerp__.py b/addons/sale_service/__openerp__.py
index 94598e37636..8f02026361b 100644
--- a/addons/sale_service/__openerp__.py
+++ b/addons/sale_service/__openerp__.py
@@ -47,7 +47,6 @@ completed.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/crm',
- 'images': ['images/product.jpeg', 'images/task_from_SO.jpeg'],
'depends': ['project', 'procurement', 'sale', 'procurement_jit'],
'data': ['views/sale_service_view.xml'],
'demo': ['demo/sale_service_demo.xml'],
diff --git a/addons/sale_stock/__openerp__.py b/addons/sale_stock/__openerp__.py
index 7d0200319ef..a7c68b4cb2a 100644
--- a/addons/sale_stock/__openerp__.py
+++ b/addons/sale_stock/__openerp__.py
@@ -44,7 +44,6 @@ You can choose flexible invoicing methods:
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/warehouse',
- 'images': ['images/deliveries_to_invoice.jpeg'],
'depends': ['sale', 'stock_account'],
'data': [
'security/sale_stock_security.xml',
diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py
index f7cd516d004..683b2ff47db 100644
--- a/addons/sale_stock/sale_stock.py
+++ b/addons/sale_stock/sale_stock.py
@@ -377,7 +377,7 @@ class stock_move(osv.osv):
return invoice_line_id
def _get_master_data(self, cr, uid, move, company, context=None):
- if move.procurement_id and move.procurement_id.sale_line_id:
+ if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.order_policy == 'picking':
sale_order = move.procurement_id.sale_line_id.order_id
return sale_order.partner_invoice_id, sale_order.user_id.id, sale_order.pricelist_id.currency_id.id
return super(stock_move, self)._get_master_data(cr, uid, move, company, context=context)
@@ -417,7 +417,7 @@ class stock_picking(osv.osv):
"""
saleorder_ids = self.pool['sale.order'].search(cr, uid, [('procurement_group_id' ,'=', picking.group_id.id)], context=context)
saleorders = self.pool['sale.order'].browse(cr, uid, saleorder_ids, context=context)
- if saleorders and saleorders[0]:
+ if saleorders and saleorders[0] and saleorders[0].order_policy == 'picking':
saleorder = saleorders[0]
return saleorder.partner_invoice_id.id
return super(stock_picking, self)._get_partner_to_invoice(cr, uid, picking, context=context)
diff --git a/addons/share/__openerp__.py b/addons/share/__openerp__.py
index 87daf1fa36d..0354085d873 100644
--- a/addons/share/__openerp__.py
+++ b/addons/share/__openerp__.py
@@ -52,5 +52,4 @@ synchronization with other companies.
'auto_install': True,
'web': True,
'qweb' : ['static/src/xml/*.xml'],
- 'images': ['images/share_wizard.jpeg','images/sharing_wizard_step1.jpeg', 'images/sharing_wizard_step2.jpeg'],
}
diff --git a/addons/share/wizard/share_wizard.py b/addons/share/wizard/share_wizard.py
index 161cbfba112..3d6246c5eff 100644
--- a/addons/share/wizard/share_wizard.py
+++ b/addons/share/wizard/share_wizard.py
@@ -831,7 +831,7 @@ class share_wizard(osv.TransientModel):
body += '%s\n\n' % ((user.signature or ''))
body += "--\n"
body += _("Odoo is a powerful and user-friendly suite of Business Applications (CRM, Sales, HR, etc.)\n"
- "It is open source and can be found on http://www.openerp.com.")
+ "It is open source and can be found on https://www.odoo.com.")
msg_id = message_obj.schedule_with_attach(cr, uid, user.email, [email_to], subject, body, model='', context=context)
notification_obj.create(cr, uid, {'user_id': result_line.user_id.id, 'message_id': msg_id}, context=context)
@@ -865,7 +865,7 @@ class share_wizard(osv.TransientModel):
body += "\n\n%s\n\n" % ( (user.signature or '') )
body += "--\n"
body += _("Odoo is a powerful and user-friendly suite of Business Applications (CRM, Sales, HR, etc.)\n"
- "It is open source and can be found on http://www.openerp.com.")
+ "It is open source and can be found on https://www.odoo.com.")
mail_ids.append(mail_mail.create(cr, uid, {
'email_from': user.email,
'email_to': email_to,
diff --git a/addons/stock/__openerp__.py b/addons/stock/__openerp__.py
index 215a160a3ba..cd39d7ec2cf 100644
--- a/addons/stock/__openerp__.py
+++ b/addons/stock/__openerp__.py
@@ -51,7 +51,6 @@ Dashboard / Reports for Warehouse Management will include:
* Moves Analysis
""",
'website': 'https://www.odoo.com/page/warehouse',
- 'images': ['images/stock_forecast_report.png', 'images/delivery_orders.jpeg', 'images/inventory_analysis.jpeg','images/location.jpeg','images/moves_analysis.jpeg','images/physical_inventories.jpeg'],
'depends': ['product', 'procurement', 'board', 'web_kanban_gauge', 'web_kanban_sparkline', 'barcodes'],
'category': 'Warehouse Management',
'sequence': 16,
diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml
index 6a47664d2ab..23a3988871a 100644
--- a/addons/stock/product_view.xml
+++ b/addons/stock/product_view.xml
@@ -23,7 +23,7 @@
<field name="inherit_id" ref="product.product_template_tree_view"/>
<field name="arch" type="xml">
<tree position="attributes">
- <attribute name="colors">{'red':virtual_available&lt;0, 'blue':virtual_available&gt;=0 and state in ('draft', 'end', 'obsolete'), 'black':virtual_available&gt;=0 and state not in ('draft', 'end', 'obsolete')}</attribute>
+ <attribute name="colors">red:virtual_available&lt;0;blue:virtual_available&gt;=0 and state in ('draft', 'end', 'obsolete');black:virtual_available&gt;=0 and state not in ('draft', 'end', 'obsolete')</attribute>
</tree>
<field name="uom_id" position="after">
<field name="qty_available"/>
diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv
index cbe1d652e4d..4bb2c1cbf52 100644
--- a/addons/stock/security/ir.model.access.csv
+++ b/addons/stock/security/ir.model.access.csv
@@ -68,8 +68,10 @@ access_stock_pack_operation_manager,stock.pack.operation manager,model_stock_pac
access_stock_pack_operation_user,stock.pack.operation user,model_stock_pack_operation,stock.group_stock_user,1,1,1,1
access_stock_pack_operation_all,stock.pack.operation all users,model_stock_pack_operation,base.group_user,1,0,0,0
access_product_putaway_all,product.putaway all users,model_product_putaway,base.group_user,1,0,0,0
+access_product_putaway_manager,product.putaway all managers,model_product_putaway,stock.group_stock_manager,1,1,1,1
access_stock_removal_all,product.removal all users,model_product_removal,base.group_user,1,0,0,0
access_stock_fixed_putaway_strat,stock_fixed_putaway_strat managers,model_stock_fixed_putaway_strat,stock.group_stock_manager,1,1,1,1
+access_stock_fixed_putaway_user,stock_fixed_putaway_strat user,model_stock_fixed_putaway_strat,stock.group_stock_user,1,0,0,0
access_stock_move_operation_link_manager,stock.move.operation.link manager,model_stock_move_operation_link,stock.group_stock_manager,1,1,1,1
access_stock_move_operation_link_user,stock.move.operation.link user,model_stock_move_operation_link,stock.group_stock_user,1,1,1,1
access_stock_move_operation_link_all,stock.move.operation.link all users,model_stock_move_operation_link,base.group_user,1,0,0,0
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index cff0da06742..df592b5285f 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -1061,6 +1061,7 @@ class stock_picking(osv.osv):
'product_qty': 1.0,
'location_id': pack.location_id.id,
'location_dest_id': quants_suggested_locations[pack_quants[0]],
+ 'owner_id': picking.owner_id.id,
})
#remove the quants inside the package so that they are excluded from the rest of the computation
for quant in pack_quants:
@@ -1079,7 +1080,7 @@ class stock_picking(osv.osv):
if qty <= 0:
continue
suggested_location_id = _picking_putaway_apply(product)
- key = (product.id, False, False, False, picking.location_id.id, suggested_location_id)
+ key = (product.id, False, False, picking.owner_id.id, picking.location_id.id, suggested_location_id)
if qtys_grouped.get(key):
qtys_grouped[key] += qty
else:
@@ -1735,6 +1736,7 @@ class stock_move(osv.osv):
'product_uom': fields.many2one('product.uom', 'Unit of Measure', required=True, states={'done': [('readonly', True)]}),
'product_uos_qty': fields.float('Quantity (UOS)', digits_compute=dp.get_precision('Product Unit of Measure'), states={'done': [('readonly', True)]}),
'product_uos': fields.many2one('product.uom', 'Product UOS', states={'done': [('readonly', True)]}),
+ 'product_tmpl_id': fields.related('product_id', 'product_tmpl_id', type='many2one', relation='product.template', string='Product Template'),
'product_packaging': fields.many2one('product.packaging', 'Prefered Packaging', help="It specifies attributes of packaging like type, quantity of packaging,etc."),
@@ -1813,8 +1815,7 @@ class stock_move(osv.osv):
return False
def _default_destination_address(self, cr, uid, context=None):
- user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
- return user.company_id.partner_id.id
+ return False
_defaults = {
'location_id': _default_location_source,
@@ -2975,7 +2976,7 @@ class stock_warehouse(osv.osv):
inter_wh_route_vals = self._get_inter_wh_route(cr, uid, warehouse, wh, context=context)
inter_wh_route_id = route_obj.create(cr, uid, vals=inter_wh_route_vals, context=context)
values = [(output_loc, transit_location, wh.out_type_id.id, wh), (transit_location, input_loc, warehouse.in_type_id.id, warehouse)]
- pull_rules_list = self._get_supply_pull_rules(cr, uid, warehouse, values, inter_wh_route_id, context=context)
+ pull_rules_list = self._get_supply_pull_rules(cr, uid, wh.id, values, inter_wh_route_id, context=context)
for pull_rule in pull_rules_list:
pull_obj.create(cr, uid, vals=pull_rule, context=context)
#if the warehouse is also set as default resupply method, assign this route automatically to the warehouse
@@ -3036,7 +3037,7 @@ class stock_warehouse(osv.osv):
'sequence': 10,
}
- def _get_supply_pull_rules(self, cr, uid, supplied_warehouse, values, new_route_id, context=None):
+ def _get_supply_pull_rules(self, cr, uid, supply_warehouse, values, new_route_id, context=None):
pull_rules_list = []
for from_loc, dest_loc, pick_type_id, warehouse in values:
pull_rules_list.append({
@@ -3047,8 +3048,8 @@ class stock_warehouse(osv.osv):
'action': 'move',
'picking_type_id': pick_type_id,
'procure_method': warehouse.lot_stock_id.id != from_loc.id and 'make_to_order' or 'make_to_stock', # first part of the resuply route is MTS
- 'warehouse_id': supplied_warehouse.id,
- 'propagate_warehouse_id': warehouse.id,
+ 'warehouse_id': warehouse.id,
+ 'propagate_warehouse_id': supply_warehouse,
})
return pull_rules_list
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index 7d82c4d65be..477d2d47489 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -288,7 +288,7 @@
<field name="product_id"/>
<field name="product_uom_qty"/>
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
- <field name="product_packaging" domain="[('product_tmpl_id','=',product_id)]" groups="product.group_stock_packaging"/>
+ <field name="product_packaging" domain="[('product_tmpl_id','=',product_tmpl_id)]" groups="product.group_stock_packaging"/>
<field name="picking_id"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="location_dest_id" groups="stock.group_locations"/>
@@ -1111,7 +1111,8 @@
<field name="product_uos" on_change="onchange_quantity(product_id, product_uom_qty, product_uom, product_uos)" class="oe_inline"/>
</div>
<field name="name"/>
- <field name="product_packaging" groups="product.group_stock_packaging" domain="[('product_tmpl_id','=',product_id)]"/>
+ <field name="product_tmpl_id" invisible="1"/>
+ <field name="product_packaging" groups="product.group_stock_packaging" domain="[('product_tmpl_id','=',product_tmpl_id)]"/>
</group>
<group>
<field name="create_date" invisible="1"/>
diff --git a/addons/stock/tests/__init__.py b/addons/stock/tests/__init__.py
new file mode 100644
index 00000000000..9d14a332a28
--- /dev/null
+++ b/addons/stock/tests/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import test_stock_flow
diff --git a/addons/stock/tests/common.py b/addons/stock/tests/common.py
new file mode 100644
index 00000000000..432c1e57f88
--- /dev/null
+++ b/addons/stock/tests/common.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+
+from openerp.tests import common
+
+
+class TestStockCommon(common.TransactionCase):
+
+ def setUp(self):
+ super(TestStockCommon, self).setUp()
+
+ self.ProductObj = self.env['product.product']
+ self.UomObj = self.env['product.uom']
+ self.PartnerObj = self.env['res.partner']
+ self.ModelDataObj = self.env['ir.model.data']
+ self.StockPackObj = self.env['stock.pack.operation']
+ self.StockQuantObj = self.env['stock.quant']
+ self.PickingObj = self.env['stock.picking']
+ self.MoveObj = self.env['stock.move']
+ self.InvObj = self.env['stock.inventory']
+ self.InvLineObj = self.env['stock.inventory.line']
+
+ # Model Data
+ self.partner_agrolite_id = self.ModelDataObj.xmlid_to_res_id('base.res_partner_2')
+ self.partner_delta_id = self.ModelDataObj.xmlid_to_res_id('base.res_partner_4')
+ self.picking_type_in = self.ModelDataObj.xmlid_to_res_id('stock.picking_type_in')
+ self.picking_type_out = self.ModelDataObj.xmlid_to_res_id('stock.picking_type_out')
+ self.supplier_location = self.ModelDataObj.xmlid_to_res_id('stock.stock_location_suppliers')
+ self.stock_location = self.ModelDataObj.xmlid_to_res_id('stock.stock_location_stock')
+ self.customer_location = self.ModelDataObj.xmlid_to_res_id('stock.stock_location_customers')
+ self.categ_unit = self.ModelDataObj.xmlid_to_res_id('product.product_uom_categ_unit')
+ self.categ_kgm = self.ModelDataObj.xmlid_to_res_id('product.product_uom_categ_kgm')
+
+ # Product Created A, B, C, D
+ self.productA = self.ProductObj.create({'name': 'Product A'})
+ self.productB = self.ProductObj.create({'name': 'Product B'})
+ self.productC = self.ProductObj.create({'name': 'Product C'})
+ self.productD = self.ProductObj.create({'name': 'Product D'})
+
+ # Configure unit of measure.
+ self.uom_kg = self.UomObj.create({
+ 'name': 'Test-KG',
+ 'category_id': self.categ_kgm,
+ 'factor_inv': 1,
+ 'factor': 1,
+ 'uom_type': 'reference',
+ 'rounding': 0.000001})
+ self.uom_tone = self.UomObj.create({
+ 'name': 'Test-Tone',
+ 'category_id': self.categ_kgm,
+ 'uom_type': 'bigger',
+ 'factor_inv': 1000.0,
+ 'rounding': 0.001})
+ self.uom_gm = self.UomObj.create({
+ 'name': 'Test-G',
+ 'category_id': self.categ_kgm,
+ 'uom_type': 'smaller',
+ 'factor': 1000.0,
+ 'rounding': 0.001})
+ self.uom_mg = self.UomObj.create({
+ 'name': 'Test-MG',
+ 'category_id': self.categ_kgm,
+ 'uom_type': 'smaller',
+ 'factor': 100000.0,
+ 'rounding': 0.001})
+ # Check Unit
+ self.uom_unit = self.UomObj.create({
+ 'name': 'Test-Unit',
+ 'category_id': self.categ_unit,
+ 'factor': 1,
+ 'uom_type': 'reference',
+ 'rounding': 1.0})
+ self.uom_dozen = self.UomObj.create({
+ 'name': 'Test-DozenA',
+ 'category_id': self.categ_unit,
+ 'factor_inv': 12,
+ 'uom_type': 'bigger',
+ 'rounding': 0.001})
+ self.uom_sdozen = self.UomObj.create({
+ 'name': 'Test-SDozenA',
+ 'category_id': self.categ_unit,
+ 'factor_inv': 144,
+ 'uom_type': 'bigger',
+ 'rounding': 0.001})
+ self.uom_sdozen_round = self.UomObj.create({
+ 'name': 'Test-SDozenA Round',
+ 'category_id': self.categ_unit,
+ 'factor_inv': 144,
+ 'uom_type': 'bigger',
+ 'rounding': 1.0})
+
+ # Product for different unit of measure.
+ self.DozA = self.ProductObj.create({'name': 'Dozon-A', 'uom_id': self.uom_dozen.id, 'uom_po_id': self.uom_dozen.id})
+ self.SDozA = self.ProductObj.create({'name': 'SuperDozon-A', 'uom_id': self.uom_sdozen.id, 'uom_po_id': self.uom_sdozen.id})
+ self.SDozARound = self.ProductObj.create({'name': 'SuperDozenRound-A', 'uom_id': self.uom_sdozen_round.id, 'uom_po_id': self.uom_sdozen_round.id})
+ self.UnitA = self.ProductObj.create({'name': 'Unit-A'})
+ self.kgB = self.ProductObj.create({'name': 'kg-B', 'uom_id': self.uom_kg.id, 'uom_po_id': self.uom_kg.id})
+ self.gB = self.ProductObj.create({'name': 'g-B', 'uom_id': self.uom_gm.id, 'uom_po_id': self.uom_gm.id})
diff --git a/addons/stock/tests/test_stock_flow.py b/addons/stock/tests/test_stock_flow.py
new file mode 100644
index 00000000000..32c9e5e3eb0
--- /dev/null
+++ b/addons/stock/tests/test_stock_flow.py
@@ -0,0 +1,1097 @@
+# -*- coding: utf-8 -*-
+
+from openerp.addons.stock.tests.common import TestStockCommon
+from openerp.tools import mute_logger, float_round
+
+
+class TestStockFlow(TestStockCommon):
+
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
+ def test_00_picking_create_and_transfer_quantity(self):
+ """ Basic stock operation on incoming and outgoing shipment. """
+ LotObj = self.env['stock.production.lot']
+ # ----------------------------------------------------------------------
+ # Create incoming shipment of product A, B, C, D
+ # ----------------------------------------------------------------------
+ # Product A ( 1 Unit ) , Product C ( 10 Unit )
+ # Product B ( 1 Unit ) , Product D ( 10 Unit )
+ # Product D ( 5 Unit )
+ # ----------------------------------------------------------------------
+
+ picking_in = self.PickingObj.create({
+ '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': 1,
+ 'product_uom': self.productA.uom_id.id,
+ 'picking_id': picking_in.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': 1,
+ 'product_uom': self.productB.uom_id.id,
+ 'picking_id': picking_in.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.productC.name,
+ 'product_id': self.productC.id,
+ 'product_uom_qty': 10,
+ 'product_uom': self.productC.uom_id.id,
+ 'picking_id': picking_in.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.productD.name,
+ 'product_id': self.productD.id,
+ 'product_uom_qty': 10,
+ 'product_uom': self.productD.uom_id.id,
+ 'picking_id': picking_in.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.productD.name,
+ 'product_id': self.productD.id,
+ 'product_uom_qty': 5,
+ 'product_uom': self.productD.uom_id.id,
+ 'picking_id': picking_in.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+
+ # Check incoming shipment move lines state.
+ for move in picking_in.move_lines:
+ self.assertEqual(move.state, 'draft', 'Wrong state of move line.')
+ # Confirm incoming shipment.
+ picking_in.action_confirm()
+ # Check incoming shipment move lines state.
+ for move in picking_in.move_lines:
+ self.assertEqual(move.state, 'assigned', 'Wrong state of move line.')
+
+ # ----------------------------------------------------------------------
+ # Replace pack operation of incoming shipments.
+ # ----------------------------------------------------------------------
+
+ picking_in.do_prepare_partial()
+ self.StockPackObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', picking_in.id)]).write({
+ 'product_qty': 4.0})
+ self.StockPackObj.search([('product_id', '=', self.productB.id), ('picking_id', '=', picking_in.id)]).write({
+ 'product_qty': 5.0})
+ self.StockPackObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', picking_in.id)]).write({
+ 'product_qty': 5.0})
+ self.StockPackObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', picking_in.id)]).write({
+ 'product_qty': 5.0})
+ lot2_productC = LotObj.create({'name': 'C Lot 2', 'product_id': self.productC.id})
+ self.StockPackObj.create({
+ 'product_id': self.productC.id,
+ 'product_qty': 2,
+ 'product_uom_id': self.productC.uom_id.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location,
+ 'picking_id': picking_in.id,
+ 'lot_id': lot2_productC.id})
+ self.StockPackObj.create({
+ 'product_id': self.productD.id,
+ 'product_qty': 2,
+ 'product_uom_id': self.productD.uom_id.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location,
+ 'picking_id': picking_in.id})
+
+ # Check incoming shipment total quantity of pack operation
+ packs = self.StockPackObj.search([('picking_id', '=', picking_in.id)])
+ total_qty = [pack.product_qty for pack in packs]
+ self.assertEqual(sum(total_qty), 23, 'Wrong quantity in pack operation (%s found instead of 23)' % (sum(total_qty)))
+
+ # Transfer Incoming Shipment.
+ picking_in.do_transfer()
+
+ # ----------------------------------------------------------------------
+ # Check state, quantity and total moves of incoming shipment.
+ # ----------------------------------------------------------------------
+
+ # Check total no of move lines of incoming shipment.
+ self.assertEqual(len(picking_in.move_lines), 6, 'Wrong number of move lines.')
+ # Check incoming shipment state.
+ self.assertEqual(picking_in.state, 'done', 'Incoming shipment state should be done.')
+ # Check incoming shipment move lines state.
+ for move in picking_in.move_lines:
+ self.assertEqual(move.state, 'done', 'Wrong state of move line.')
+ # Check product A done quantity must be 3 and 1
+ moves = self.MoveObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', picking_in.id)])
+ a_done_qty = [move.product_uom_qty for move in moves]
+ self.assertEqual(set(a_done_qty), set([1.0, 3.0]), 'Wrong move quantity for product A.')
+ # Check product B done quantity must be 4 and 1
+ moves = self.MoveObj.search([('product_id', '=', self.productB.id), ('picking_id', '=', picking_in.id)])
+ b_done_qty = [move.product_uom_qty for move in moves]
+ self.assertEqual(set(b_done_qty), set([4.0, 1.0]), 'Wrong move quantity for product B.')
+ # Check product C done quantity must be 7
+ c_done_qty = self.MoveObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', picking_in.id)], limit=1).product_uom_qty
+ self.assertEqual(c_done_qty, 7.0, 'Wrong move quantity of product C (%s found instead of 7)' % (c_done_qty))
+ # Check product D done quantity must be 7
+ d_done_qty = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', picking_in.id)], limit=1).product_uom_qty
+ self.assertEqual(d_done_qty, 7.0, 'Wrong move quantity of product D (%s found instead of 7)' % (d_done_qty))
+
+ # ----------------------------------------------------------------------
+ # Check Back order of Incoming shipment.
+ # ----------------------------------------------------------------------
+
+ # Check back order created or not.
+ 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.')
+ # 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]
+ self.assertEqual(sum(product_c_qty), 3.0, 'Wrong move quantity of product C (%s found instead of 3)' % (product_c_qty))
+ # Check back order should be created with 8 quantity of product D.
+ moves = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', back_order_in.id)])
+ product_d_qty = [move.product_uom_qty for move in moves]
+ self.assertEqual(sum(product_d_qty), 8.0, 'Wrong move quantity of product D (%s found instead of 8)' % (product_d_qty))
+
+ # ======================================================================
+ # Create Outgoing shipment with ...
+ # product A ( 10 Unit ) , product B ( 5 Unit )
+ # product C ( 3 unit ) , product D ( 10 Unit )
+ # ======================================================================
+
+ picking_out = self.PickingObj.create({
+ 'partner_id': self.partner_agrolite_id,
+ 'picking_type_id': self.picking_type_out})
+ self.MoveObj.create({
+ 'name': self.productA.name,
+ 'product_id': self.productA.id,
+ 'product_uom_qty': 10,
+ 'product_uom': self.productA.uom_id.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_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': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ self.MoveObj.create({
+ 'name': self.productC.name,
+ 'product_id': self.productC.id,
+ 'product_uom_qty': 3,
+ 'product_uom': self.productC.uom_id.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ self.MoveObj.create({
+ 'name': self.productD.name,
+ 'product_id': self.productD.id,
+ 'product_uom_qty': 10,
+ 'product_uom': self.productD.uom_id.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ # Confirm outgoing shipment.
+ picking_out.action_confirm()
+ for move in picking_out.move_lines:
+ self.assertEqual(move.state, 'confirmed', 'Wrong state of move line.')
+ # Product assign to outgoing shipments
+ picking_out.action_assign()
+ for move in picking_out.move_lines:
+ self.assertEqual(move.state, 'assigned', 'Wrong state of move line.')
+ # Check availability for product A
+ aval_a_qty = self.MoveObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', picking_out.id)], limit=1).availability
+ self.assertEqual(aval_a_qty, 4.0, 'Wrong move quantity availability of product A (%s found instead of 4)' % (aval_a_qty))
+ # Check availability for product B
+ aval_b_qty = self.MoveObj.search([('product_id', '=', self.productB.id), ('picking_id', '=', picking_out.id)], limit=1).availability
+ self.assertEqual(aval_b_qty, 5.0, 'Wrong move quantity availability of product B (%s found instead of 5)' % (aval_b_qty))
+ # Check availability for product C
+ aval_c_qty = self.MoveObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', picking_out.id)], limit=1).availability
+ self.assertEqual(aval_c_qty, 3.0, 'Wrong move quantity availability of product C (%s found instead of 3)' % (aval_c_qty))
+ # Check availability for product D
+ aval_d_qty = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', picking_out.id)], limit=1).availability
+ self.assertEqual(aval_d_qty, 7.0, 'Wrong move quantity availability of product D (%s found instead of 7)' % (aval_d_qty))
+
+ # ----------------------------------------------------------------------
+ # Replace pack operation of outgoing shipment.
+ # ----------------------------------------------------------------------
+
+ picking_out.do_prepare_partial()
+ self.StockPackObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', picking_out.id)]).write({'product_qty': 2.0})
+ self.StockPackObj.search([('product_id', '=', self.productB.id), ('picking_id', '=', picking_out.id)]).write({'product_qty': 3.0})
+ self.StockPackObj.create({
+ 'product_id': self.productB.id,
+ 'product_qty': 2,
+ 'product_uom_id': self.productB.uom_id.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location,
+ 'picking_id': picking_out.id})
+ self.StockPackObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', picking_out.id)]).write({
+ 'product_qty': 2.0, 'lot_id': lot2_productC.id})
+ self.StockPackObj.create({
+ 'product_id': self.productC.id,
+ 'product_qty': 3,
+ 'product_uom_id': self.productC.uom_id.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location,
+ 'picking_id': picking_out.id})
+ self.StockPackObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', picking_out.id)]).write({'product_qty': 6.0})
+
+ # Transfer picking.
+ picking_out.do_transfer()
+
+ # ----------------------------------------------------------------------
+ # Check state, quantity and total moves of outgoing shipment.
+ # ----------------------------------------------------------------------
+
+ # check outgoing shipment status.
+ self.assertEqual(picking_out.state, 'done', 'Wrong state of outgoing shipment.')
+ # check outgoing shipment total moves and and its state.
+ self.assertEqual(len(picking_out.move_lines), 5, 'Wrong number of move lines')
+ for move in picking_out.move_lines:
+ self.assertEqual(move.state, 'done', 'Wrong state of move line.')
+ back_order_out = self.PickingObj.search([('backorder_id', '=', picking_out.id)])
+ #------------------
+ # Check back order.
+ # -----------------
+ self.assertEqual(len(back_order_out), 1, 'Back order should be created.')
+ # Check total move lines of back order.
+ self.assertEqual(len(back_order_out.move_lines), 2, 'Wrong number of move lines')
+ # Check back order should be created with 8 quantity of product A.
+ product_a_qty = self.MoveObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', back_order_out.id)], limit=1).product_uom_qty
+ self.assertEqual(product_a_qty, 8.0, 'Wrong move quantity of product A (%s found instead of 8)' % (product_a_qty))
+ # Check back order should be created with 4 quantity of product D.
+ product_d_qty = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', back_order_out.id)], limit=1).product_uom_qty
+ self.assertEqual(product_d_qty, 4.0, 'Wrong move quantity of product D (%s found instead of 4)' % (product_d_qty))
+
+ #-----------------------------------------------------------------------
+ # Check stock location quant quantity and quantity available
+ # of product A, B, C, D
+ #-----------------------------------------------------------------------
+
+ # Check quants and available quantity for product A
+ quants = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+
+ self.assertEqual(sum(total_qty), 2.0, 'Expecting 2.0 Unit , got %.4f Unit on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.productA.qty_available, 2.0, 'Wrong quantity available (%s found instead of 2.0)' % (self.productA.qty_available))
+ # Check quants and available quantity for product B
+ quants = self.StockQuantObj.search([('product_id', '=', self.productB.id), ('location_id', '=', self.stock_location)])
+ self.assertFalse(quants, 'No quant should found as outgoing shipment took everything out of stock.')
+ self.assertEqual(self.productB.qty_available, 0.0, 'Product B should have zero quantity available.')
+ # Check quants and available quantity for product C
+ quants = self.StockQuantObj.search([('product_id', '=', self.productC.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 2.0, 'Expecting 2.0 Unit, got %.4f Unit on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.productC.qty_available, 2.0, 'Wrong quantity available (%s found instead of 2.0)' % (self.productC.qty_available))
+ # Check quants and available quantity for product D
+ quant = self.StockQuantObj.search([('product_id', '=', self.productD.id), ('location_id', '=', self.stock_location)], limit=1)
+ self.assertEqual(quant.qty, 1.0, 'Expecting 1.0 Unit , got %.4f Unit on location stock!' % (quant.qty))
+ self.assertEqual(self.productD.qty_available, 1.0, 'Wrong quantity available (%s found instead of 1.0)' % (self.productD.qty_available))
+ #-----------------------------------------------------------------------
+ # Back Order of Incoming shipment
+ #-----------------------------------------------------------------------
+
+ lot3_productC = LotObj.create({'name': 'Lot 3', 'product_id': self.productC.id})
+ lot4_productC = LotObj.create({'name': 'Lot 4', 'product_id': self.productC.id})
+ lot5_productC = LotObj.create({'name': 'Lot 5', 'product_id': self.productC.id})
+ lot6_productC = LotObj.create({'name': 'Lot 6', 'product_id': self.productC.id})
+ lot1_productD = LotObj.create({'name': 'Lot 1', 'product_id': self.productD.id})
+ lot2_productD = LotObj.create({'name': 'Lot 2', 'product_id': self.productD.id})
+
+ # Confirm back order of incoming shipment.
+ back_order_in.action_confirm()
+ self.assertEqual(back_order_in.state, 'assigned', 'Wrong state of incoming shipment back order.')
+ for move in back_order_in.move_lines:
+ self.assertEqual(move.state, 'assigned', 'Wrong state of move line.')
+
+ # ----------------------------------------------------------------------
+ # Replace pack operation (Back order of Incoming shipment)
+ # ----------------------------------------------------------------------
+
+ back_order_in.do_prepare_partial()
+ packD = self.StockPackObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', back_order_in.id)])
+ self.assertEqual(len(packD), 1, 'Wrong number of pack operation.')
+ packD.write({'product_qty': 4, 'lot_id': lot1_productD.id})
+ self.StockPackObj.create({
+ 'product_id': self.productD.id,
+ 'product_qty': 4,
+ 'product_uom_id': self.productD.uom_id.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location,
+ 'picking_id': back_order_in.id,
+ 'lot_id': lot2_productD.id})
+ self.StockPackObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', back_order_in.id)], limit=1).write({'product_qty': 1, 'lot_id': lot3_productC.id})
+ self.StockPackObj.create({
+ 'product_id': self.productC.id,
+ 'product_qty': 1,
+ 'product_uom_id': self.productC.uom_id.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location,
+ 'picking_id': back_order_in.id,
+ 'lot_id': lot4_productC.id})
+ self.StockPackObj.create({
+ 'product_id': self.productC.id,
+ 'product_qty': 2,
+ 'product_uom_id': self.productC.uom_id.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location,
+ 'picking_id': back_order_in.id,
+ 'lot_id': lot5_productC.id})
+ self.StockPackObj.create({
+ 'product_id': self.productC.id,
+ 'product_qty': 2,
+ 'product_uom_id': self.productC.uom_id.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location,
+ 'picking_id': back_order_in.id,
+ 'lot_id': lot6_productC.id})
+ self.StockPackObj.create({
+ 'product_id': self.productA.id,
+ 'product_qty': 10,
+ 'product_uom_id': self.productA.uom_id.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location,
+ 'picking_id': back_order_in.id})
+ back_order_in.do_transfer()
+
+ # ----------------------------------------------------------------------
+ # Check state, quantity and total moves (Back order of Incoming shipment).
+ # ----------------------------------------------------------------------
+
+ # Check total no of move lines.
+ self.assertEqual(len(back_order_in.move_lines), 6, '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'.
+ for move in back_order_in.move_lines:
+ self.assertEqual(move.state, 'done', 'Wrong state of move lines.')
+ # Check product A done quantity must be 10
+ movesA = self.MoveObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', back_order_in.id)])
+ self.assertEqual(movesA.product_uom_qty, 10, "Wrong move quantity of product A (%s found instead of 10)" % (movesA.product_uom_qty))
+ # Check product C done quantity must be 3.0, 1.0, 2.0
+ movesC = self.MoveObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', back_order_in.id)])
+ c_done_qty = [move.product_uom_qty for move in movesC]
+ self.assertEqual(set(c_done_qty), set([3.0, 1.0, 2.0]), 'Wrong quantity of moves product C.')
+ # 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.')
+ # Check no back order is created.
+ self.assertFalse(self.PickingObj.search([('backorder_id', '=', back_order_in.id)]), "Should not create any back order.")
+
+ #-----------------------------------------------------------------------
+ # Check stock location quant quantity and quantity available
+ # of product A, B, C, D
+ #-----------------------------------------------------------------------
+
+ # Check quants and available quantity for product A.
+ quants = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 12.0, 'Wrong total stock location quantity (%s found instead of 12)' % (sum(total_qty)))
+ self.assertEqual(self.productA.qty_available, 12.0, 'Wrong quantity available (%s found instead of 12)' % (self.productA.qty_available))
+ # Check quants and available quantity for product B.
+ quants = self.StockQuantObj.search([('product_id', '=', self.productB.id), ('location_id', '=', self.stock_location)])
+ self.assertFalse(quants, 'No quant should found as outgoing shipment took everything out of stock')
+ self.assertEqual(self.productB.qty_available, 0.0, 'Total quantity in stock should be 0 as the backorder took everything out of stock')
+ # Check quants and available quantity for product C.
+ quants = self.StockQuantObj.search([('product_id', '=', self.productC.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 8.0, 'Wrong total stock location quantity (%s found instead of 8)' % (sum(total_qty)))
+ self.assertEqual(self.productC.qty_available, 8.0, 'Wrong quantity available (%s found instead of 8)' % (self.productC.qty_available))
+ # Check quants and available quantity for product D.
+ quants = self.StockQuantObj.search([('product_id', '=', self.productD.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 9.0, 'Wrong total stock location quantity (%s found instead of 9)' % (sum(total_qty)))
+ self.assertEqual(self.productD.qty_available, 9.0, 'Wrong quantity available (%s found instead of 9)' % (self.productD.qty_available))
+ #-----------------------------------------------------------------------
+ # Back order of Outgoing shipment
+ # ----------------------------------------------------------------------
+
+ back_order_out.do_prepare_partial()
+ back_order_out.do_transfer()
+
+ # Check stock location quants and available quantity for product A.
+ quants = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertGreaterEqual(float_round(sum(total_qty), precision_rounding=0.0001), 1, 'Total stock location quantity for product A should not be nagative.')
+
+ def test_10_pickings_transfer_with_different_uom(self):
+ """ Picking transfer with diffrent unit of meassure. """
+
+ # ----------------------------------------------------------------------
+ # Create incoming shipment of products DozA, SDozA, SDozARound, kgB, gB
+ # ----------------------------------------------------------------------
+ # DozA ( 10 Dozen ) , SDozA ( 10.5 SuperDozen )
+ # SDozARound ( 10.5 10.5 SuperDozenRound ) , kgB ( 0.020 kg )
+ # gB ( 525.3 g )
+ # ----------------------------------------------------------------------
+
+ picking_in_A = self.PickingObj.create({
+ 'partner_id': self.partner_delta_id,
+ 'picking_type_id': self.picking_type_in})
+ self.MoveObj.create({
+ 'name': self.DozA.name,
+ 'product_id': self.DozA.id,
+ 'product_uom_qty': 10,
+ 'product_uom': self.DozA.uom_id.id,
+ 'picking_id': picking_in_A.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.SDozA.name,
+ 'product_id': self.SDozA.id,
+ 'product_uom_qty': 10.5,
+ 'product_uom': self.SDozA.uom_id.id,
+ 'picking_id': picking_in_A.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.SDozARound.name,
+ 'product_id': self.SDozARound.id,
+ 'product_uom_qty': 10.5,
+ 'product_uom': self.SDozARound.uom_id.id,
+ 'picking_id': picking_in_A.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.kgB.name,
+ 'product_id': self.kgB.id,
+ 'product_uom_qty': 0.020,
+ 'product_uom': self.kgB.uom_id.id,
+ 'picking_id': picking_in_A.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.gB.name,
+ 'product_id': self.gB.id,
+ 'product_uom_qty': 525.3,
+ 'product_uom': self.gB.uom_id.id,
+ 'picking_id': picking_in_A.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+
+ # Check incoming shipment move lines state.
+ for move in picking_in_A.move_lines:
+ self.assertEqual(move.state, 'draft', 'Move state must be draft.')
+ # Confirm incoming shipment.
+ picking_in_A.action_confirm()
+ # Check incoming shipment move lines state.
+ for move in picking_in_A.move_lines:
+ self.assertEqual(move.state, 'assigned', 'Move state must be draft.')
+ picking_in_A.do_prepare_partial()
+
+ # ----------------------------------------------------
+ # Check pack operation quantity of incoming shipments.
+ # ----------------------------------------------------
+ PackSdozAround = self.StockPackObj.search([('product_id', '=', self.SDozARound.id), ('picking_id', '=', picking_in_A.id)], limit=1)
+ self.assertEqual(PackSdozAround.product_qty, 11, 'Wrong quantity in pack operation (%s found instead of 11)' % (PackSdozAround.product_qty))
+ picking_in_A.do_transfer()
+ #-----------------------------------------------------------------------
+ # Check stock location quant quantity and quantity available
+ #-----------------------------------------------------------------------
+
+ # Check quants and available quantity for product DozA
+ quants = self.StockQuantObj.search([('product_id', '=', self.DozA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 10, 'Expecting 10 Dozen , got %.4f Dozen on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.DozA.qty_available, 10, 'Wrong quantity available (%s found instead of 10)' % (self.DozA.qty_available))
+ # Check quants and available quantity for product SDozA
+ quants = self.StockQuantObj.search([('product_id', '=', self.SDozA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 10.5, 'Expecting 10.5 SDozen , got %.4f SDozen on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.SDozA.qty_available, 10.5, 'Wrong quantity available (%s found instead of 10.5)' % (self.SDozA.qty_available))
+ # Check quants and available quantity for product SDozARound
+ quants = self.StockQuantObj.search([('product_id', '=', self.SDozARound.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 11, 'Expecting 11 SDozenRound , got %.4f SDozenRound on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.SDozARound.qty_available, 11, 'Wrong quantity available (%s found instead of 11)' % (self.SDozARound.qty_available))
+ # Check quants and available quantity for product gB
+ quants = self.StockQuantObj.search([('product_id', '=', self.gB.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 525.3, 'Expecting 525.3 gram , got %.4f gram on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.gB.qty_available, 525.3, 'Wrong quantity available (%s found instead of 525.3' % (self.gB.qty_available))
+ # Check quants and available quantity for product kgB
+ quants = self.StockQuantObj.search([('product_id', '=', self.kgB.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 0.020, 'Expecting 0.020 kg , got %.4f kg on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.kgB.qty_available, 0.020, 'Wrong quantity available (%s found instead of 0.020)' % (self.kgB.qty_available))
+
+ # ----------------------------------------------------------------------
+ # Create Incoming Shipment B
+ # ----------------------------------------------------------------------
+
+ picking_in_B = self.PickingObj.create({
+ 'partner_id': self.partner_delta_id,
+ 'picking_type_id': self.picking_type_in})
+ self.MoveObj.create({
+ 'name': self.DozA.name,
+ 'product_id': self.DozA.id,
+ 'product_uom_qty': 120,
+ 'product_uom': self.uom_unit.id,
+ 'picking_id': picking_in_B.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.SDozA.name,
+ 'product_id': self.SDozA.id,
+ 'product_uom_qty': 1512,
+ 'product_uom': self.uom_unit.id,
+ 'picking_id': picking_in_B.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.SDozARound.name,
+ 'product_id': self.SDozARound.id,
+ 'product_uom_qty': 1584,
+ 'product_uom': self.uom_unit.id,
+ 'picking_id': picking_in_B.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.kgB.name,
+ 'product_id': self.kgB.id,
+ 'product_uom_qty': 20.0,
+ 'product_uom': self.uom_gm.id,
+ 'picking_id': picking_in_B.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ self.MoveObj.create({
+ 'name': self.gB.name,
+ 'product_id': self.gB.id,
+ 'product_uom_qty': 0.525,
+ 'product_uom': self.uom_kg.id,
+ 'picking_id': picking_in_B.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+
+ # Check incoming shipment move lines state.
+ for move in picking_in_B.move_lines:
+ self.assertEqual(move.state, 'draft', 'Wrong state of move line.')
+ # Confirm incoming shipment.
+ picking_in_B.action_confirm()
+ # Check incoming shipment move lines state.
+ for move in picking_in_B.move_lines:
+ self.assertEqual(move.state, 'assigned', 'Wrong state of move line.')
+ picking_in_B.do_prepare_partial()
+
+ # ----------------------------------------------------------------------
+ # Check product quantity and unit of measure of pack operaation.
+ # ----------------------------------------------------------------------
+
+ # Check pack operation quantity and unit of measure for product DozA.
+ PackdozA = self.StockPackObj.search([('product_id', '=', self.DozA.id), ('picking_id', '=', picking_in_B.id)], limit=1)
+ self.assertEqual(PackdozA.product_qty, 120, 'Wrong quantity in pack operation (%s found instead of 120)' % (PackdozA.product_qty))
+ self.assertEqual(PackdozA.product_uom_id.id, self.uom_unit.id, 'Wrong uom in pack operation for product DozA.')
+ # Check pack operation quantity and unit of measure for product SDozA.
+ PackSdozA = self.StockPackObj.search([('product_id', '=', self.SDozA.id), ('picking_id', '=', picking_in_B.id)], limit=1)
+ self.assertEqual(PackSdozA.product_qty, 1512, 'Wrong quantity in pack operation (%s found instead of 1512)' % (PackSdozA.product_qty))
+ self.assertEqual(PackSdozA.product_uom_id.id, self.uom_unit.id, 'Wrong uom in pack operation for product SDozA.')
+ # Check pack operation quantity and unit of measure for product SDozARound.
+ PackSdozAround = self.StockPackObj.search([('product_id', '=', self.SDozARound.id), ('picking_id', '=', picking_in_B.id)], limit=1)
+ self.assertEqual(PackSdozAround.product_qty, 1584, 'Wrong quantity in pack operation (%s found instead of 1584)' % (PackSdozAround.product_qty))
+ self.assertEqual(PackSdozAround.product_uom_id.id, self.uom_unit.id, 'Wrong uom in pack operation for product SDozARound.')
+ # Check pack operation quantity and unit of measure for product gB.
+ packgB = self.StockPackObj.search([('product_id', '=', self.gB.id), ('picking_id', '=', picking_in_B.id)], limit=1)
+ self.assertEqual(packgB.product_qty, 525, 'Wrong quantity in pack operation (%s found instead of 525)' % (packgB.product_qty))
+ self.assertEqual(packgB.product_uom_id.id, self.uom_gm.id, 'Wrong uom in pack operation for product gB.')
+ # Check pack operation quantity and unit of measure for product kgB.
+ packkgB = self.StockPackObj.search([('product_id', '=', self.kgB.id), ('picking_id', '=', picking_in_B.id)], limit=1)
+ self.assertEqual(packkgB.product_qty, 20.0, 'Wrong quantity in pack operation (%s found instead of 20)' % (packkgB.product_qty))
+ self.assertEqual(packkgB.product_uom_id.id, self.uom_gm.id, 'Wrong uom in pack operation for product kgB')
+
+ # ----------------------------------------------------------------------
+ # Replace pack operation of incoming shipment.
+ # ----------------------------------------------------------------------
+
+ self.StockPackObj.search([('product_id', '=', self.kgB.id), ('picking_id', '=', picking_in_B.id)]).write({
+ 'product_qty': 0.020, 'product_uom_id': self.uom_kg.id})
+ self.StockPackObj.search([('product_id', '=', self.gB.id), ('picking_id', '=', picking_in_B.id)]).write({
+ 'product_qty': 525.3, 'product_uom_id': self.uom_gm.id})
+ self.StockPackObj.search([('product_id', '=', self.DozA.id), ('picking_id', '=', picking_in_B.id)]).write({
+ 'product_qty': 4, 'product_uom_id': self.uom_dozen.id})
+ self.StockPackObj.create({
+ 'product_id': self.DozA.id,
+ 'product_qty': 48,
+ 'product_uom_id': self.uom_unit.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location,
+ 'picking_id': picking_in_B.id})
+
+ # Transfer product.
+ # -----------------
+ picking_in_B.do_transfer()
+
+ #-----------------------------------------------------------------------
+ # Check incoming shipment
+ #-----------------------------------------------------------------------
+
+ # Check incoming shipment state.
+ self.assertEqual(picking_in_B.state, 'done', 'Incoming shipment state should be done.')
+ # Check incoming shipment move lines state.
+ 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')
+ # 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))
+ self.assertEqual(moves_DozA.product_uom.id, self.uom_unit.id, 'Wrong uom in move for product DozA.')
+ # Check product SDozA done quantity.
+ moves_SDozA = self.MoveObj.search([('product_id', '=', self.SDozA.id), ('picking_id', '=', picking_in_B.id)], limit=1)
+ self.assertEqual(moves_SDozA.product_uom_qty, 1512, 'Wrong move quantity (%s found instead of 1512)' % (moves_SDozA.product_uom_qty))
+ self.assertEqual(moves_SDozA.product_uom.id, self.uom_unit.id, 'Wrong uom in move for product SDozA.')
+ # Check product SDozARound done quantity.
+ moves_SDozARound = self.MoveObj.search([('product_id', '=', self.SDozARound.id), ('picking_id', '=', picking_in_B.id)], limit=1)
+ self.assertEqual(moves_SDozARound.product_uom_qty, 1584, 'Wrong move quantity (%s found instead of 1584)' % (moves_SDozARound.product_uom_qty))
+ self.assertEqual(moves_SDozARound.product_uom.id, self.uom_unit.id, 'Wrong uom in move for product SDozARound.')
+ # Check product kgB done quantity.
+ moves_kgB = self.MoveObj.search([('product_id', '=', self.kgB.id), ('picking_id', '=', picking_in_B.id)], limit=1)
+ self.assertEqual(moves_kgB.product_uom_qty, 20, 'Wrong quantity in move (%s found instead of 20)' % (moves_kgB.product_uom_qty))
+ 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.id, self.uom_kg.id, 'Wrong uom in move for product gB.')
+ moves_gB_g = self.MoveObj.search([('product_id', '=', self.gB.id), ('picking_id', '=', picking_in_B.id), ('product_uom', '=', self.uom_gm.id)], limit=1)
+ self.assertEqual(moves_gB_g.product_uom_qty, 0.3, 'Wrong move quantity (%s found instead of 0.3)' % (moves_gB_g.product_uom_qty))
+ self.assertEqual(moves_gB_g.product_uom.id, self.uom_gm.id, 'Wrong uom in move for product gB.')
+
+ # ----------------------------------------------------------------------
+ # Check Back order of Incoming shipment.
+ # ----------------------------------------------------------------------
+
+ # Check back order created or not.
+ bo_in_B = self.PickingObj.search([('backorder_id', '=', picking_in_B.id)])
+ self.assertEqual(len(bo_in_B), 1, 'Back order should be created.')
+ # Check total move lines of back order.
+ self.assertEqual(len(bo_in_B.move_lines), 1, 'Wrong number of move lines')
+ # Check back order created with correct quantity and uom or not.
+ moves_DozA = self.MoveObj.search([('product_id', '=', self.DozA.id), ('picking_id', '=', bo_in_B.id)], limit=1)
+ self.assertEqual(moves_DozA.product_uom_qty, 24.0, 'Wrong move quantity (%s found instead of 0.525)' % (moves_DozA.product_uom_qty))
+ self.assertEqual(moves_DozA.product_uom.id, self.uom_unit.id, 'Wrong uom in move for product DozA.')
+
+ # ----------------------------------------------------------------------
+ # Check product stock location quantity and quantity available.
+ # ----------------------------------------------------------------------
+
+ # Check quants and available quantity for product DozA
+ quants = self.StockQuantObj.search([('product_id', '=', self.DozA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 18, 'Expecting 18 Dozen , got %.4f Dozen on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.DozA.qty_available, 18, 'Wrong quantity available (%s found instead of 18)' % (self.DozA.qty_available))
+ # Check quants and available quantity for product SDozA
+ quants = self.StockQuantObj.search([('product_id', '=', self.SDozA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 21, 'Expecting 18 SDozen , got %.4f SDozen on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.SDozA.qty_available, 21, 'Wrong quantity available (%s found instead of 21)' % (self.SDozA.qty_available))
+ # Check quants and available quantity for product SDozARound
+ quants = self.StockQuantObj.search([('product_id', '=', self.SDozARound.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 22, 'Expecting 22 SDozenRound , got %.4f SDozenRound on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.SDozARound.qty_available, 22, 'Wrong quantity available (%s found instead of 22)' % (self.SDozARound.qty_available))
+ # Check quants and available quantity for product gB.
+ quants = self.StockQuantObj.search([('product_id', '=', self.gB.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 1050.6, 'Expecting 1050.6 Gram , got %.4f Gram on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.gB.qty_available, 1050.6, 'Wrong quantity available (%s found instead of 1050.6)' % (self.gB.qty_available))
+ # Check quants and available quantity for product kgB.
+ quants = self.StockQuantObj.search([('product_id', '=', self.kgB.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 0.040, 'Expecting 0.040 kg , got %.4f kg on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.kgB.qty_available, 0.040, 'Wrong quantity available (%s found instead of 0.040)' % (self.kgB.qty_available))
+
+ # ----------------------------------------------------------------------
+ # Create outgoing shipment.
+ # ----------------------------------------------------------------------
+
+ before_out_quantity = self.kgB.qty_available
+ picking_out = self.PickingObj.create({
+ 'partner_id': self.partner_agrolite_id,
+ 'picking_type_id': self.picking_type_out})
+ self.MoveObj.create({
+ 'name': self.kgB.name,
+ 'product_id': self.kgB.id,
+ 'product_uom_qty': 0.966,
+ 'product_uom': self.uom_gm.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ self.MoveObj.create({
+ 'name': self.kgB.name,
+ 'product_id': self.kgB.id,
+ 'product_uom_qty': 0.034,
+ 'product_uom': self.uom_gm.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ picking_out.action_confirm()
+ picking_out.action_assign()
+ picking_out.do_prepare_partial()
+ picking_out.do_transfer()
+
+ # Check quantity difference after stock transfer.
+ quantity_diff = before_out_quantity - self.kgB.qty_available
+ self.assertEqual(float_round(quantity_diff, precision_rounding=0.0001), 0.001, 'Wrong quantity diffrence.')
+ self.assertEqual(self.kgB.qty_available, 0.039, 'Wrong quantity available (%s found instead of 0.039)' % (self.kgB.qty_available))
+
+ # ======================================================================
+ # Outgoing shipments.
+ # ======================================================================
+ # Create Outgoing shipment with ...
+ # product DozA ( 54 Unit ) , SDozA ( 288 Unit )
+ # product SDozRound ( 360 unit ) , product gB ( 0.503 kg )
+ # product kgB ( 19 g )
+ # ======================================================================
+
+ picking_out = self.PickingObj.create({
+ 'partner_id': self.partner_agrolite_id,
+ 'picking_type_id': self.picking_type_out})
+ self.MoveObj.create({
+ 'name': self.DozA.name,
+ 'product_id': self.DozA.id,
+ 'product_uom_qty': 54,
+ 'product_uom': self.uom_unit.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ self.MoveObj.create({
+ 'name': self.SDozA.name,
+ 'product_id': self.SDozA.id,
+ 'product_uom_qty': 288,
+ 'product_uom': self.uom_unit.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ self.MoveObj.create({
+ 'name': self.SDozARound.name,
+ 'product_id': self.SDozARound.id,
+ 'product_uom_qty': 360,
+ 'product_uom': self.uom_unit.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ self.MoveObj.create({
+ 'name': self.gB.name,
+ 'product_id': self.gB.id,
+ 'product_uom_qty': 0.503,
+ 'product_uom': self.uom_kg.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ self.MoveObj.create({
+ 'name': self.kgB.name,
+ 'product_id': self.kgB.id,
+ 'product_uom_qty': 20,
+ 'product_uom': self.uom_gm.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ # Confirm outgoing shipment.
+ picking_out.action_confirm()
+ for move in picking_out.move_lines:
+ self.assertEqual(move.state, 'confirmed', 'Wrong state of move line.')
+ # Assing product to outgoing shipments
+ picking_out.action_assign()
+ for move in picking_out.move_lines:
+ self.assertEqual(move.state, 'assigned', 'Wrong state of move line.')
+ # Check product A available quantity
+ DozA_qty = self.MoveObj.search([('product_id', '=', self.DozA.id), ('picking_id', '=', picking_out.id)], limit=1).availability
+ self.assertEqual(DozA_qty, 4.5, 'Wrong move quantity availability (%s found instead of 4.5)' % (DozA_qty))
+ # Check product B available quantity
+ SDozA_qty = self.MoveObj.search([('product_id', '=', self.SDozA.id), ('picking_id', '=', picking_out.id)], limit=1).availability
+ self.assertEqual(SDozA_qty, 2, 'Wrong move quantity availability (%s found instead of 2)' % (SDozA_qty))
+ # Check product C available quantity
+ SDozARound_qty = self.MoveObj.search([('product_id', '=', self.SDozARound.id), ('picking_id', '=', picking_out.id)], limit=1).availability
+ self.assertEqual(SDozARound_qty, 3, 'Wrong move quantity availability (%s found instead of 3)' % (SDozARound_qty))
+ # Check product D available quantity
+ gB_qty = self.MoveObj.search([('product_id', '=', self.gB.id), ('picking_id', '=', picking_out.id)], limit=1).availability
+ self.assertEqual(gB_qty, 503, 'Wrong move quantity availability (%s found instead of 503)' % (gB_qty))
+ # Check product D available quantity
+ kgB_qty = self.MoveObj.search([('product_id', '=', self.kgB.id), ('picking_id', '=', picking_out.id)], limit=1).availability
+ self.assertEqual(kgB_qty, 0.020, 'Wrong move quantity availability (%s found instead of 0.020)' % (kgB_qty))
+
+ picking_out.action_confirm()
+ picking_out.action_assign()
+ picking_out.do_prepare_partial()
+ picking_out.do_transfer()
+
+ # ----------------------------------------------------------------------
+ # Check product stock location quantity and quantity available.
+ # ----------------------------------------------------------------------
+
+ # Check quants and available quantity for product DozA
+ quants = self.StockQuantObj.search([('product_id', '=', self.DozA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 13.5, 'Expecting 13.5 Dozen , got %.4f Dozen on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.DozA.qty_available, 13.5, 'Wrong quantity available (%s found instead of 13.5)' % (self.DozA.qty_available))
+ # Check quants and available quantity for product SDozA
+ quants = self.StockQuantObj.search([('product_id', '=', self.SDozA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 19, 'Expecting 19 SDozen , got %.4f SDozen on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.SDozA.qty_available, 19, 'Wrong quantity available (%s found instead of 19)' % (self.SDozA.qty_available))
+ # Check quants and available quantity for product SDozARound
+ quants = self.StockQuantObj.search([('product_id', '=', self.SDozARound.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 19, 'Expecting 19 SDozRound , got %.4f SDozRound on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.SDozARound.qty_available, 19, 'Wrong quantity available (%s found instead of 19)' % (self.SDozARound.qty_available))
+ # Check quants and available quantity for product gB.
+ quants = self.StockQuantObj.search([('product_id', '=', self.gB.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(float_round(sum(total_qty), precision_rounding=0.0001), 547.6, 'Expecting 547.6 g , got %.4f g on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.gB.qty_available, 547.6, 'Wrong quantity available (%s found instead of 547.6)' % (self.gB.qty_available))
+ # Check quants and available quantity for product kgB.
+ quants = self.StockQuantObj.search([('product_id', '=', self.kgB.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 0.019, 'Expecting 0.019 kg , got %.4f kg on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.kgB.qty_available, 0.019, 'Wrong quantity available (%s found instead of 0.019)' % (self.kgB.qty_available))
+ # ----------------------------------------------------------------------
+ # Receipt back order of incoming shipment.
+ # ----------------------------------------------------------------------
+
+ bo_in_B.do_prepare_partial()
+ bo_in_B.do_transfer()
+ # Check quants and available quantity for product kgB.
+ quants = self.StockQuantObj.search([('product_id', '=', self.DozA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 15.5, 'Expecting 15.5 Dozen , got %.4f Dozen on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.DozA.qty_available, 15.5, 'Wrong quantity available (%s found instead of 15.5)' % (self.DozA.qty_available))
+
+ # -----------------------------------------
+ # Create product in kg and receive in ton.
+ # -----------------------------------------
+
+ productKG = self.ProductObj.create({'name': 'Product KG', 'uom_id': self.uom_kg.id, 'uom_po_id': self.uom_kg.id})
+ picking_in = self.PickingObj.create({
+ 'partner_id': self.partner_delta_id,
+ 'picking_type_id': self.picking_type_in})
+ self.MoveObj.create({
+ 'name': productKG.name,
+ 'product_id': productKG.id,
+ 'product_uom_qty': 1.0,
+ 'product_uom': self.uom_tone.id,
+ 'picking_id': picking_in.id,
+ 'location_id': self.supplier_location,
+ 'location_dest_id': self.stock_location})
+ # Check incoming shipment state.
+ self.assertEqual(picking_in.state, 'draft', 'Incoming shipment state should be draft.')
+ # Check incoming shipment move lines state.
+ for move in picking_in.move_lines:
+ self.assertEqual(move.state, 'draft', 'Wrong state of move line.')
+ # Confirm incoming shipment.
+ picking_in.action_confirm()
+ # Check incoming shipment move lines state.
+ for move in picking_in.move_lines:
+ self.assertEqual(move.state, 'assigned', 'Wrong state of move line.')
+ picking_in.do_prepare_partial()
+ # Check pack operation quantity.
+ packKG = self.StockPackObj.search([('product_id', '=', productKG.id), ('picking_id', '=', picking_in.id)], limit=1)
+ self.assertEqual(packKG.product_qty, 1000, 'Wrong product quantity in pack operation (%s found instead of 1000)' % (packKG.product_qty))
+ self.assertEqual(packKG.product_uom_id.id, self.uom_kg.id, 'Wrong product uom in pack operation.')
+ # Transfer Incoming shipment.
+ picking_in.do_transfer()
+
+ #-----------------------------------------------------------------------
+ # Check incoming shipment after transfer.
+ #-----------------------------------------------------------------------
+
+ # Check incoming shipment state.
+ self.assertEqual(picking_in.state, 'done', 'Incoming shipment state should be done.')
+ # Check incoming shipment move lines state.
+ for move in picking_in.move_lines:
+ self.assertEqual(move.state, 'done', 'Wrong state of move lines.')
+ # Check total done move lines for incoming shipment.
+ self.assertEqual(len(picking_in.move_lines), 1, 'Wrong number of move lines')
+ # Check product DozA done quantity.
+ move = self.MoveObj.search([('product_id', '=', productKG.id), ('picking_id', '=', picking_in.id)], limit=1)
+ self.assertEqual(move.product_uom_qty, 1, 'Wrong product quantity in done move.')
+ self.assertEqual(move.product_uom.id, self.uom_tone.id, 'Wrong unit of measure in done move.')
+ self.assertEqual(productKG.qty_available, 1000, 'Wrong quantity available of product (%s found instead of 1000)' % (productKG.qty_available))
+ picking_out = self.PickingObj.create({
+ 'partner_id': self.partner_agrolite_id,
+ 'picking_type_id': self.picking_type_out})
+ self.MoveObj.create({
+ 'name': productKG.name,
+ 'product_id': productKG.id,
+ 'product_uom_qty': 2.5,
+ 'product_uom': self.uom_gm.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ picking_out.action_confirm()
+ picking_out.action_assign()
+ picking_out.do_prepare_partial()
+ pack_opt = self.StockPackObj.search([('product_id', '=', productKG.id), ('picking_id', '=', picking_out.id)], limit=1)
+ pack_opt.write({'product_qty': 0.5})
+ picking_out.do_transfer()
+ quants = self.StockQuantObj.search([('product_id', '=', productKG.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ # Check total quantity stock location.
+ self.assertEqual(sum(total_qty), 999.9995, 'Expecting 999.9995 kg , got %.4f kg on location stock!' % (sum(total_qty)))
+
+ # Check Back order created or not.
+ #---------------------------------
+ bo_out_1 = self.PickingObj.search([('backorder_id', '=', picking_out.id)])
+ self.assertEqual(len(bo_out_1), 1, 'Back order should be created.')
+ # Check total move lines of back order.
+ self.assertEqual(len(bo_out_1.move_lines), 1, 'Wrong number of move lines')
+ moves_KG = self.MoveObj.search([('product_id', '=', productKG.id), ('picking_id', '=', bo_out_1.id)], limit=1)
+ # Check back order created with correct quantity and uom or not.
+ self.assertEqual(moves_KG.product_uom_qty, 2.0, 'Wrong move quantity (%s found instead of 2.0)' % (moves_KG.product_uom_qty))
+ self.assertEqual(moves_KG.product_uom.id, self.uom_gm.id, 'Wrong uom in move for product KG.')
+ bo_out_1.action_assign()
+ bo_out_1.do_prepare_partial()
+ pack_opt = self.StockPackObj.search([('product_id', '=', productKG.id), ('picking_id', '=', bo_out_1.id)], limit=1)
+ pack_opt.write({'product_qty': 0.5})
+ bo_out_1.do_transfer()
+ quants = self.StockQuantObj.search([('product_id', '=', productKG.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+
+ # Check total quantity stock location.
+ self.assertEqual(sum(total_qty), 999.9990, 'Expecting 999.9990 kg , got %.4f kg on location stock!' % (sum(total_qty)))
+
+ # Check Back order created or not.
+ #---------------------------------
+ bo_out_2 = self.PickingObj.search([('backorder_id', '=', bo_out_1.id)])
+ self.assertEqual(len(bo_out_2), 1, 'Back order should be created.')
+ # Check total move lines of back order.
+ self.assertEqual(len(bo_out_2.move_lines), 1, 'Wrong number of move lines')
+ # Check back order created with correct move quantity and uom or not.
+ moves_KG = self.MoveObj.search([('product_id', '=', productKG.id), ('picking_id', '=', bo_out_2.id)], limit=1)
+ self.assertEqual(moves_KG.product_uom_qty, 1.5, 'Wrong move quantity (%s found instead of 1.5)' % (moves_KG.product_uom_qty))
+ self.assertEqual(moves_KG.product_uom.id, self.uom_gm.id, 'Wrong uom in move for product KG.')
+ bo_out_2.action_assign()
+ bo_out_2.do_prepare_partial()
+ pack_opt = self.StockPackObj.search([('product_id', '=', productKG.id), ('picking_id', '=', bo_out_2.id)], limit=1)
+ pack_opt.write({'product_qty': 0.5})
+ bo_out_2.do_transfer()
+ # Check total quantity stock location of product KG.
+ quants = self.StockQuantObj.search([('product_id', '=', productKG.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 999.9985, 'Expecting 999.9985 kg , got %.4f kg on location stock!' % (sum(total_qty)))
+
+ # Check Back order created or not.
+ #---------------------------------
+ bo_out_3 = self.PickingObj.search([('backorder_id', '=', bo_out_2.id)])
+ self.assertEqual(len(bo_out_3), 1, 'Back order should be created.')
+ # Check total move lines of back order.
+ self.assertEqual(len(bo_out_3.move_lines), 1, 'Wrong number of move lines')
+ # Check back order created with correct quantity and uom or not.
+ moves_KG = self.MoveObj.search([('product_id', '=', productKG.id), ('picking_id', '=', bo_out_3.id)], limit=1)
+ self.assertEqual(moves_KG.product_uom_qty, 1, 'Wrong move quantity (%s found instead of 1.0)' % (moves_KG.product_uom_qty))
+ self.assertEqual(moves_KG.product_uom.id, self.uom_gm.id, 'Wrong uom in move for product KG.')
+ bo_out_3.action_assign()
+ bo_out_3.do_prepare_partial()
+ pack_opt = self.StockPackObj.search([('product_id', '=', productKG.id), ('picking_id', '=', bo_out_3.id)], limit=1)
+ pack_opt.write({'product_qty': 0.5})
+ bo_out_3.do_transfer()
+ quants = self.StockQuantObj.search([('product_id', '=', productKG.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 999.9980, 'Expecting 999.9980 kg , got %.4f kg on location stock!' % (sum(total_qty)))
+
+ # Check Back order created or not.
+ #---------------------------------
+ bo_out_4 = self.PickingObj.search([('backorder_id', '=', bo_out_3.id)])
+
+ self.assertEqual(len(bo_out_4), 1, 'Back order should be created.')
+ # Check total move lines of back order.
+ self.assertEqual(len(bo_out_4.move_lines), 1, 'Wrong number of move lines')
+ # Check back order created with correct quantity and uom or not.
+ moves_KG = self.MoveObj.search([('product_id', '=', productKG.id), ('picking_id', '=', bo_out_4.id)], limit=1)
+ self.assertEqual(moves_KG.product_uom_qty, 0.5, 'Wrong move quantity (%s found instead of 0.5)' % (moves_KG.product_uom_qty))
+ self.assertEqual(moves_KG.product_uom.id, self.uom_gm.id, 'Wrong uom in move for product KG.')
+ bo_out_4.action_assign()
+ bo_out_4.do_prepare_partial()
+ pack_opt = self.StockPackObj.search([('product_id', '=', productKG.id), ('picking_id', '=', bo_out_4.id)], limit=1)
+ pack_opt.write({'product_qty': 0.5})
+ bo_out_4.do_transfer()
+ quants = self.StockQuantObj.search([('product_id', '=', productKG.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 999.9975, 'Expecting 999.9975 kg , got %.4f kg on location stock!' % (sum(total_qty)))
+
+ def test_20_create_inventory_with_different_uom(self):
+ """Create inventory with different unit of measure."""
+
+ # ------------------------------------------------
+ # Test inventory with product A(Unit).
+ # ------------------------------------------------
+
+ inventory = self.InvObj.create({'name': 'Test',
+ 'product_id': self.UnitA.id,
+ 'filter': 'product'})
+ inventory.prepare_inventory()
+ self.assertFalse(inventory.line_ids, "Inventory line should not created.")
+ inventory_line = self.InvLineObj.create({
+ 'inventory_id': inventory.id,
+ 'product_id': self.UnitA.id,
+ 'product_uom_id': self.uom_dozen.id,
+ 'product_qty': 10,
+ 'location_id': self.stock_location})
+ inventory.action_done()
+ # Check quantity available of product UnitA.
+ quants = self.StockQuantObj.search([('product_id', '=', self.UnitA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 120, 'Expecting 120 Units , got %.4f Units on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.UnitA.qty_available, 120, 'Expecting 120 Units , got %.4f Units of quantity available!' % (self.UnitA.qty_available))
+ # Create Inventory again for product UnitA.
+ inventory = self.InvObj.create({'name': 'Test',
+ 'product_id': self.UnitA.id,
+ 'filter': 'product'})
+ inventory.prepare_inventory()
+ self.assertEqual(len(inventory.line_ids), 1, "One inventory line should be created.")
+ inventory_line = self.InvLineObj.search([('product_id', '=', self.UnitA.id), ('inventory_id', '=', inventory.id)], limit=1)
+ self.assertEqual(inventory_line.product_qty, 120, "Wrong product quantity in inventory line.")
+ # Modify the inventory line and set the quantity to 144 product on this new inventory.
+ inventory_line.write({'product_qty': 144})
+ inventory.action_done()
+ move = self.MoveObj.search([('product_id', '=', self.UnitA.id), ('inventory_id', '=', inventory.id)], limit=1)
+ self.assertEqual(move.product_uom_qty, 24, "Wrong move quantity of product UnitA.")
+ # Check quantity available of product UnitA.
+ quants = self.StockQuantObj.search([('product_id', '=', self.UnitA.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 144, 'Expecting 144 Units , got %.4f Units on location stock!' % (sum(total_qty)))
+ self.assertEqual(self.UnitA.qty_available, 144, 'Expecting 144 Units , got %.4f Units of quantity available!' % (self.UnitA.qty_available))
+
+ # ------------------------------------------------
+ # Test inventory with product KG.
+ # ------------------------------------------------
+
+ productKG = self.ProductObj.create({'name': 'Product KG', 'uom_id': self.uom_kg.id, 'uom_po_id': self.uom_kg.id})
+ inventory = self.InvObj.create({'name': 'Inventory Product KG',
+ 'product_id': productKG.id,
+ 'filter': 'product'})
+ inventory.prepare_inventory()
+ self.assertFalse(inventory.line_ids, "Inventory line should not created.")
+ inventory_line = self.InvLineObj.create({
+ 'inventory_id': inventory.id,
+ 'product_id': productKG.id,
+ 'product_uom_id': self.uom_tone.id,
+ 'product_qty': 5,
+ 'location_id': self.stock_location})
+ inventory.action_done()
+ quants = self.StockQuantObj.search([('product_id', '=', productKG.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 5000, 'Expecting 5000 kg , got %.4f kg on location stock!' % (sum(total_qty)))
+ self.assertEqual(productKG.qty_available, 5000, 'Expecting 5000 kg , got %.4f kg of quantity available!' % (productKG.qty_available))
+ # Create Inventory again.
+ inventory = self.InvObj.create({'name': 'Test',
+ 'product_id': productKG.id,
+ 'filter': 'product'})
+ inventory.prepare_inventory()
+ self.assertEqual(len(inventory.line_ids), 1, "One inventory line should be created.")
+ inventory_line = self.InvLineObj.search([('product_id', '=', productKG.id), ('inventory_id', '=', inventory.id)], limit=1)
+ self.assertEqual(inventory_line.product_qty, 5000, "Wrong product quantity in inventory line.")
+ # Modify the inventory line and set the quantity to 4000 product on this new inventory.
+ inventory_line.write({'product_qty': 4000})
+ inventory.action_done()
+ # Check inventory move quantity of product KG.
+ move = self.MoveObj.search([('product_id', '=', productKG.id), ('inventory_id', '=', inventory.id)], limit=1)
+ self.assertEqual(move.product_uom_qty, 1000, "Wrong move quantity of product KG.")
+ # Check quantity available of product KG.
+ quants = self.StockQuantObj.search([('product_id', '=', productKG.id), ('location_id', '=', self.stock_location)])
+ total_qty = [quant.qty for quant in quants]
+ self.assertEqual(sum(total_qty), 4000, 'Expecting 4000 kg , got %.4f on location stock!' % (sum(total_qty)))
+ self.assertEqual(productKG.qty_available, 4000, 'Expecting 4000 kg , got %.4f of quantity available!' % (productKG.qty_available))
diff --git a/addons/stock/views/report_stockpicking.xml b/addons/stock/views/report_stockpicking.xml
index 97131958ded..ba41f2b2210 100644
--- a/addons/stock/views/report_stockpicking.xml
+++ b/addons/stock/views/report_stockpicking.xml
@@ -9,26 +9,41 @@
<div class="row"><div class="col-xs-4 pull-right">
<img t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('Code128', o.name, 600, 100)" style="width:300px;height:50px;"/>
</div></div>
- <div t-if="o.picking_type_id.code=='incoming' and o.partner_id">
- <span><strong>Supplier Address:</strong></span>
- </div>
- <div t-if="o.picking_type_id.code=='internal' and o.partner_id">
- <span><strong>Warehouse Address:</strong></span>
- </div>
- <div t-if="o.picking_type_id.code=='outgoing' and o.partner_id">
- <span><strong>Customer Address:</strong></span>
- </div>
- <div t-if="o.partner_id" name="partner_header">
- <div t-field="o.partner_id"
- t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/>
- <p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
- </div>
-
- <h2>
- <span t-field="o.picking_type_id"/>:
- <span t-field="o.name"/>
- </h2>
- <table class="table table-condensed">
+ <div class="row">
+ <div class="col-xs-6">
+ <div t-if="o.picking_type_id.code=='incoming' and o.partner_id">
+ <span><strong>Supplier Address:</strong></span>
+ </div>
+ <div t-if="o.picking_type_id.code=='internal' and o.partner_id">
+ <span><strong>Warehouse Address:</strong></span>
+ </div>
+ <div t-if="o.picking_type_id.code=='outgoing' and o.partner_id">
+ <span><strong>Customer Address:</strong></span>
+ </div>
+ <div t-if="o.partner_id" name="partner_header">
+ <div t-field="o.partner_id"
+ t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/>
+ <p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
+ </div>
+ </div>
+ <div class="col-xs-5 col-xs-offset-1">
+ <div t-if="o.move_lines[0].partner_id and o.move_lines[0].partner_id.id != o.partner_id.id">
+ <span><strong>Delivery Address:</strong></span>
+ <div t-field="o.move_lines[0].partner_id"
+ t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/>
+ </div>
+ <div t-if="o.picking_type_id.code != 'internal' and (not o.move_lines[0].partner_id) and o.picking_type_id.warehouse_id.partner_id">
+ <span><strong>Warehouse Address:</strong></span>
+ <div t-field="o.picking_type_id.warehouse_id.partner_id"
+ t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/>
+ </div>
+ </div>
+ </div>
+ <h2>
+ <span t-field="o.picking_type_id"/>:
+ <span t-field="o.name"/>
+ </h2>
+ <table class="table table-condensed">
<thead>
<tr>
<th><strong>Order (Origin)</strong></th>
diff --git a/addons/stock_account/__openerp__.py b/addons/stock_account/__openerp__.py
index 9774b75427b..5db699845da 100644
--- a/addons/stock_account/__openerp__.py
+++ b/addons/stock_account/__openerp__.py
@@ -39,7 +39,6 @@ Dashboard / Reports for Warehouse Management includes:
* Stock Inventory Value at given date (support dates in the past)
""",
'website': 'https://www.odoo.com/page/warehouse',
- 'images': [],
'depends': ['stock', 'account'],
'category': 'Hidden',
'sequence': 16,
diff --git a/addons/stock_account/stock.py b/addons/stock_account/stock.py
index 587f135d259..b965f6689d7 100644
--- a/addons/stock_account/stock.py
+++ b/addons/stock_account/stock.py
@@ -292,7 +292,9 @@ class stock_picking(osv.osv):
invoices[key] = invoice_id
else:
invoice = invoice_obj.browse(cr, uid, invoices[key], context=context)
- invoice.write({'origin': '%s, %s' % (invoice.origin, invoice_vals['origin'],)})
+ if not invoice.origin or invoice_vals['origin'] not in invoice.origin.split(', '):
+ origin = filter(None, [invoice.origin, invoice_vals['origin']])
+ invoice.write({'origin': ', '.join(origin)})
invoice_line_vals = move_obj._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context)
invoice_line_vals['invoice_id'] = invoices[key]
diff --git a/addons/stock_account/wizard/stock_valuation_history.py b/addons/stock_account/wizard/stock_valuation_history.py
index 06ef0aeefc9..193b19cbc7b 100644
--- a/addons/stock_account/wizard/stock_valuation_history.py
+++ b/addons/stock_account/wizard/stock_valuation_history.py
@@ -49,20 +49,19 @@ class stock_history(osv.osv):
prod_dict = {}
if 'inventory_value' in fields:
for line in res:
- if '__domain' in line:
- lines = self.search(cr, uid, line['__domain'], context=context)
- inv_value = 0.0
- product_tmpl_obj = self.pool.get("product.template")
- lines_rec = self.browse(cr, uid, lines, context=context)
- for line_rec in lines_rec:
- if line_rec.product_id.cost_method == 'real':
- price = line_rec.price_unit_on_quant
- else:
- if not line_rec.product_id.id in prod_dict:
- prod_dict[line_rec.product_id.id] = product_tmpl_obj.get_history_price(cr, uid, line_rec.product_id.product_tmpl_id.id, line_rec.company_id.id, date=date, context=context)
- price = prod_dict[line_rec.product_id.id]
- inv_value += price * line_rec.quantity
- line['inventory_value'] = inv_value
+ lines = self.search(cr, uid, line.get('__domain', []), context=context)
+ inv_value = 0.0
+ product_tmpl_obj = self.pool.get("product.template")
+ lines_rec = self.browse(cr, uid, lines, context=context)
+ for line_rec in lines_rec:
+ if line_rec.product_id.cost_method == 'real':
+ price = line_rec.price_unit_on_quant
+ else:
+ if not line_rec.product_id.id in prod_dict:
+ prod_dict[line_rec.product_id.id] = product_tmpl_obj.get_history_price(cr, uid, line_rec.product_id.product_tmpl_id.id, line_rec.company_id.id, date=date, context=context)
+ price = prod_dict[line_rec.product_id.id]
+ inv_value += price * line_rec.quantity
+ line['inventory_value'] = inv_value
return res
def _get_inventory_value(self, cr, uid, ids, name, attr, context=None):
diff --git a/addons/stock_account/wizard/stock_valuation_history_view.xml b/addons/stock_account/wizard/stock_valuation_history_view.xml
index 5ca5fa4dc5d..d2026b5a5ab 100644
--- a/addons/stock_account/wizard/stock_valuation_history_view.xml
+++ b/addons/stock_account/wizard/stock_valuation_history_view.xml
@@ -82,7 +82,7 @@
<field name="arch" type="xml">
<search string="Stock Value At Date">
<field name="product_id"/>
- <field name="location_id" filter_domain="[('name', 'child_of', self)]"/>
+ <field name="location_id" filter_domain="[('location_id', 'child_of', self)]"/>
<field name="company_id" groups="base.group_multi_company"/>
<group expand="1" string="Group By">
<filter string="Product" icon="terp-accessories-archiver" context="{'group_by':'product_id'}" name='group_by_product'/>
diff --git a/addons/stock_dropshipping/__openerp__.py b/addons/stock_dropshipping/__openerp__.py
index 6f58d65efe9..2ff27230c04 100644
--- a/addons/stock_dropshipping/__openerp__.py
+++ b/addons/stock_dropshipping/__openerp__.py
@@ -40,7 +40,6 @@ internal transfer document is needed.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/warehouse',
- 'images': [],
'depends': ['purchase', 'sale_stock'],
'data': ['stock_dropshipping.xml'],
'test': [
diff --git a/addons/stock_invoice_directly/__openerp__.py b/addons/stock_invoice_directly/__openerp__.py
index ed046460f4f..bb107c3e3ee 100644
--- a/addons/stock_invoice_directly/__openerp__.py
+++ b/addons/stock_invoice_directly/__openerp__.py
@@ -33,7 +33,6 @@ wizard if the delivery is to be invoiced.
""",
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/warehouse',
- 'images': ['images/create_invoice.jpeg'],
'depends': ['delivery', 'stock'],
'data': [],
'demo': [],
diff --git a/addons/stock_landed_costs/__openerp__.py b/addons/stock_landed_costs/__openerp__.py
index bb673d4fc3e..29012bdd8a3 100644
--- a/addons/stock_landed_costs/__openerp__.py
+++ b/addons/stock_landed_costs/__openerp__.py
@@ -30,7 +30,6 @@ Landed Costs Management
This module allows you to easily add extra costs on pickings and decide the split of these costs among their stock moves in order to take them into account in your stock valuation.
""",
'website': 'https://www.odoo.com/page/warehouse',
- 'images': [],
'depends': ['stock_account'],
'category': 'Warehouse Management',
'sequence': 16,
diff --git a/addons/subscription/__openerp__.py b/addons/subscription/__openerp__.py
index 4c2620b4d85..68e059016d2 100644
--- a/addons/subscription/__openerp__.py
+++ b/addons/subscription/__openerp__.py
@@ -42,5 +42,4 @@ e.g. To have an invoice generated automatically periodically:
'demo': ['subscription_demo.xml',],
'installable': True,
'auto_install': False,
- 'images': ['images/subscription_document_fields.jpeg','images/subscriptions.jpeg'],
}
diff --git a/addons/survey/__openerp__.py b/addons/survey/__openerp__.py
index dcac9ab75fc..7477a76c3e5 100644
--- a/addons/survey/__openerp__.py
+++ b/addons/survey/__openerp__.py
@@ -55,5 +55,4 @@ sent mails with personal token for the invitation of the survey.
'auto_install': False,
'application': True,
'sequence': 10,
- 'images': [],
}
diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py
index 151df516f3b..511d70896c3 100644
--- a/addons/survey/controllers/main.py
+++ b/addons/survey/controllers/main.py
@@ -30,7 +30,6 @@ from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT as DTF, ustr
-from openerp.tools.safe_eval import safe_eval
_logger = logging.getLogger(__name__)
@@ -167,6 +166,11 @@ class WebsiteSurvey(http.Controller):
elif user_input.state == 'skip':
flag = (True if prev and prev == 'prev' else False)
page, page_nr, last = survey_obj.next_page(cr, uid, user_input, user_input.last_displayed_page_id.id, go_back=flag, context=context)
+
+ #special case if you click "previous" from the last page, then leave the survey, then reopen it from the URL, avoid crash
+ if not page:
+ page, page_nr, last = survey_obj.next_page(cr, uid, user_input, user_input.last_displayed_page_id.id, go_back=True, context=context)
+
data = {'survey': survey, 'page': page, 'page_nr': page_nr, 'token': user_input.token}
if last:
data.update({'last': True})
diff --git a/addons/survey/static/src/js/survey.js b/addons/survey/static/src/js/survey.js
index d8c1d07f300..778bc24ea3d 100644
--- a/addons/survey/static/src/js/survey.js
+++ b/addons/survey/static/src/js/survey.js
@@ -39,7 +39,7 @@ openerp.website.if_dom_contains('.js_surveyform', function (the_form) {
print_mode = true;
}
- // Quizz correction mode
+ // Quiz correction mode
if (! _.isUndefined(scores_controller)) {
quiz_correction_mode = true;
}
@@ -79,33 +79,48 @@ openerp.website.if_dom_contains('.js_surveyform', function (the_form) {
// Pre-filling of the form with previous answers
function prefill(){
- var prefill_def = $.ajax(prefill_controller, {dataType: "json"})
- .done(function(json_data){
- _.each(json_data, function(value, key){
- the_form.find(".form-control[name=" + key + "]").val(value);
- the_form.find("input[name^=" + key + "]").each(function(){
- $(this).val(value);
+ if (! _.isUndefined(prefill_controller)) {
+ var prefill_def = $.ajax(prefill_controller, {dataType: "json"})
+ .done(function(json_data){
+ _.each(json_data, function(value, key){
+
+ // prefill of text/number/date boxes
+ var input = the_form.find(".form-control[name=" + key + "]");
+ input.val(value);
+
+ // special case for comments under multiple suggestions questions
+ if (_.string.endsWith(key, "_comment") &&
+ (input.parent().hasClass("js_comments") || input.parent().hasClass("js_ck_comments"))) {
+ input.siblings().find('>input').attr("checked","checked");
+ }
+
+ // checkboxes and radios
+ the_form.find("input[name^=" + key + "][type!='text']").each(function(){
+ $(this).val(value);
+ });
});
+ })
+ .fail(function(){
+ console.warn("[survey] Unable to load prefill data");
});
- })
- .fail(function(){
- console.warn("[survey] Unable to load prefill data");
- });
- return prefill_def;
+ return prefill_def;
+ }
}
// Display score if quiz correction mode
function display_scores(){
- var score_def = $.ajax(scores_controller, {dataType: "json"})
- .done(function(json_data){
- _.each(json_data, function(value, key){
- the_form.find("span[data-score-question=" + key + "]").text("Your score: " + value);
+ if (! _.isUndefined(scores_controller)) {
+ var score_def = $.ajax(scores_controller, {dataType: "json"})
+ .done(function(json_data){
+ _.each(json_data, function(value, key){
+ the_form.find("span[data-score-question=" + key + "]").text("Your score: " + value);
+ });
+ })
+ .fail(function(){
+ console.warn("[survey] Unable to load score data");
});
- })
- .fail(function(){
- console.warn("[survey] Unable to load score data");
- });
- return score_def;
+ return score_def;
+ }
}
// Parameters for form submission
diff --git a/addons/survey/survey.py b/addons/survey/survey.py
index da84d8ddc8c..612ce78abfa 100644
--- a/addons/survey/survey.py
+++ b/addons/survey/survey.py
@@ -201,7 +201,7 @@ class survey_survey(osv.Model):
'Email Template', ondelete='set null'),
'thank_you_message': fields.html('Thank you message', translate=True,
help="This message will be displayed when survey is completed"),
- 'quizz_mode': fields.boolean(string='Quizz mode')
+ 'quizz_mode': fields.boolean(string='Quiz mode')
}
def _default_stage(self, cr, uid, context=None):
diff --git a/addons/survey/test/__init__.py b/addons/survey/test/__init__.py
index b2a1a5accf2..ee6793c4b7c 100644
--- a/addons/survey/test/__init__.py
+++ b/addons/survey/test/__init__.py
@@ -21,7 +21,3 @@
from . import test_survey
-
-checks = [
- test_survey
-]
diff --git a/addons/warning/__openerp__.py b/addons/warning/__openerp__.py
index a991f42fce0..e7e72738819 100644
--- a/addons/warning/__openerp__.py
+++ b/addons/warning/__openerp__.py
@@ -37,5 +37,4 @@ picking and invoice. The message is triggered by the form's onchange event.
'demo': [],
'installable': True,
'auto_install': False,
- 'images': ['images/customers_warnings.jpeg','images/sale_order_warning.jpeg'],
}
diff --git a/addons/warning/warning.py b/addons/warning/warning.py
index 81bac3cdff5..954b0e96e95 100644
--- a/addons/warning/warning.py
+++ b/addons/warning/warning.py
@@ -76,7 +76,9 @@ class sale_order(osv.osv):
warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']
- return {'value': result.get('value',{}), 'warning':warning}
+ if warning:
+ result['warning'] = warning
+ return result
class purchase_order(osv.osv):
@@ -104,7 +106,9 @@ class purchase_order(osv.osv):
warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']
- return {'value': result.get('value',{}), 'warning':warning}
+ if warning:
+ result['warning'] = warning
+ return result
@@ -143,7 +147,9 @@ class account_invoice(osv.osv):
warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']
- return {'value': result.get('value',{}), 'warning':warning}
+ if warning:
+ result['warning'] = warning
+ return result
class stock_picking(osv.osv):
@@ -171,8 +177,9 @@ class stock_picking(osv.osv):
warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']
- return {'value': result.get('value',{}), 'warning':warning}
-
+ if warning:
+ result['warning'] = warning
+ return result
class product_product(osv.osv):
_inherit = 'product.template'
@@ -221,7 +228,9 @@ class sale_order_line(osv.osv):
warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title']
warning['message'] = message and message +'\n\n'+result['warning']['message'] or result['warning']['message']
- return {'value': result.get('value',{}), 'warning':warning}
+ if warning:
+ result['warning'] = warning
+ return result
class purchase_order_line(osv.osv):
@@ -252,4 +261,6 @@ class purchase_order_line(osv.osv):
warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title']
warning['message'] = message and message +'\n\n'+result['warning']['message'] or result['warning']['message']
- return {'value': result.get('value',{}), 'warning':warning}
+ if warning:
+ result['warning'] = warning
+ return result
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index e95e8f3c58b..cec9ae94837 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -744,24 +744,21 @@ class Database(http.Controller):
return {'error': _('Could not drop database !'), 'title': _('Drop Database')}
@http.route('/web/database/backup', type='http', auth="none")
- def backup(self, backup_db, backup_pwd, token, **kwargs):
+ def backup(self, backup_db, backup_pwd, token, backup_format='zip'):
try:
- format = kwargs.get('format')
- ext = "zip" if format == 'zip' else "dump"
- db_dump = base64.b64decode(
- request.session.proxy("db").dump(backup_pwd, backup_db, format))
- filename = "%(db)s_%(timestamp)s.%(ext)s" % {
- 'db': backup_db,
- 'timestamp': datetime.datetime.utcnow().strftime(
- "%Y-%m-%d_%H-%M-%SZ"),
- 'ext': ext
- }
- return request.make_response(db_dump,
- [('Content-Type', 'application/octet-stream; charset=binary'),
- ('Content-Disposition', content_disposition(filename))],
- {'fileToken': token}
- )
+ openerp.service.security.check_super(backup_pwd)
+ ts = datetime.datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S")
+ filename = "%s_%s.%s" % (backup_db, ts, backup_format)
+ headers = [
+ ('Content-Type', 'application/octet-stream; charset=binary'),
+ ('Content-Disposition', content_disposition(filename)),
+ ]
+ dump_stream = openerp.service.db.dump_db(backup_db, None, backup_format)
+ response = werkzeug.wrappers.Response(dump_stream, headers=headers, direct_passthrough=True)
+ response.set_cookie('fileToken', token)
+ return response
except Exception, e:
+ _logger.exception('Database.backup')
return simplejson.dumps([[],[{'error': openerp.tools.ustr(e), 'title': _('Backup Database')}]])
@http.route('/web/database/restore', type='http', auth="none")
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 296c78d8d6d..abbc1330804 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -3554,7 +3554,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
reinit_value: function(val) {
this.internal_set_value(val);
this.floating = false;
- if (this.is_started)
+ if (this.is_started && !this.no_rerender)
this.render_value();
},
initialize_field: function() {
@@ -3805,7 +3805,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
}
if (! no_recurse) {
var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context());
- this.alive(dataset.name_get([self.get("value")])).done(function(data) {
+ var def = this.alive(dataset.name_get([self.get("value")])).done(function(data) {
if (!data[0]) {
self.do_warn(_t("Render"), _t("No value found for the field "+self.field.string+" for value "+self.get("value")));
return;
@@ -3818,6 +3818,9 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
self.display_value["" + self.get("value")] = self.display_value_backup["" + self.get("value")];
self.render_value(true);
});
+ if (this.view && this.view.render_value_defs){
+ this.view.render_value_defs.push(def);
+ }
}
},
display_string: function(str) {
@@ -4293,7 +4296,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
this.dataset.index = 0;
}
this.trigger_on_change();
- if (this.is_started) {
+ if (this.is_started && !this.no_rerender) {
return self.reload_current_view();
} else {
return $.when();
@@ -4437,12 +4440,19 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
return true;
}
var r;
- return _.every(this.records.records, function(record){
+ if (_.isEmpty(this.records.records)){
+ return true;
+ }
+ current_values = {};
+ _.each(this.editor.form.fields, function(field){
+ field._inhibit_on_change_flag = true;
+ field.no_rerender = true;
+ current_values[field.name] = field.get('value');
+ });
+ var valid = _.every(this.records.records, function(record){
r = record;
_.each(self.editor.form.fields, function(field){
- field._inhibit_on_change_flag = true;
field.set_value(r.attributes[field.name]);
- field._inhibit_on_change_flag = false;
});
return _.every(self.editor.form.fields, function(field){
field.process_modifiers();
@@ -4450,6 +4460,12 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
return field.is_valid();
});
});
+ _.each(this.editor.form.fields, function(field){
+ field.set('value', current_values[field.name]);
+ field._inhibit_on_change_flag = false;
+ field.no_rerender = false;
+ });
+ return valid;
},
do_add_record: function () {
if (this.editable()) {
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index 831d383c731..77559504060 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -231,8 +231,9 @@
this.records.add(record, {
at: this.prepends_on_create() ? 0 : null});
}
-
- return this.ensure_saved().then(function () {
+ return this.ensure_saved().then(function(){
+ return $.when.apply(null, self.editor.form.render_value_defs);
+ }).then(function () {
var $recordRow = self.groups.get_row_for(record);
var cells = self.get_cells_for($recordRow);
var fields = {};
diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js
index 68915077a48..06e19e28ca9 100644
--- a/addons/web/static/src/js/view_tree.js
+++ b/addons/web/static/src/js/view_tree.js
@@ -222,27 +222,25 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
var local_context = {
active_model: self.dataset.model,
active_id: id,
- active_ids: [id]};
+ active_ids: [id]
+ };
+ var ctx = instance.web.pyeval.eval(
+ 'context', new instance.web.CompoundContext(
+ this.dataset.get_context(), local_context));
return this.rpc('/web/treeview/action', {
id: id,
model: this.dataset.model,
- context: instance.web.pyeval.eval(
- 'context', new instance.web.CompoundContext(
- this.dataset.get_context(), local_context))
+ context: ctx
}).then(function (actions) {
if (!actions.length) { return; }
var action = actions[0][2];
- var c = new instance.web.CompoundContext(local_context);
+ var c = new instance.web.CompoundContext(local_context).set_eval_context(ctx);
if (action.context) {
c.add(action.context);
}
- return instance.web.pyeval.eval_domains_and_contexts({
- contexts: [c], domains: []
- }).then(function (res) {
- action.context = res.context;
- return self.do_action(action);
- }, null);
- }, null);
+ action.context = c;
+ return self.do_action(action);
+ });
},
// show & hide the contents
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index b0bceafc5a3..7d053f81700 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -216,6 +216,15 @@
</td>
</tr>
<tr>
+ <td><label for="backup_format">Backup format:</label></td>
+ <td class="oe_form_field oe_form_field_selection">
+ <select name="backup_format">
+ <option value="zip">zip (includes filestore)</option>
+ <option value="dump">pg_dump custom format (without filestore)</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
<td><label for="backup_pwd">Master Password:</label></td>
<td><input type="password" name="backup_pwd" class="required" /></td>
</tr>
diff --git a/addons/web/views/database_manager.html b/addons/web/views/database_manager.html
index da2c1a9f41a..79a193f283f 100644
--- a/addons/web/views/database_manager.html
+++ b/addons/web/views/database_manager.html
@@ -3,7 +3,7 @@
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
- <title>OpenERP</title>
+ <title>Odoo</title>
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
<link rel="stylesheet" href="/web/static/src/css/full.css" />
<script src="/web/static/lib/es5-shim/es5-shim.min.js" type="text/javascript"></script>
@@ -104,4 +104,4 @@
<script>CFInstall.check({mode: "overlay"});</script>
<![endif]-->
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/addons/web/views/database_selector.html b/addons/web/views/database_selector.html
index 8164ddb9498..67c82c6c260 100644
--- a/addons/web/views/database_selector.html
+++ b/addons/web/views/database_selector.html
@@ -2,7 +2,7 @@
<html style="height: 100%">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
- <title>OpenERP</title>
+ <title>Odoo</title>
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="/web/static/src/css/full.css">
<link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css">
diff --git a/addons/website/models/website.py b/addons/website/models/website.py
index 513a15676d2..e49773028c9 100644
--- a/addons/website/models/website.py
+++ b/addons/website/models/website.py
@@ -78,7 +78,8 @@ def is_multilang_url(local_url, langs=None):
path = url[0]
query_string = url[1] if len(url) > 1 else None
router = request.httprequest.app.get_db_router(request.db).bind('')
- func = router.match(path, query_args=query_string)[0]
+ # Force to check method to POST. Odoo uses methods : ['POST'] and ['GET', 'POST']
+ func = router.match(path, method='POST', query_args=query_string)[0]
return func.routing.get('website', False) and func.routing.get('multilang', True)
except Exception:
return False
diff --git a/addons/website/static/src/css/snippets.css b/addons/website/static/src/css/snippets.css
index 83e5a83a38c..de7cef637bb 100644
--- a/addons/website/static/src/css/snippets.css
+++ b/addons/website/static/src/css/snippets.css
@@ -42,8 +42,9 @@
border: 0;
}
#oe_snippets .tab-content {
+ margin-right: 120px;
display: inline-block;
- white-space: nowrap;
+ white-space: normal;
background: black;
margin-right: 60px;
}
@@ -68,7 +69,7 @@
}
.oe_snippet {
- float: left;
+ display: inline-block;
vertical-align: top;
width: 93px;
margin-left: 1px;
diff --git a/addons/website/static/src/css/snippets.sass b/addons/website/static/src/css/snippets.sass
index 6aba0d51daf..0440b6f634c 100644
--- a/addons/website/static/src/css/snippets.sass
+++ b/addons/website/static/src/css/snippets.sass
@@ -39,8 +39,9 @@
.pill-content
border: 0
.tab-content
+ margin-right: 120px
display: inline-block
- white-space: nowrap
+ white-space: normal
background: black
margin-right: 60px
> div
@@ -57,7 +58,7 @@
@include transform-origin(50%)
.oe_snippet
- float: left
+ display: inline-block
vertical-align: top
width: 93px
margin-left: 1px
diff --git a/addons/website_crm/controllers/main.py b/addons/website_crm/controllers/main.py
index 02e700ae5bc..76f917ae398 100644
--- a/addons/website_crm/controllers/main.py
+++ b/addons/website_crm/controllers/main.py
@@ -28,7 +28,8 @@ class contactus(http.Controller):
def create_lead(self, request, values, kwargs):
""" Allow to be overrided """
- return request.registry['crm.lead'].create(request.cr, SUPERUSER_ID, values, request.context)
+ cr, context = request.cr, request.context
+ return request.registry['crm.lead'].create(cr, SUPERUSER_ID, values, context=dict(context, mail_create_nosubscribe=True))
def preRenderThanks(self, values, kwargs):
""" Allow to be overrided """
@@ -76,11 +77,8 @@ class contactus(http.Controller):
values = dict(values, error=error, kwargs=kwargs.items())
return request.website.render(kwargs.get("view_from", "website.contactus"), values)
- try:
- values['medium_id'] = request.registry['ir.model.data'].get_object_reference(request.cr, SUPERUSER_ID, 'crm', 'crm_tracking_medium_website')[1]
- values['team_id'] = request.registry['ir.model.data'].xmlid_to_res_id(request.cr, SUPERUSER_ID, 'website.salesteam_website_sales')
- except ValueError:
- pass
+ values['medium_id'] = request.registry['ir.model.data'].xmlid_to_res_id(request.cr, SUPERUSER_ID, 'crm.crm_medium_website')
+ values['team_id'] = request.registry['ir.model.data'].xmlid_to_res_id(request.cr, SUPERUSER_ID, 'website.salesteam_website_sales')
# description is required, so it is always already initialized
if post_description:
diff --git a/addons/website_forum/models/forum.py b/addons/website_forum/models/forum.py
index b8fe561d4a5..63200dd51f2 100644
--- a/addons/website_forum/models/forum.py
+++ b/addons/website_forum/models/forum.py
@@ -48,6 +48,7 @@ class Forum(models.Model):
faq = fields.Html('Guidelines', default=_get_default_faq, translate=True)
description = fields.Html(
'Description',
+ translate=True,
default='<p> This community is for professionals and enthusiasts of our products and services.'
'Share and discuss the best content and new marketing ideas,'
'build your professional profile and become a better marketer together.</p>')
@@ -264,6 +265,15 @@ class Post(models.Model):
def _get_post_karma_rights(self):
user = self.env.user
+ def name_get(self, cr, uid, ids, context=None):
+ result = []
+ for post in self.browse(cr, uid, ids, context=context):
+ if post.parent_id and not post.name:
+ result.append((post.id, '%s (%s)' % (post.parent_id.name, post.id)))
+ else:
+ result.append((post.id, '%s' % (post.name)))
+ return result
+
self.karma_accept = self.parent_id and self.parent_id.create_uid.id == self._uid and self.forum_id.karma_answer_accept_own or self.forum_id.karma_answer_accept_all
self.karma_edit = self.create_uid.id == self._uid and self.forum_id.karma_edit_own or self.forum_id.karma_edit_all
self.karma_close = self.create_uid.id == self._uid and self.forum_id.karma_close_own or self.forum_id.karma_close_all
diff --git a/addons/website_forum/views/forum.xml b/addons/website_forum/views/forum.xml
index 421d76e37e2..916ddeb5d0c 100644
--- a/addons/website_forum/views/forum.xml
+++ b/addons/website_forum/views/forum.xml
@@ -29,6 +29,7 @@
<h1>
<field name="name"/>
</h1>
+ <field name="description"/>
<notebook>
<page string="Options">
<group string="Post Types">
diff --git a/addons/website_mail/tests/__init__.py b/addons/website_mail/tests/__init__.py
index fb06fbc2f3e..c6311ba9f30 100644
--- a/addons/website_mail/tests/__init__.py
+++ b/addons/website_mail/tests/__init__.py
@@ -21,6 +21,3 @@
import test_controllers
-checks = [
- test_controllers,
-]
diff --git a/addons/website_project_issue/tests/__init__.py b/addons/website_project_issue/tests/__init__.py
index 93963ea4559..deb30d89953 100644
--- a/addons/website_project_issue/tests/__init__.py
+++ b/addons/website_project_issue/tests/__init__.py
@@ -21,6 +21,3 @@
from . import test_access_rights
-checks = [
- test_access_rights,
-]
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index 6ada56a55c5..c8f4e29a6f9 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -144,8 +144,9 @@ class website_sale(http.Controller):
domain = request.website.sale_product_domain()
if search:
- domain += ['|', '|', '|', ('name', 'ilike', search), ('description', 'ilike', search),
- ('description_sale', 'ilike', search), ('product_variant_ids.default_code', 'ilike', search)]
+ for srch in search.split(" "):
+ domain += ['|', '|', '|', ('name', 'ilike', srch), ('description', 'ilike', srch),
+ ('description_sale', 'ilike', srch), ('product_variant_ids.default_code', 'ilike', srch)]
if category:
domain += [('public_categ_ids', 'child_of', int(category))]
diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py
index 1dbe4b2eb5c..255789ae9c3 100644
--- a/addons/website_sale/models/product.py
+++ b/addons/website_sale/models/product.py
@@ -47,15 +47,14 @@ class product_public_category(osv.osv):
]
def name_get(self, cr, uid, ids, context=None):
- if not len(ids):
- return []
- reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
res = []
- for record in reads:
- name = record['name']
- if record['parent_id']:
- name = record['parent_id'][1]+' / '+name
- res.append((record['id'], name))
+ for cat in self.browse(cr, uid, ids, context=context):
+ names = [cat.name]
+ pcat = cat.parent_id
+ while pcat:
+ names.append(pcat.name)
+ pcat = pcat.parent_id
+ res.append((cat.id, ' / '.join(reversed(names))))
return res
def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
diff --git a/addons/website_sale/static/src/js/website_sale_payment.js b/addons/website_sale/static/src/js/website_sale_payment.js
index ca61e0655d9..8ca1b85f907 100644
--- a/addons/website_sale/static/src/js/website_sale_payment.js
+++ b/addons/website_sale/static/src/js/website_sale_payment.js
@@ -10,7 +10,7 @@ $(document).ready(function () {
.find("input[name='acquirer']:checked").click();
// When clicking on payment button: create the tx using json then continue to the acquirer
- $payment.on("click", 'button[type="submit"]', function (ev) {
+ $payment.on("click", 'button[type="submit"],button[name="submit"]', function (ev) {
ev.preventDefault();
ev.stopPropagation();
var $form = $(ev.currentTarget).parents('form');
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index 19f7eabbd1f..42670547b20 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -748,7 +748,7 @@
</xpath>
</template>
-<template id="reduction_code" inherit_id="website_sale.cart" active="False" customize_show="True" name="Reduction Code">
+<template id="reduction_code" inherit_id="website_sale.cart" active="False" customize_show="True" name="Coupon Code">
<xpath expr="//div[@id='right_column']" position="inside">
<h4>Coupon Code</h4>
<p>
diff --git a/addons/website_twitter/static/src/js/website.twitter.animation.js b/addons/website_twitter/static/src/js/website.twitter.animation.js
index 62d62cc5e74..10d9820ee30 100644
--- a/addons/website_twitter/static/src/js/website.twitter.animation.js
+++ b/addons/website_twitter/static/src/js/website.twitter.animation.js
@@ -17,9 +17,6 @@
if (url) {
window.open(url, '_blank');
}
- else {
- debugger;
- }
}
});
$("<center><div><img src='/website_twitter/static/src/img/loadtweet.gif'></div></center>").appendTo(timeline);