summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2015-05-11 15:12:24 +0200
committerChristophe Simonis <chs@odoo.com>2015-05-11 15:12:24 +0200
commit0da722aa6a3a5b2ea8a9f740ea2ccc5ed2260b0a (patch)
tree9005a0ab8df9df374dda2d3cb4c8564bc73a023c /addons
parentd756bab4d1d89af414b80523124b463252f31159 (diff)
parentbff6dffbb424a9ab458ae8fc2044f0b52490c36b (diff)
[MERGE] forward port of branch 8.0 up to bff6dff
Diffstat (limited to 'addons')
-rw-r--r--addons/account/account_bank_statement.py8
-rw-r--r--addons/account/account_invoice.py2
-rw-r--r--addons/account/account_move_line.py17
-rw-r--r--addons/account/report/account_entries_report.py2
-rw-r--r--addons/account/static/src/js/account_widgets.js4
-rw-r--r--addons/account/wizard/account_statement_from_invoice.py2
-rw-r--r--addons/calendar/calendar.py3
-rw-r--r--addons/crm/crm_lead_view.xml4
-rw-r--r--addons/delivery/__openerp__.py4
-rw-r--r--addons/delivery/test/stock_move_values_with_invoice_before_delivery.yml83
-rw-r--r--addons/hw_proxy/controllers/main.py4
-rw-r--r--addons/hw_scale/__openerp__.py1
-rw-r--r--addons/hw_scale/controllers/main.py20
-rw-r--r--addons/hw_scanner/__openerp__.py1
-rw-r--r--addons/hw_scanner/controllers/main.py15
-rw-r--r--addons/im_chat/static/src/xml/im_chat.xml2
-rw-r--r--addons/mass_mailing/views/mass_mailing.xml2
-rw-r--r--addons/mrp/mrp.py10
-rw-r--r--addons/mrp/procurement.py2
-rw-r--r--addons/payment_adyen/models/adyen.py2
-rw-r--r--addons/point_of_sale/report/pos_details.py8
-rw-r--r--addons/point_of_sale/static/src/js/db.js2
-rw-r--r--addons/purchase/purchase.py51
-rw-r--r--addons/sale/test/manual_order_policy.yml2
-rw-r--r--addons/sale_layout/security/ir.model.access.csv2
-rw-r--r--addons/stock/stock.py51
-rw-r--r--addons/stock/stock_view.xml2
-rw-r--r--addons/stock/test/inventory.yml2
-rw-r--r--addons/stock/wizard/stock_change_product_qty.py7
-rw-r--r--addons/stock_account/stock.py13
-rw-r--r--addons/stock_dropshipping/stock_dropshipping.py13
-rw-r--r--addons/stock_dropshipping/test/crossdock.yml86
-rw-r--r--addons/web/controllers/main.py2
-rw-r--r--addons/website_membership/__init__.py1
-rw-r--r--addons/website_membership/controllers/main.py51
-rw-r--r--addons/website_membership/models/__init__.py1
-rw-r--r--addons/website_membership/models/membership.py35
-rw-r--r--addons/website_quote/models/order.py4
-rw-r--r--addons/website_quote/static/src/css/website_quotation.css5
-rw-r--r--addons/website_quote/views/website_quotation.xml2
-rw-r--r--addons/website_sale/controllers/main.py7
-rw-r--r--addons/website_sale/static/src/js/website_sale.js3
42 files changed, 415 insertions, 123 deletions
diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py
index a3769e08818..896f359beae 100644
--- a/addons/account/account_bank_statement.py
+++ b/addons/account/account_bank_statement.py
@@ -20,6 +20,7 @@
##############################################################################
from openerp.osv import fields, osv, expression
+from openerp.tools import float_is_zero
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
from openerp.report import report_sxw
@@ -597,7 +598,7 @@ class account_bank_statement_line(osv.osv):
domain += [(amount_field, '<', 0), (amount_field, '>', (sign * amount))]
else:
domain += [(amount_field, '>', 0), (amount_field, '<', (sign * amount))]
- mv_lines = self.get_move_lines_for_reconciliation(cr, uid, st_line, excluded_ids=excluded_ids, limit=5, additional_domain=domain)
+ mv_lines = self.get_move_lines_for_reconciliation(cr, uid, st_line, excluded_ids=excluded_ids, limit=5, additional_domain=domain, context=context)
ret = []
total = 0
for line in mv_lines:
@@ -841,6 +842,11 @@ class account_bank_statement_line(osv.osv):
if mv_line_dict.get('counterpart_move_line_id'):
#post an account line that use the same currency rate than the counterpart (to balance the account) and post the difference in another line
ctx['date'] = mv_line.date
+ if mv_line.currency_id.id == mv_line_dict['currency_id'] \
+ and float_is_zero(abs(mv_line.amount_currency) - abs(mv_line_dict['amount_currency']), precision_rounding=mv_line.currency_id.rounding):
+ debit_at_old_rate = mv_line.credit
+ credit_at_old_rate = mv_line.debit
+ else:
debit_at_old_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'], context=ctx)
credit_at_old_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=ctx)
mv_line_dict['credit'] = credit_at_old_rate
diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py
index 73ce713d994..2c4b9ef0bb1 100644
--- a/addons/account/account_invoice.py
+++ b/addons/account/account_invoice.py
@@ -131,7 +131,7 @@ class account_invoice(models.Model):
continue
# Get the correct line residual amount
if line.currency_id == self.currency_id:
- line_amount = line.currency_id and line.amount_residual_currency or line.amount_residual
+ line_amount = line.amount_residual_currency if line.currency_id else line.amount_residual
else:
from_currency = line.company_id.currency_id.with_context(date=line.date)
line_amount = from_currency.compute(line.amount_residual, self.currency_id)
diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py
index 77a6ff2ed86..520b3ddad80 100644
--- a/addons/account/account_move_line.py
+++ b/addons/account/account_move_line.py
@@ -867,12 +867,17 @@ class account_move_line(osv.osv):
total_amount_currency_str = rml_parser.formatLang(total_amount, currency_obj=line_currency)
ret_line['credit_currency'] = actual_credit
ret_line['debit_currency'] = actual_debit
- ctx = context.copy()
- if target_date:
- ctx.update({'date': target_date})
- total_amount = currency_obj.compute(cr, uid, line_currency.id, target_currency.id, total_amount, context=ctx)
- actual_debit = currency_obj.compute(cr, uid, line_currency.id, target_currency.id, actual_debit, context=ctx)
- actual_credit = currency_obj.compute(cr, uid, line_currency.id, target_currency.id, actual_credit, context=ctx)
+ if target_currency == company_currency:
+ actual_debit = debit
+ actual_credit = credit
+ total_amount = debit or credit
+ else:
+ ctx = context.copy()
+ if target_date:
+ ctx.update({'date': target_date})
+ total_amount = currency_obj.compute(cr, uid, line_currency.id, target_currency.id, total_amount, context=ctx)
+ actual_debit = currency_obj.compute(cr, uid, line_currency.id, target_currency.id, actual_debit, context=ctx)
+ actual_credit = currency_obj.compute(cr, uid, line_currency.id, target_currency.id, actual_credit, context=ctx)
amount_str = rml_parser.formatLang(actual_debit or actual_credit, currency_obj=target_currency)
total_amount_str = rml_parser.formatLang(total_amount, currency_obj=target_currency)
diff --git a/addons/account/report/account_entries_report.py b/addons/account/report/account_entries_report.py
index dd2f40560d0..9e042712ed0 100644
--- a/addons/account/report/account_entries_report.py
+++ b/addons/account/report/account_entries_report.py
@@ -134,7 +134,7 @@ class account_entries_report(osv.osv):
l.amount_currency as amount_currency,
l.debit as debit,
l.credit as credit,
- l.debit-l.credit as balance
+ coalesce(l.debit, 0.0) - coalesce(l.credit, 0.0) as balance
from
account_move_line l
left join account_account a on (l.account_id = a.id)
diff --git a/addons/account/static/src/js/account_widgets.js b/addons/account/static/src/js/account_widgets.js
index 86a5172005d..da0e51a8680 100644
--- a/addons/account/static/src/js/account_widgets.js
+++ b/addons/account/static/src/js/account_widgets.js
@@ -236,7 +236,7 @@ openerp.account = function (instance) {
// Display the reconciliations
return self.model_bank_statement_line
- .call("get_data_for_reconciliations", [reconciliations_to_show])
+ .call("get_data_for_reconciliations", [reconciliations_to_show], {context:self.session.user_context})
.then(function (data) {
var child_promises = [];
while ((datum = data.shift()) !== undefined)
@@ -1638,7 +1638,7 @@ openerp.account = function (instance) {
limit += 1; // Let's fetch 1 more item than requested
if (limit > 0) {
return self.model_bank_statement_line
- .call("get_move_lines_for_reconciliation_by_statement_line_id", [self.st_line.id, excluded_ids, self.filter, offset, limit])
+ .call("get_move_lines_for_reconciliation_by_statement_line_id", [self.st_line.id, excluded_ids, self.filter, offset, limit], {context:self.session.user_context})
.then(function (lines) {
_.each(lines, function(line) { self.decorateMoveLine(line, self.st_line.currency_id) }, self);
// If we could fetch 1 more item than what we'll display, that means there are move lines left to be displayed (so we enable the pager)
diff --git a/addons/account/wizard/account_statement_from_invoice.py b/addons/account/wizard/account_statement_from_invoice.py
index 658e1b4d829..e43988d1121 100644
--- a/addons/account/wizard/account_statement_from_invoice.py
+++ b/addons/account/wizard/account_statement_from_invoice.py
@@ -47,8 +47,8 @@ class account_statement_from_invoice_lines(osv.osv_memory):
statement_obj = self.pool.get('account.bank.statement')
statement_line_obj = self.pool.get('account.bank.statement.line')
currency_obj = self.pool.get('res.currency')
- line_date = time.strftime('%Y-%m-%d')
statement = statement_obj.browse(cr, uid, statement_id, context=context)
+ line_date = statement.date
# for each selected move lines
for line in line_obj.browse(cr, uid, line_ids, context=context):
diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py
index 90f81e8d884..28f5191666e 100644
--- a/addons/calendar/calendar.py
+++ b/addons/calendar/calendar.py
@@ -702,6 +702,9 @@ class calendar_event(osv.Model):
val = pytz.UTC.localize(val)
return val.astimezone(timezone)
+ if context is None:
+ context = {}
+
timezone = pytz.timezone(context.get('tz') or 'UTC')
startdate = pytz.UTC.localize(datetime.strptime(event.start, DEFAULT_SERVER_DATETIME_FORMAT)) # Add "+hh:mm" timezone
if not startdate:
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index b07b21e02a6..3fad50e1595 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -408,8 +408,8 @@
<field name="arch" type="xml">
<form string="Opportunities">
<header>
- <button name="case_mark_won" string="Mark Won" type="object" class="oe_highlight"/>
- <button name="case_mark_lost" string="Mark Lost" type="object" class="oe_highlight"/>
+ <button name="case_mark_won" string="Mark Won" attrs="{'invisible': [('probability', '=', 100)]}" type="object" class="oe_highlight"/>
+ <button name="case_mark_lost" string="Mark Lost" attrs="{'invisible': [('probability', '=', 100)]}" type="object" class="oe_highlight"/>
<field name="stage_id" widget="statusbar" clickable="True"
options="{'fold_field': 'fold'}"
domain="['&amp;', ('team_ids', '=', team_id), '|', ('type', '=', type), ('type', '=', 'both')]"/>
diff --git a/addons/delivery/__openerp__.py b/addons/delivery/__openerp__.py
index 1d1c59652c4..8a468af3431 100644
--- a/addons/delivery/__openerp__.py
+++ b/addons/delivery/__openerp__.py
@@ -41,7 +41,9 @@ invoices from picking, OpenERP is able to add and compute the shipping line.
'views/report_shipping.xml',
],
'demo': ['delivery_demo.xml'],
- 'test': ['test/delivery_cost.yml'],
+ 'test': ['test/delivery_cost.yml',
+ 'test/stock_move_values_with_invoice_before_delivery.yml',
+ ],
'installable': True,
'auto_install': False,
}
diff --git a/addons/delivery/test/stock_move_values_with_invoice_before_delivery.yml b/addons/delivery/test/stock_move_values_with_invoice_before_delivery.yml
new file mode 100644
index 00000000000..1a998f51e22
--- /dev/null
+++ b/addons/delivery/test/stock_move_values_with_invoice_before_delivery.yml
@@ -0,0 +1,83 @@
+-
+ test that the store fields of stock moves are computed with invoice before delivery flow
+-
+ set a weight on ipod 16GB
+-
+ !python {model: product.product, id: product.product_product_11}: |
+ self.write({'weight': 0.25, 'weight_net': 0.2})
+-
+ create a SO with invoice before delivery
+-
+ !record {model: sale.order, id: sale_prepaid}:
+ partner_id: base.res_partner_18
+ partner_invoice_id: base.res_partner_18
+ partner_shipping_id: base.res_partner_18
+ pricelist_id: product.list0
+ order_policy: 'prepaid'
+ order_line:
+ - name: 'Ipod'
+ product_id: product.product_product_11
+ product_uom_qty: 2
+ product_uos_qty: 2
+ product_uom: product.product_uom_unit
+ price_unit: 750.00
+ carrier_id: normal_delivery_carrier
+-
+ I add delivery cost in Sale order.
+-
+ !python {model: sale.order, id: sale_prepaid}: |
+ self.delivery_set()
+-
+ I confirm the SO.
+-
+ !workflow {model: sale.order, action: order_confirm, ref: sale_prepaid}
+-
+ I check that the invoice was created
+-
+ !python {model: sale.order, id: sale_prepaid}: |
+ assert len(self.invoice_ids) == 1, "Invoice not created."
+-
+ I confirm the invoice
+-
+ !python {model: sale.order, id: sale_prepaid}: |
+ invoice = self.invoice_ids[0]
+ invoice.signal_workflow('invoice_open')
+-
+ I pay the invoice.
+-
+ !python {model: sale.order, id: sale_prepaid}: |
+ invoice = self.invoice_ids[0]
+ invoice.signal_workflow('invoice_open')
+ journal = self.env['account.journal'].search([('type', '=', 'cash'),
+ ('company_id', '=', self.company_id.id)],
+ limit=1)
+ invoice.pay_and_reconcile(
+ invoice.amount_total, ref('account.cash'), ref('account.period_8'),
+ journal.id, ref('account.cash'),
+ ref('account.period_8'), journal.id,
+ name='test')
+-
+ Check the SO after paying the invoice
+-
+ !python {model: sale.order, id: sale_prepaid}: |
+ assert self.invoice_exists, 'order not invoiced'
+ assert self.invoiced, 'order is not paid'
+ assert len(self.picking_ids) == 1, 'pickings not generated'
+-
+ check the stock moves
+-
+ !python {model: sale.order, id: sale_prepaid}: |
+ move = self.picking_ids.move_lines
+ assert move.product_qty == 2, 'wrong product_qty'
+ assert move.weight == 0.5, 'wrong move weight'
+ assert move.weight_net == 0.4, 'wrong move weight_net'
+-
+ ship
+-
+ !python {model: sale.order, id: sale_prepaid}: |
+ picking = self.picking_ids.action_done()
+-
+ Check the SO after shipping
+-
+ !python {model: sale.order, id: sale_prepaid}: |
+ assert self.state == 'done'
diff --git a/addons/hw_proxy/controllers/main.py b/addons/hw_proxy/controllers/main.py
index 64f6cf0765c..337874cf67a 100644
--- a/addons/hw_proxy/controllers/main.py
+++ b/addons/hw_proxy/controllers/main.py
@@ -192,12 +192,12 @@ class Proxy(http.Controller):
print 'print_receipt' + str(receipt)
@http.route('/hw_proxy/is_scanner_connected', type='json', auth='none', cors='*')
- def print_receipt(self, receipt):
+ def is_scanner_connected(self, receipt):
print 'is_scanner_connected?'
return False
@http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
- def print_receipt(self, receipt):
+ def scanner(self, receipt):
print 'scanner'
time.sleep(10)
return ''
diff --git a/addons/hw_scale/__openerp__.py b/addons/hw_scale/__openerp__.py
index 6c83a854730..387a7e618d9 100644
--- a/addons/hw_scale/__openerp__.py
+++ b/addons/hw_scale/__openerp__.py
@@ -37,6 +37,7 @@ such as the Mettler Toledo Ariva.
""",
'author': 'OpenERP SA',
'depends': ['hw_proxy'],
+ 'external_dependencies': {'python': ['serial']},
'test': [
],
'installable': True,
diff --git a/addons/hw_scale/controllers/main.py b/addons/hw_scale/controllers/main.py
index 531f2739f55..44a031f5566 100644
--- a/addons/hw_scale/controllers/main.py
+++ b/addons/hw_scale/controllers/main.py
@@ -178,28 +178,34 @@ class Scale(Thread):
if not self.device:
time.sleep(5)
-s = Scale()
-
-hw_proxy.drivers['scale'] = s
+scale_thread = None
+if serial:
+ scale_thread = Scale()
+ hw_proxy.drivers['scale'] = scale_thread
class ScaleDriver(hw_proxy.Proxy):
@http.route('/hw_proxy/scale_read/', type='json', auth='none', cors='*')
def scale_read(self):
- return {'weight':s.get_weight(), 'unit':'kg', 'info':s.get_weight_info()}
+ if scale_thread:
+ return {'weight': scale_thread.get_weight(), 'unit':'kg', 'info': scale_thread.get_weight_info()}
+ return None
@http.route('/hw_proxy/scale_zero/', type='json', auth='none', cors='*')
def scale_zero(self):
- s.set_zero()
+ if scale_thread:
+ scale_thread.set_zero()
return True
@http.route('/hw_proxy/scale_tare/', type='json', auth='none', cors='*')
def scale_tare(self):
- s.set_tare()
+ if scale_thread:
+ scale_thread.set_tare()
return True
@http.route('/hw_proxy/scale_clear_tare/', type='json', auth='none', cors='*')
def scale_clear_tare(self):
- s.clear_tare()
+ if scale_thread:
+ scale_thread.clear_tare()
return True
diff --git a/addons/hw_scanner/__openerp__.py b/addons/hw_scanner/__openerp__.py
index aa74b0fbc5b..f38fa7dc420 100644
--- a/addons/hw_scanner/__openerp__.py
+++ b/addons/hw_scanner/__openerp__.py
@@ -38,6 +38,7 @@ point of sale module.
""",
'author': 'OpenERP SA',
'depends': ['hw_proxy'],
+ 'external_dependencies': {'python': ['evdev']},
'test': [
],
'installable': True,
diff --git a/addons/hw_scanner/controllers/main.py b/addons/hw_scanner/controllers/main.py
index 057674c1ca1..0ae08d7d36b 100644
--- a/addons/hw_scanner/controllers/main.py
+++ b/addons/hw_scanner/controllers/main.py
@@ -111,8 +111,6 @@ class Scanner(Thread):
def get_device(self):
try:
- if not evdev:
- return None
devices = [ device for device in listdir(self.input_dir)]
keyboards = [ device for device in devices if ('kbd' in device) and ('keyboard' not in device.lower())]
scanners = [ device for device in devices if ('barcode' in device.lower()) or ('scanner' in device.lower())]
@@ -135,7 +133,6 @@ class Scanner(Thread):
been returned before. This is necessary to catch barcodes scanned while the POS is
busy reading another barcode
"""
-
self.lockedstart()
while True:
@@ -204,12 +201,12 @@ class Scanner(Thread):
except Exception as e:
self.set_status('error',str(e))
-s = Scanner()
-
-hw_proxy.drivers['scanner'] = s
+scanner_thread = None
+if evdev:
+ scanner_thread = Scanner()
+ hw_proxy.drivers['scanner'] = scanner_thread
class ScannerDriver(hw_proxy.Proxy):
@http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
- def scanner(self):
- return s.get_barcode()
-
+ def scanner_thread(self):
+ return scanner_thread.get_barcode() if scanner_thread else None
diff --git a/addons/im_chat/static/src/xml/im_chat.xml b/addons/im_chat/static/src/xml/im_chat.xml
index 290de6b8cc6..6393064daa2 100644
--- a/addons/im_chat/static/src/xml/im_chat.xml
+++ b/addons/im_chat/static/src/xml/im_chat.xml
@@ -82,7 +82,7 @@
<div class="oe_im">
<div class="oe_im_frame_header">
<i class="fa fa-search oe_im_search_icon"/>
- <input class="oe_im_searchbox" t-att-placeholder="_t('Search users...')"/>
+ <input class="oe_im_searchbox" placeholder="Search users..."/>
</div>
<div class="oe_im_users"></div>
<div class="oe_im_content"></div>
diff --git a/addons/mass_mailing/views/mass_mailing.xml b/addons/mass_mailing/views/mass_mailing.xml
index d8db956f113..825d5a7d2db 100644
--- a/addons/mass_mailing/views/mass_mailing.xml
+++ b/addons/mass_mailing/views/mass_mailing.xml
@@ -270,7 +270,7 @@
</record>
<record model="ir.actions.act_window" id="action_view_mass_mailing_lists">
- <field name="name">Contact Lists</field>
+ <field name="name">Mailing Lists</field>
<field name="res_model">mail.mass_mailing.list</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py
index 73d3fc1660b..fbcee18d3e4 100644
--- a/addons/mrp/mrp.py
+++ b/addons/mrp/mrp.py
@@ -25,7 +25,7 @@ from collections import OrderedDict
import openerp.addons.decimal_precision as dp
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
-from openerp.tools import float_compare
+from openerp.tools import float_compare, float_is_zero
from openerp.tools.translate import _
from openerp import tools, SUPERUSER_ID
from openerp.addons.product import _common
@@ -604,7 +604,7 @@ class mrp_production(osv.osv):
'date_planned': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'product_qty': lambda *a: 1.0,
'user_id': lambda self, cr, uid, c: uid,
- 'name': lambda x, y, z, c: x.pool.get('ir.sequence').next_by_code(y, z, 'mrp.production') or '/',
+ 'name': lambda self, cr, uid, context: self.pool['ir.sequence'].next_by_code(cr, uid, 'mrp.production', context=context) or '/',
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.production', context=c),
'location_src_id': _src_id_default,
'location_dest_id': _dest_id_default
@@ -949,6 +949,7 @@ class mrp_production(osv.osv):
uom_obj = self.pool.get("product.uom")
production = self.browse(cr, uid, production_id, context=context)
production_qty_uom = uom_obj._compute_qty(cr, uid, production.product_uom.id, production_qty, production.product_id.uom_id.id)
+ precision = self.pool['decimal.precision'].precision_get(cr, uid, 'Product Unit of Measure')
main_production_move = False
if production_mode == 'consume_produce':
@@ -970,7 +971,8 @@ class mrp_production(osv.osv):
location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context)
stock_mov_obj.write(cr, uid, new_moves, {'production_id': production_id}, context=context)
remaining_qty = subproduct_factor * production_qty_uom - qty
- if remaining_qty: # In case you need to make more than planned
+ if not float_is_zero(remaining_qty, precision_rounding=precision):
+ # In case you need to make more than planned
#consumed more in wizard than previously planned
extra_move_id = stock_mov_obj.copy(cr, uid, produce_product.id, default={'product_uom_qty': remaining_qty,
'production_id': production_id}, context=context)
@@ -1000,7 +1002,7 @@ class mrp_production(osv.osv):
stock_mov_obj.action_consume(cr, uid, [raw_material_line.id], consumed_qty, raw_material_line.location_id.id,
restrict_lot_id=consume['lot_id'], consumed_for=main_production_move, context=context)
remaining_qty -= consumed_qty
- if remaining_qty:
+ if not float_is_zero(remaining_qty, precision_rounding=precision):
#consumed more in wizard than previously planned
product = self.pool.get('product.product').browse(cr, uid, consume['product_id'], context=context)
extra_move_id = self._make_consume_line_from_data(cr, uid, production, product, product.uom_id.id, remaining_qty, False, 0, context=context)
diff --git a/addons/mrp/procurement.py b/addons/mrp/procurement.py
index 7969a7f724a..8eb61d6c5cc 100644
--- a/addons/mrp/procurement.py
+++ b/addons/mrp/procurement.py
@@ -117,7 +117,7 @@ class procurement_order(osv.osv):
if self.check_bom_exists(cr, uid, [procurement.id], context=context):
#create the MO as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
vals = self._prepare_mo_vals(cr, uid, procurement, context=context)
- produce_id = production_obj.create(cr, SUPERUSER_ID, vals, context=context)
+ produce_id = production_obj.create(cr, SUPERUSER_ID, vals, context=dict(context, force_company=procurement.company_id.id))
res[procurement.id] = produce_id
self.write(cr, uid, [procurement.id], {'production_id': produce_id})
self.production_order_create_note(cr, uid, procurement, context=context)
diff --git a/addons/payment_adyen/models/adyen.py b/addons/payment_adyen/models/adyen.py
index 7a995809222..52336cb48d9 100644
--- a/addons/payment_adyen/models/adyen.py
+++ b/addons/payment_adyen/models/adyen.py
@@ -28,7 +28,7 @@ class AcquirerAdyen(osv.Model):
- yhpp: hosted payment page: pay.shtml for single, select.shtml for multiple
"""
return {
- 'adyen_form_url': 'https://%s.adyen.com/hpp/pay.shtml' % environment,
+ 'adyen_form_url': 'https://%s.adyen.com/hpp/pay.shtml' % 'live' if environment == 'prod' else environment,
}
def _get_providers(self, cr, uid, context=None):
diff --git a/addons/point_of_sale/report/pos_details.py b/addons/point_of_sale/report/pos_details.py
index fe57b0042a8..bb784b98a45 100644
--- a/addons/point_of_sale/report/pos_details.py
+++ b/addons/point_of_sale/report/pos_details.py
@@ -129,13 +129,7 @@ class pos_details(report_sxw.rml_parse):
return {}
def _total_of_the_day(self, objects):
- if self.total:
- if self.total == self.total_invoiced:
- return self.total
- else:
- return ((self.total or 0.00) - (self.total_invoiced or 0.00))
- else:
- return False
+ return self.total or 0.00
def _sum_invoice(self, objects):
return reduce(lambda acc, obj:
diff --git a/addons/point_of_sale/static/src/js/db.js b/addons/point_of_sale/static/src/js/db.js
index 2729d7118e9..83c5a7be2f5 100644
--- a/addons/point_of_sale/static/src/js/db.js
+++ b/addons/point_of_sale/static/src/js/db.js
@@ -170,7 +170,7 @@ openerp.point_of_sale.load_db = function load_db(instance, module){
for (var i = 0; i < packagings.length; i++) {
str += '|' + packagings[i].barcode;
}
- str = product.id + ':' + str.replace(':','') + '\n';
+ str = product.id + ':' + str.replace(/:/g,'') + '\n';
return str;
},
add_products: function(products){
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index 1b149a08069..fa9c4d5ce8d 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -1196,18 +1196,19 @@ class procurement_order(osv.osv):
uom_obj = self.pool.get("product.uom")
for procurement in self.browse(cr, uid, ids, context=context):
if procurement.rule_id.action == 'buy' and procurement.purchase_line_id:
- uom = procurement.purchase_line_id.product_uom
- product_qty = uom_obj._compute_qty_obj(cr, uid, procurement.product_uom, procurement.product_qty, uom, context=context)
if procurement.purchase_line_id.state not in ('draft', 'cancel'):
raise UserError(
_('Can not cancel this procurement like this as the related purchase order has been confirmed already. Please cancel the purchase order first. '))
- if float_compare(procurement.purchase_line_id.product_qty, product_qty, 0, precision_rounding=uom.rounding) > 0:
- purchase_line_obj.write(cr, uid, [procurement.purchase_line_id.id], {'product_qty': procurement.purchase_line_id.product_qty - product_qty}, context=context)
- else:
+
+ new_qty, new_price = self._calc_new_qty_price(cr, uid, procurement, cancel=True, context=context)
+ if new_qty != procurement.purchase_line_id.product_qty:
+ purchase_line_obj.write(cr, uid, [procurement.purchase_line_id.id], {'product_qty': new_qty, 'price_unit': new_price}, context=context)
+ if float_compare(new_qty, 0.0, precision_rounding=procurement.product_uom.rounding) != 1:
if procurement.purchase_line_id.id not in lines_to_cancel:
lines_to_cancel += [procurement.purchase_line_id.id]
if lines_to_cancel:
purchase_line_obj.action_cancel(cr, uid, lines_to_cancel, context=context)
+ purchase_line_obj.unlink(cr, uid, lines_to_cancel, context=context)
return super(procurement_order, self).propagate_cancels(cr, uid, ids, context=context)
def _run(self, cr, uid, procurement, context=None):
@@ -1371,6 +1372,41 @@ class procurement_order(osv.osv):
res[procurement.id] = values
return res
+ def _calc_new_qty_price(self, cr, uid, procurement, po_line=None, cancel=False, context=None):
+ if not po_line:
+ po_line = procurement.purchase_line_id
+
+ uom_obj = self.pool.get('product.uom')
+ qty = uom_obj._compute_qty(cr, uid, procurement.product_uom.id, procurement.product_qty,
+ procurement.product_id.uom_po_id.id)
+ if cancel:
+ qty = -qty
+
+ # Make sure we use the minimum quantity of the partner corresponding to the PO
+ if po_line.product_id.seller_id.id == po_line.order_id.partner_id.id:
+ supplierinfo_min_qty = po_line.product_id.seller_qty
+ else:
+ supplierinfo_obj = self.pool.get('product.supplierinfo')
+ supplierinfo_ids = supplierinfo_obj.search(cr, uid, [('name', '=', po_line.order_id.partner_id.id), ('product_tmpl_id', '=', po_line.product_id.product_tmpl_id.id)])
+ supplierinfo_min_qty = supplierinfo_obj.browse(cr, uid, supplierinfo_ids).min_qty
+
+ if supplierinfo_min_qty == 0.0:
+ qty += po_line.product_qty
+ else:
+ # Recompute quantity by adding existing running procurements.
+ for proc in po_line.procurement_ids:
+ qty += uom_obj._compute_qty(cr, uid, proc.product_uom.id, proc.product_qty,
+ proc.product_id.uom_po_id.id) if proc.state == 'running' else 0.0
+ qty = max(qty, supplierinfo_min_qty) if qty > 0.0 else 0.0
+
+ price = po_line.price_unit
+ if qty != po_line.product_qty:
+ pricelist_obj = self.pool.get('product.pricelist')
+ pricelist_id = po_line.order_id.partner_id.property_product_pricelist_purchase.id
+ price = pricelist_obj.price_get(cr, uid, [pricelist_id], procurement.product_id.id, qty, po_line.order_id.partner_id.id, {'uom': procurement.product_uom.id})[pricelist_id]
+
+ return qty, price
+
def _get_grouping_dicts(self, cr, uid, ids, context=None):
"""
It will group the procurements according to the pos they should go into. That way, lines going to the same
@@ -1455,8 +1491,11 @@ class procurement_order(osv.osv):
for proc in procurements:
if po_prod_dict.get(proc.product_id.id):
po_line = po_prod_dict[proc.product_id.id]
- uom_id = po_line.product_uom #Convert to UoM of existing line
+ # FIXME: compute quantity using `_calc_new_qty_price` method.
+ # new_qty, new_price = self._calc_new_qty_price(cr, uid, proc, po_line=po_line, context=context)
+ uom_id = po_line.product_uom # Convert to UoM of existing line
qty = uom_obj._compute_qty_obj(cr, uid, proc.product_uom, proc.product_qty, uom_id)
+
if lines_to_update.get(po_line):
lines_to_update[po_line] += [(proc.id, qty)]
else:
diff --git a/addons/sale/test/manual_order_policy.yml b/addons/sale/test/manual_order_policy.yml
index d2a1ff0195f..1bb1f1d4415 100644
--- a/addons/sale/test/manual_order_policy.yml
+++ b/addons/sale/test/manual_order_policy.yml
@@ -7,7 +7,7 @@
-
!python {model: sale.order}: |
sale_order = self.browse(cr, uid, ref("sale_order_2"))
- assert len(sale_order.invoice_ids) == False, "Invoice should not created."
+ assert len(sale_order.invoice_ids) == 0, "Invoice should not created."
-
I create advance invoice where type is 'Fixed Price'.
-
diff --git a/addons/sale_layout/security/ir.model.access.csv b/addons/sale_layout/security/ir.model.access.csv
index 10a5f7b06b0..9ec03a6e378 100644
--- a/addons/sale_layout/security/ir.model.access.csv
+++ b/addons/sale_layout/security/ir.model.access.csv
@@ -1,6 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
report_layout_category_1,report_layout_category_1,model_sale_layout_category,base.group_sale_manager,1,1,1,1
report_layout_category_2,report_layout_category_2,model_sale_layout_category,account.group_account_manager,1,1,1,1
-report_layout_category_3,report_layout_category_3,model_sale_layout_category,base.group_sale_salesman,1,1,1,O
+report_layout_category_3,report_layout_category_3,model_sale_layout_category,base.group_sale_salesman,1,1,1,0
report_layout_category_4,report_layout_category_4,model_sale_layout_category,base.group_sale_salesman_all_leads,1,1,1,0
report_layout_category_5,report_layout_category_5,model_sale_layout_category,account.group_account_invoice,1,0,0,0
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index 13de717ce36..5fbfe6fedec 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -1627,7 +1627,7 @@ class stock_production_lot(osv.osv):
# Move
# ----------------------------------------------------
-class stock_move(models.Model):
+class stock_move(osv.osv):
_name = "stock.move"
_description = "Stock Move"
_order = 'picking_id, sequence, id'
@@ -1648,11 +1648,12 @@ class stock_move(models.Model):
res.append((line.id, name))
return res
- @api.depends('product_id', 'product_uom_qty', 'product_uom', 'product_id.uom_id')
- def _quantity_normalize(self):
+ def _quantity_normalize(self, cr, uid, ids, name, args, context=None):
uom_obj = self.pool.get('product.uom')
- for m in self:
- m.product_qty = uom_obj._compute_qty_obj(self.env.cr, self.env.uid, m.product_uom, m.product_uom_qty, m.product_id.uom_id, context=self.env.context)
+ res = {}
+ for m in self.browse(cr, uid, ids, context=context):
+ res[m.id] = uom_obj._compute_qty_obj(cr, uid, m.product_uom, m.product_uom_qty, m.product_id.uom_id, context=context)
+ return res
def _get_remaining_qty(self, cr, uid, ids, field_name, args, context=None):
uom_obj = self.pool.get('product.uom')
@@ -1736,6 +1737,11 @@ class stock_move(models.Model):
res += [x.id for x in picking.move_lines]
return res
+ def _get_moves_from_prod(self, cr, uid, ids, context=None):
+ if ids:
+ return self.pool.get('stock.move').search(cr, uid, [('product_id', 'in', ids)], context=context)
+ return []
+
def _set_product_qty(self, cr, uid, id, field, value, arg, context=None):
""" The meaning of product_qty field changed lately and is now a functional field computing the quantity
in the default product UoM. This code has been added to raise an error if a write is made given a value
@@ -1744,9 +1750,6 @@ class stock_move(models.Model):
"""
raise UserError(_('The requested operation cannot be processed because of a programming error setting the `product_qty` field instead of the `product_uom_qty`.'))
- product_qty = new_fields.Float(compute='_quantity_normalize', inverse='_set_product_qty', digits=0, store=True, string='Quantity',
- help='Quantity in the default UoM of the product')
-
_columns = {
'sequence': fields.integer('Sequence'),
'name': fields.char('Description', required=True, select=True),
@@ -1755,6 +1758,8 @@ class stock_move(models.Model):
'date': fields.datetime('Date', required=True, select=True, help="Move date: scheduled date until move is done, then date of actual move processing", states={'done': [('readonly', True)]}),
'date_expected': fields.datetime('Expected Date', states={'done': [('readonly', True)]}, required=True, select=True, help="Scheduled date for the processing of this move"),
'product_id': fields.many2one('product.product', 'Product', required=True, select=True, domain=[('type', '<>', 'service')], states={'done': [('readonly', True)]}),
+ 'product_qty': fields.function(_quantity_normalize, fnct_inv=_set_product_qty, type='float', digits=0, store=True, string='Quantity',
+ help='Quantity in the default UoM of the product'),
'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'),
required=True, states={'done': [('readonly', True)]},
help="This is the quantity of products from an inventory "
@@ -2759,6 +2764,36 @@ class stock_inventory(osv.osv):
vals.append(product_line)
return vals
+ def _check_filter_product(self, cr, uid, ids, context=None):
+ for inventory in self.browse(cr, uid, ids, context=context):
+ if inventory.filter == 'none' and inventory.product_id and inventory.location_id and inventory.lot_id:
+ return True
+ if inventory.filter not in ('product', 'product_owner') and inventory.product_id:
+ return False
+ if inventory.filter != 'lot' and inventory.lot_id:
+ return False
+ if inventory.filter not in ('owner', 'product_owner') and inventory.partner_id:
+ return False
+ if inventory.filter != 'pack' and inventory.package_id:
+ return False
+ return True
+
+ def onchange_filter(self, cr, uid, ids, filter, context=None):
+ to_clean = { 'value': {} }
+ if filter not in ('product', 'product_owner'):
+ to_clean['value']['product_id'] = False
+ if filter != 'lot':
+ to_clean['value']['lot_id'] = False
+ if filter not in ('owner', 'product_owner'):
+ to_clean['value']['partner_id'] = False
+ if filter != 'pack':
+ to_clean['value']['package_id'] = False
+ return to_clean
+
+ _constraints = [
+ (_check_filter_product, 'The selected inventory options are not coherent.',
+ ['filter', 'product_id', 'lot_id', 'partner_id', 'package_id']),
+ ]
class stock_inventory_line(osv.osv):
_name = "stock.inventory.line"
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index 9d751828373..36de197aff8 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -107,7 +107,7 @@
<group>
<group>
<field name="location_id" domain="[('usage','not in', ['supplier','production'])]"/>
- <field name="filter" string="Inventory of" widget='radio' attrs="{'readonly': [('state', '!=', 'draft')]}"/>
+ <field name="filter" string="Inventory of" widget='radio' attrs="{'readonly': [('state', '!=', 'draft')]}" on_change="onchange_filter(filter)"/>
</group>
<group>
<field name="date"/>
diff --git a/addons/stock/test/inventory.yml b/addons/stock/test/inventory.yml
index a0b0f58512b..4bfc70d3dd1 100644
--- a/addons/stock/test/inventory.yml
+++ b/addons/stock/test/inventory.yml
@@ -9,6 +9,7 @@
-
!record {model: stock.inventory, id: inventory_test0}:
name: Test
+ filter: product
product_id: inventory_product
-
I confirm the inventory and check that my inventory has no line, as the product is a new one.
@@ -60,6 +61,7 @@
-
!record {model: stock.inventory, id: inventory_test1}:
name: second test inventory
+ filter: product
product_id: inventory_product
-
I confirm the inventory and check that my inventory has one line, with a quantity of 10.
diff --git a/addons/stock/wizard/stock_change_product_qty.py b/addons/stock/wizard/stock_change_product_qty.py
index d541338ed2e..435cf85c82b 100644
--- a/addons/stock/wizard/stock_change_product_qty.py
+++ b/addons/stock/wizard/stock_change_product_qty.py
@@ -94,8 +94,15 @@ class stock_change_product_qty(osv.osv_memory):
ctx = context.copy()
ctx['location'] = data.location_id.id
ctx['lot_id'] = data.lot_id.id
+ if data.product_id.id and data.lot_id.id:
+ filter = 'none'
+ elif data.product_id.id:
+ filter = 'product'
+ else:
+ filter = 'none'
inventory_id = inventory_obj.create(cr, uid, {
'name': _('INV: %s') % tools.ustr(data.product_id.name),
+ 'filter': filter,
'product_id': data.product_id.id,
'location_id': data.location_id.id,
'lot_id': data.lot_id.id}, context=context)
diff --git a/addons/stock_account/stock.py b/addons/stock_account/stock.py
index 392bc3ff2f3..252ef04c051 100644
--- a/addons/stock_account/stock.py
+++ b/addons/stock_account/stock.py
@@ -151,6 +151,13 @@ class stock_move(osv.osv):
if move.product_uos:
uos_id = move.product_uos.id
quantity = move.product_uos_qty
+
+ taxes_ids = []
+ if move.origin_returned_move_id.purchase_line_id.taxes_id:
+ taxes_ids = [tax.id for tax in move.origin_returned_move_id.purchase_line_id.taxes_id]
+ elif move.procurement_id.sale_line_id.tax_id:
+ taxes_ids = [tax.id for tax in move.procurement_id.sale_line_id.tax_id]
+
return {
'name': move.name,
'account_id': account_id,
@@ -158,6 +165,7 @@ class stock_move(osv.osv):
'uos_id': uos_id,
'quantity': quantity,
'price_unit': self._get_price_unit_invoice(cr, uid, move, inv_type),
+ 'invoice_line_tax_id': [(6, 0, taxes_ids)],
'discount': 0.0,
'account_analytic_id': False,
}
@@ -292,6 +300,7 @@ class stock_picking(osv.osv):
move_obj = self.pool.get('stock.move')
invoices = {}
is_extra_move, extra_move_tax = move_obj._get_moves_taxes(cr, uid, moves, context=context)
+ product_price_unit = {}
for move in moves:
company = move.company_id
origin = move.picking_id.name
@@ -317,6 +326,10 @@ class stock_picking(osv.osv):
invoice_line_vals = move_obj._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context)
invoice_line_vals['invoice_id'] = invoices[key]
invoice_line_vals['origin'] = origin
+ if not is_extra_move[move.id]:
+ product_price_unit[invoice_line_vals['product_id']] = invoice_line_vals['price_unit']
+ if is_extra_move[move.id] and invoice_line_vals['product_id'] in product_price_unit:
+ invoice_line_vals['price_unit'] = product_price_unit[invoice_line_vals['product_id']]
if is_extra_move[move.id] and extra_move_tax[move.picking_id, move.product_id]:
invoice_line_vals['invoice_line_tax_id'] = extra_move_tax[move.picking_id, move.product_id]
diff --git a/addons/stock_dropshipping/stock_dropshipping.py b/addons/stock_dropshipping/stock_dropshipping.py
index cbd079263ca..2fe37ea8565 100644
--- a/addons/stock_dropshipping/stock_dropshipping.py
+++ b/addons/stock_dropshipping/stock_dropshipping.py
@@ -6,16 +6,17 @@ from openerp import models, api
class sale_order_line(models.Model):
_inherit = 'sale.order.line'
- @api.one
+ @api.multi
def _check_routing(self, product, warehouse):
""" skip stock verification if the route goes from supplier to customer
As the product never goes in stock, no need to verify it's availibility
"""
res = super(sale_order_line, self)._check_routing(product, warehouse)
if not res:
- for pull_rule in self.route_id.pull_ids:
- if (pull_rule.picking_type_id.default_location_src_id.usage == 'supplier' and
- pull_rule.picking_type_id.default_location_dest_id.usage == 'customer'):
- res = True
- break
+ for line in self:
+ for pull_rule in line.route_id.pull_ids:
+ if (pull_rule.picking_type_id.default_location_src_id.usage == 'supplier' and
+ pull_rule.picking_type_id.default_location_dest_id.usage == 'customer'):
+ res = True
+ break
return res
diff --git a/addons/stock_dropshipping/test/crossdock.yml b/addons/stock_dropshipping/test/crossdock.yml
index 5ad1f1db187..2c79a1a3754 100644
--- a/addons/stock_dropshipping/test/crossdock.yml
+++ b/addons/stock_dropshipping/test/crossdock.yml
@@ -1,42 +1,58 @@
--
- Create new product without any routes
--
- !record {model: product.product, id: cross_shop_product}:
- name: PCE
- type: product
- categ_id: product.product_category_1
- list_price: 100.0
- standard_price: 70.0
- seller_ids:
- - delay: 1
- name: base.res_partner_2
- min_qty: 2.0
- qty: 5.0
- type: product
- uom_id: product.product_uom_unit
- uom_po_id: product.product_uom_unit
--
- Create a sales order with a line of 100 PCE incoming shipment, with route_id crossdock shipping.
--
- !record {model: sale.order, id: sale_order_crossdock_shpng}:
- partner_id: base.res_partner_4
- note: Create Sales order
- order_line:
- - product_id: cross_shop_product
- product_uom_qty: 100.00
+-
+ Create a supplier.
+-
+ !record {model: res.partner, id: supplier_crossdock}:
+ name: Crossdocking supplier
+-
+ Use the warehouse created in cancellation_propagated.yml.
+-
+ !python {model: stock.warehouse, id: wh_pps}: |
+ assert self.crossdock_route_id.active, 'Crossdock route is not active'
+-
+ Create new product without any routes.
+-
+ !record {model: product.product, id: cross_shop_product}:
+ name: PCE
+ type: product
+ categ_id: product.product_category_1
+ list_price: 100.0
+ standard_price: 70.0
+ seller_ids:
+ - delay: 1
+ name: supplier_crossdock
+ min_qty: 2.0
+ qty: 5.0
+ type: product
+ uom_id: product.product_uom_unit
+ uom_po_id: product.product_uom_unit
+-
+ Create a sales order with a line of 100 PCE incoming shipment, with route_id crossdock shipping.
+-
+ !record {model: sale.order, id: sale_order_crossdock_shpng}:
+ partner_id: base.res_partner_4
+ warehouse_id: wh_pps
+ note: Create Sales order
+ order_line:
+ - product_id: cross_shop_product
+ product_uom_qty: 100.00
-
!python {model: sale.order.line}: |
- route_warehouse0_crossdock = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).crossdock_route_id.id
+ route_wh_pps_crossdock = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock_dropshipping.wh_pps')).crossdock_route_id.id
order = self.pool.get('sale.order').browse(cr, uid, ref('sale_order_crossdock_shpng'))
line_ids = [x.id for x in order.order_line]
- self.write(cr, uid, line_ids, {'route_id': route_warehouse0_crossdock})
--
- Confirm sales order
--
- !workflow {model: sale.order, action: order_confirm, ref: sale_order_crossdock_shpng}
--
- Check a quotation was created to a certain supplier and confirm so it becomes a confirmed purchase order
+ self.write(cr, uid, line_ids, {'route_id': route_wh_pps_crossdock})
+-
+ Confirm sales order.
+-
+ !workflow {model: sale.order, action: order_confirm, ref: sale_order_crossdock_shpng}
+-
+ Run the scheduler.
+-
+ !python {model: procurement.order}: |
+ self.run_scheduler(cr, uid)
+-
+ Check a quotation was created for the created supplier and confirm it.
-
!python {model: purchase.order}: |
- po_id = self.search(cr, uid, [('partner_id', '=', ref('base.res_partner_2'))])
+ po_id = self.search(cr, uid, [('partner_id', '=', ref('supplier_crossdock')), ('state','=','draft')])
self.wkf_confirm_order(cr, uid, po_id)
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index 893caf3040d..663a6adde09 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -451,7 +451,7 @@ def content_disposition(filename):
if browser == 'msie' and version < 9:
return "attachment; filename=%s" % escaped
elif browser == 'safari':
- return "attachment; filename=%s" % filename
+ return "attachment; filename=\"%s\"" % filename
else:
return "attachment; filename*=UTF-8''%s" % escaped
diff --git a/addons/website_membership/__init__.py b/addons/website_membership/__init__.py
index ee5959455ad..9f86759e32b 100644
--- a/addons/website_membership/__init__.py
+++ b/addons/website_membership/__init__.py
@@ -1 +1,2 @@
import controllers
+import models
diff --git a/addons/website_membership/controllers/main.py b/addons/website_membership/controllers/main.py
index beedd50a04f..1df80c36c71 100644
--- a/addons/website_membership/controllers/main.py
+++ b/addons/website_membership/controllers/main.py
@@ -27,7 +27,7 @@ class WebsiteMembership(http.Controller):
'/members/association/<membership_id>/country/<country_name>-<int:country_id>/page/<int:page>',
'/members/association/<membership_id>/country/<int:country_id>/page/<int:page>',
], type='http', auth="public", website=True)
- def members(self, membership_id=None, country_name=None, country_id=0, page=0, **post):
+ def members(self, membership_id=None, country_name=None, country_id=0, page=1, **post):
cr, uid, context = request.cr, request.uid, request.context
product_obj = request.registry['product.product']
country_obj = request.registry['res.country']
@@ -56,7 +56,7 @@ class WebsiteMembership(http.Controller):
membership_line_ids = []
country_domain = ('free_member', '=', True)
countries = partner_obj.read_group(
- cr, uid, [country_domain, ("website_published", "=", True)], ["id", "country_id"],
+ cr, SUPERUSER_ID, [country_domain, ("website_published", "=", True)], ["id", "country_id"],
groupby="country_id", orderby="country_id", context=request.context)
countries_total = sum(country_dict['country_id_count'] for country_dict in countries)
@@ -64,11 +64,12 @@ class WebsiteMembership(http.Controller):
if country_id:
line_domain.append(('partner.country_id', '=', country_id))
current_country = country_obj.read(cr, uid, country_id, ['id', 'name'], context)
- if not any(x['country_id'][0] == country_id for x in countries):
+ if not any(x['country_id'][0] == country_id for x in countries if x['country_id']):
countries.append({
'country_id_count': 0,
'country_id': (country_id, current_country["name"])
})
+ countries = filter(lambda d:d['country_id'], countries)
countries.sort(key=lambda d: d['country_id'][1])
countries.insert(0, {
@@ -82,15 +83,32 @@ class WebsiteMembership(http.Controller):
# make sure we don't access to lines with unpublished membershipts
line_domain.append(('membership_id', 'in', membership_ids))
- # displayed membership lines
+ limit = self._references_per_page
+ offset = limit * (page - 1)
+
+ count_members = 0
+ membership_line_ids = []
+ # displayed non-free membership lines
if membership_id != 'free':
- membership_line_ids = membership_line_obj.search(cr, SUPERUSER_ID, line_domain, context=context)
+ count_members = membership_line_obj.search_count(cr, SUPERUSER_ID, line_domain, context=context)
+ if offset <= count_members:
+ membership_line_ids = tuple(membership_line_obj.search(cr, SUPERUSER_ID, line_domain, offset, limit, context=context))
membership_lines = membership_line_obj.browse(cr, uid, membership_line_ids, context=context)
# TODO: Following line can be deleted in master. Kept for retrocompatibility.
membership_lines = sorted(membership_lines, key=lambda x: x.membership_id.website_sequence)
- partner_ids = [m.partner.id for m in membership_lines]
- free_partner_ids = partner_obj.search(cr, SUPERUSER_ID, [('free_member', '=', True), ('website_published', '=', True)], context=context)
- google_map_partner_ids = ",".join(map(str, partner_ids))
+ page_partner_ids = set(m.partner.id for m in membership_lines)
+
+ google_map_partner_ids = []
+ if request.env.ref('website_membership.opt_index_google_map').customize_show:
+ membership_lines_ids = membership_line_obj.search(cr, uid, line_domain, context=context)
+ google_map_partner_ids = membership_line_obj.get_published_companies(cr, uid, membership_line_ids, limit=2000, context=context)
+
+ search_domain = [('free_member', '=', True), ('website_published', '=', True)]
+ if post_name:
+ search_domain += ['|', ('name', 'ilike', post_name), ('website_description', 'ilike', post_name)]
+ if country_id:
+ search_domain += [('country_id', '=', country_id)]
+ free_partner_ids = partner_obj.search(cr, SUPERUSER_ID, search_domain, context=context)
memberships_data = []
for membership_record in memberships:
memberships_data.append({'id': membership_record.id, 'name': membership_record.name})
@@ -100,12 +118,23 @@ class WebsiteMembership(http.Controller):
if free_partner_ids:
memberships_data.append({'id': 'free', 'name': _('Free Members')})
if not membership_id or membership_id == 'free':
- memberships_partner_ids['free'] = free_partner_ids
+ if count_members < offset + limit:
+ free_start = max(offset - count_members, 0)
+ free_end = max(offset + limit - count_members, 0)
+ memberships_partner_ids['free'] = free_partner_ids[free_start:free_end]
+ page_partner_ids |= set(memberships_partner_ids['free'])
+ google_map_partner_ids += free_partner_ids[:2000-len(google_map_partner_ids)]
+ count_members += len(free_partner_ids)
+
+ google_map_partner_ids = ",".join(map(str, google_map_partner_ids))
+
+ partners = { p.id: p for p in partner_obj.browse(request.cr, SUPERUSER_ID, list(page_partner_ids), request.context)}
- partners = dict((p.id, p) for p in partner_obj.browse(request.cr, SUPERUSER_ID, list(set(partner_ids + free_partner_ids)), request.context))
+ base_url = '/members%s%s' % ('/association/%s' % membership_id if membership_id else '',
+ '/country/%s' % country_id if country_id else '')
# request pager for lines
- pager = request.website.pager(url="/members", total=len(membership_line_ids), page=page, step=self._references_per_page, scope=7, url_args=post)
+ pager = request.website.pager(url=base_url, total=count_members, page=page, step=limit, scope=7, url_args=post)
values = {
'partners': partners,
diff --git a/addons/website_membership/models/__init__.py b/addons/website_membership/models/__init__.py
new file mode 100644
index 00000000000..58702d876e4
--- /dev/null
+++ b/addons/website_membership/models/__init__.py
@@ -0,0 +1 @@
+import membership
diff --git a/addons/website_membership/models/membership.py b/addons/website_membership/models/membership.py
new file mode 100644
index 00000000000..feba5ce6ea6
--- /dev/null
+++ b/addons/website_membership/models/membership.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# 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/>.
+#
+##############################################################################
+
+from openerp.osv import osv, fields
+
+class membership_membership_line(osv.Model):
+ _inherit = 'membership.membership_line'
+
+ def get_published_companies(self, cr, uid, ids, limit=None, context=None):
+ if not ids:
+ return []
+ limit_clause = '' if limit is None else ' LIMIT %d' % limit
+ cr.execute('SELECT DISTINCT p.id \
+ FROM res_partner p INNER JOIN membership_membership_line m \
+ ON p.id = m.partner \
+ WHERE website_published AND is_company AND m.id IN %s ' + limit_clause, (tuple(ids),))
+ return [partner_id[0] for partner_id in cr.fetchall()]
diff --git a/addons/website_quote/models/order.py b/addons/website_quote/models/order.py
index 9090d68a68e..9be35a29fb1 100644
--- a/addons/website_quote/models/order.py
+++ b/addons/website_quote/models/order.py
@@ -279,6 +279,8 @@ class sale_quote_option(osv.osv):
'name': product_obj.name,
'uom_id': product_obj.product_tmpl_id.uom_id.id,
})
+ if product_obj.description_sale:
+ vals['name'] += '\n'+product_obj.description_sale
return {'value': vals}
class sale_order_option(osv.osv):
@@ -311,6 +313,8 @@ class sale_order_option(osv.osv):
'name': product_obj.name,
'uom_id': product_obj.product_tmpl_id.uom_id.id,
})
+ if product_obj.description_sale:
+ vals['name'] += '\n'+product_obj.description_sale
return {'value': vals}
class product_template(osv.Model):
diff --git a/addons/website_quote/static/src/css/website_quotation.css b/addons/website_quote/static/src/css/website_quotation.css
index c4bbd4d9c64..defa37feef5 100644
--- a/addons/website_quote/static/src/css/website_quotation.css
+++ b/addons/website_quote/static/src/css/website_quotation.css
@@ -42,6 +42,11 @@
padding-left: 30px;
font-size: 90%;
}
+@media (max-width: 991px) {
+ .o_website_quote .bs-sidebar {
+ position: static;
+ }
+}
@media (min-width: 992px) {
.o_website_quote .bs-sidebar .nav > .active > ul {
display: block;
diff --git a/addons/website_quote/views/website_quotation.xml b/addons/website_quote/views/website_quotation.xml
index c53df0bdd56..55b90835578 100644
--- a/addons/website_quote/views/website_quotation.xml
+++ b/addons/website_quote/views/website_quotation.xml
@@ -157,7 +157,7 @@
<template id="quotation_toolbar">
<div class="text-center hidden-print" t-if="quotation.state in ('draft', 'sent', 'waiting_date')">
- <a class="btn btn-success" data-toggle="modal" data-target="#modalaccept"
+ <a class="btn btn-success" data-toggle="modal" data-target="#modelaccept"
t-if="days_valid &gt;= 0">
<i class="fa fa-check"></i> Accept
</a>
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index 59dcbeb0dda..76d2e353e04 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -980,10 +980,13 @@ class website_sale(http.Controller):
return ret
@http.route(['/shop/get_unit_price'], type='json', auth="public", methods=['POST'], website=True)
- def get_unit_price(self, product_ids, add_qty, **kw):
+ def get_unit_price(self, product_ids, add_qty, use_order_pricelist=False, **kw):
cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
products = pool['product.product'].browse(cr, uid, product_ids, context=context)
partner = pool['res.users'].browse(cr, uid, uid, context=context).partner_id
- pricelist_id = request.session.get('sale_order_code_pricelist_id') or partner.property_product_pricelist.id
+ if use_order_pricelist:
+ pricelist_id = request.session.get('sale_order_code_pricelist_id') or partner.property_product_pricelist.id
+ else:
+ pricelist_id = partner.property_product_pricelist.id
prices = pool['product.pricelist'].price_rule_get_multi(cr, uid, [], [(product, add_qty, partner) for product in products], context=context)
return {product_id: prices[product_id][pricelist_id][0] for product_id in product_ids}
diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js
index 111a3c70ea4..97f3f3a7ac0 100644
--- a/addons/website_sale/static/src/js/website_sale.js
+++ b/addons/website_sale/static/src/js/website_sale.js
@@ -56,7 +56,8 @@ $('.oe_website_sale').each(function () {
if (isNaN(value)) value = 0;
openerp.jsonRpc("/shop/get_unit_price", 'call', {
'product_ids': product_ids,
- 'add_qty': value})
+ 'add_qty': value,
+ 'use_order_pricelist': true})
.then(function (res) {
//basic case
$dom.find('span.oe_currency_value').last().text(res[product_id].toFixed(2));