[MERGE] forward port of branch 8.0 up to e27744d

This commit is contained in:
Christophe Simonis
2015-03-18 14:05:02 +01:00
parent a757fce9d6
commit a329aa1949
35 changed files with 293 additions and 109 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
wiz = wizards.browse(cr, uid, ref('account.account_configuration_installer_todo'))
part = self.pool.get('res.partner').browse(cr, uid, ref('base.main_partner'))
# if we know the country and the wizard has not yet been executed, we do it
if (part.country_id.id) and (wiz.state=='open'):
if (part.country_id.id) and (part.country_id.code) and (wiz.state=='open'):
mod = 'l10n_'+part.country_id.code.lower()
ids = modules.search(cr, uid, [ ('name','=',mod) ], context=context)
if ids:
+1 -1
View File
@@ -8,7 +8,7 @@
<div class="col-xs-5 col-xs-offset-7">
<address t-field="o.partner_id"
t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' />
<span t-field="o.partner_id.vat"/>
<span t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></span>
</div>
</div>
+4 -5
View File
@@ -171,10 +171,6 @@ class account_asset_asset(osv.osv):
for x in range(len(posted_depreciation_line_ids), undone_dotation_number):
i = x + 1
amount = self._compute_board_amount(cr, uid, asset, i, residual_amount, amount_to_depr, undone_dotation_number, posted_depreciation_line_ids, total_days, depreciation_date, context=context)
company_currency = asset.company_id.currency_id.id
current_currency = asset.currency_id.id
# compute amount into company currency
amount = currency_obj.compute(cr, uid, current_currency, company_currency, amount, context=context)
residual_amount -= amount
vals = {
'amount': amount,
@@ -215,7 +211,10 @@ class account_asset_asset(osv.osv):
l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
res=dict(cr.fetchall())
for asset in self.browse(cr, uid, ids, context):
res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0) - asset.salvage_value
company_currency = asset.company_id.currency_id.id
current_currency = asset.currency_id.id
amount = self.pool['res.currency'].compute(cr, uid, company_currency, current_currency, res.get(asset.id, 0.0), context=context)
res[asset.id] = asset.purchase_value - amount - asset.salvage_value
for id in ids:
res.setdefault(id, 0.0)
return res
+3 -1
View File
@@ -27,8 +27,10 @@ from operator import itemgetter
class account_move_line(osv.osv):
_inherit = "account.move.line"
# delegate to parent, used for local fields.function redefinition
def _amount_residual(self, cr, uid, ids, field_names, args, context=None):
return self._amount_residual(self, cr, uid, ids, field_names, args, context=context)
return super(account_move_line, self)._amount_residual(
cr, uid, ids, field_names, args, context=context)
def _to_pay_search(self, cr, uid, obj, name, args, context=None):
if not args:
+12
View File
@@ -16,6 +16,18 @@
</field>
</record>
<!-- Force update of groups' user view.
When updating (not new install), as `share` module is not loaded yet, shared groups
are taken into account when getting the group list, resulting to a different
organisation of the `Sharing` category (booleans instead of a selection) and thus,
different virtual field names.
When applying then inheritance of the following view, the check of field names
failed due to these virtual field name missmatch.
This is only for databases updated between a6eac0f08ee9100d1a44a4068d4f580b1c7c4b2c
and the commit introducing this line.
-->
<function model="res.groups" name="update_user_groups_view" />
<!-- Update user form !-->
<record id="view_users_form_mail" model="ir.ui.view">
<field name="name">res.users.form.mail</field>
+20 -5
View File
@@ -1094,14 +1094,27 @@ class mrp_production(osv.osv):
#is 1 element long, so we can take the first.
return stock_move.action_confirm(cr, uid, [move_id], context=context)[0]
def _get_raw_material_procure_method(self, cr, uid, product, context=None):
'''This method returns the procure_method to use when creating the stock move for the production raw materials'''
def _get_raw_material_procure_method(self, cr, uid, product, location_id=False, location_dest_id=False, context=None):
'''This method returns the procure_method to use when creating the stock move for the production raw materials
Besides the standard configuration of looking if the product or product category has the MTO route,
you can also define a rule e.g. from Stock to Production (which might be used in the future like the sale orders)
'''
warehouse_obj = self.pool['stock.warehouse']
routes = product.route_ids + product.categ_id.total_route_ids
if location_id and location_dest_id:
pull_obj = self.pool['procurement.rule']
pulls = pull_obj.search(cr, uid, [('route_id', 'in', [x.id for x in routes]),
('location_id', '=', location_dest_id),
('location_src_id', '=', location_id)], limit=1, context=context)
if pulls:
return pull_obj.browse(cr, uid, pulls[0], context=context).procure_method
try:
mto_route = warehouse_obj._get_mto_route(cr, uid, context=context)
except:
return "make_to_stock"
routes = product.route_ids + product.categ_id.total_route_ids
if mto_route in [x.id for x in routes]:
return "make_to_order"
return "make_to_stock"
@@ -1133,7 +1146,8 @@ class mrp_production(osv.osv):
move = stock_move.copy(cr, uid, move_id, default = {
'location_id': source_location_id,
'location_dest_id': dest_location_id,
'procure_method': self._get_raw_material_procure_method(cr, uid, product, context=context),
'procure_method': self._get_raw_material_procure_method(cr, uid, product, location_id=source_location_id,
location_dest_id=dest_location_id, context=context),
'raw_material_production_id': False,
'move_dest_id': move_id,
'picking_type_id': types and types[0] or False,
@@ -1166,7 +1180,8 @@ class mrp_production(osv.osv):
'location_id': source_location_id,
'location_dest_id': destination_location_id,
'company_id': production.company_id.id,
'procure_method': prev_move and 'make_to_stock' or self._get_raw_material_procure_method(cr, uid, product, context=context), #Make_to_stock avoids creating procurement
'procure_method': prev_move and 'make_to_stock' or self._get_raw_material_procure_method(cr, uid, product, location_id=source_location_id,
location_dest_id=destination_location_id, context=context), #Make_to_stock avoids creating procurement
'raw_material_production_id': production.id,
#this saves us a browse in create()
'price_unit': product.standard_price,
+10 -5
View File
@@ -1128,11 +1128,16 @@ class pos_order(osv.osv):
if not grouped_data[key]:
grouped_data[key].append(values)
else:
current_value = grouped_data[key][0]
current_value['quantity'] = current_value.get('quantity', 0.0) + values.get('quantity', 0.0)
current_value['credit'] = current_value.get('credit', 0.0) + values.get('credit', 0.0)
current_value['debit'] = current_value.get('debit', 0.0) + values.get('debit', 0.0)
current_value['tax_amount'] = current_value.get('tax_amount', 0.0) + values.get('tax_amount', 0.0)
for line in grouped_data[key]:
if line.get('tax_code_id') == values.get('tax_code_id'):
current_value = line
current_value['quantity'] = current_value.get('quantity', 0.0) + values.get('quantity', 0.0)
current_value['credit'] = current_value.get('credit', 0.0) + values.get('credit', 0.0)
current_value['debit'] = current_value.get('debit', 0.0) + values.get('debit', 0.0)
current_value['tax_amount'] = current_value.get('tax_amount', 0.0) + values.get('tax_amount', 0.0)
break
else:
grouped_data[key].append(values)
else:
grouped_data[key].append(values)
+1
View File
@@ -35,6 +35,7 @@
var s = new openerp.init();
var wc = new s.web.WebClient();
wc._title_changed = function() {};
wc.show_application = function() {
wc.action_manager.do_action("pos.ui");
};
+11 -2
View File
@@ -791,6 +791,12 @@ class product_template(osv.osv):
return super(product_template, self).name_get(cr, user, ids, context)
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
# Only use the product.product heuristics if there is a search term and the domain
# does not specify a match on `product.template` IDs.
if not name or any(term[0] == 'id' for term in (args or [])):
return super(product_template, self).name_search(
cr, user, name=name, args=args, operator=operator, context=context, limit=limit)
product_product = self.pool['product.product']
results = product_product.name_search(
cr, user, name, args, operator=operator, context=context, limit=limit)
@@ -798,8 +804,11 @@ class product_template(osv.osv):
template_ids = [p.product_tmpl_id.id
for p in product_product.browse(
cr, user, product_ids, context=context)]
uniq_ids = OrderedDict.fromkeys(template_ids).keys()
return self.name_get(cr, user, uniq_ids, context=context)
# re-apply product.template order + name_get
return super(product_template, self).name_search(
cr, user, '', args=[('id', 'in', template_ids)],
operator=operator, context=context, limit=limit)
class product_product(osv.osv):
_name = "product.product"
+3 -3
View File
@@ -1019,9 +1019,9 @@ class purchase_order_line(osv.osv):
if line.state not in ['draft', 'cancel']:
raise UserError(_('Cannot delete a purchase order line which is in state \'%s\'.') %(line.state,))
procurement_obj = self.pool.get('procurement.order')
procurement_ids_to_cancel = procurement_obj.search(cr, uid, [('purchase_line_id', 'in', ids)], context=context)
if procurement_ids_to_cancel:
self.pool['procurement.order'].cancel(cr, uid, procurement_ids_to_cancel)
procurement_ids_to_except = procurement_obj.search(cr, uid, [('purchase_line_id', 'in', ids)], context=context)
if procurement_ids_to_except:
self.pool['procurement.order'].write(cr, uid, procurement_ids_to_except, {'state': 'exception'}, context=context)
return super(purchase_order_line, self).unlink(cr, uid, ids, context=context)
def onchange_product_uom(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id,
+1 -1
View File
@@ -550,7 +550,7 @@
<field name="name">On Purchase Order Lines</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">purchase.order.line</field>
<field name="domain">[('order_id.invoice_method','&lt;&gt;','picking'), ('state','in',('confirmed','done')), ('invoiced', '=', False)]</field>
<field name="domain">[('order_id.invoice_method','=','manual'), ('state','in',('confirmed','done')), ('invoiced', '=', False)]</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="purchase_order_line_search"/>
@@ -39,4 +39,4 @@
procurement = self.browse(cr, uid, ref('procurement_order_testcase0'))
procurement.purchase_line_id.unlink()
procurement.refresh()
assert procurement.state == 'cancel', 'Procurement should be cancelled'
assert procurement.state == 'exception', 'Procurement should be in exception'
+2
View File
@@ -578,11 +578,13 @@ class sale_order(osv.osv):
context = {}
sale_order_line_obj = self.pool.get('sale.order.line')
account_invoice_obj = self.pool.get('account.invoice')
procurement_obj = self.pool.get('procurement.order')
for sale in self.browse(cr, uid, ids, context=context):
for inv in sale.invoice_ids:
if inv.state not in ('draft', 'cancel'):
raise UserError(_('Cannot cancel this sales order!') + ':' + _('First cancel all invoices attached to this sales order.'))
inv.signal_workflow('invoice_cancel')
procurement_obj.cancel(cr, uid, sum([l.procurement_ids.ids for l in sale.order_line],[]))
sale_order_line_obj.write(cr, uid, [l.id for l in sale.order_line],
{'state': 'cancel'})
self.write(cr, uid, ids, {'state': 'cancel'})
-13
View File
@@ -155,19 +155,6 @@ class sale_order(osv.osv):
move_obj.write(cr, uid, [x.id for x in picking.move_lines], {'invoice_state': 'invoiced'}, context=context)
return res
def action_cancel(self, cr, uid, ids, context=None):
if context is None:
context = {}
sale_order_line_obj = self.pool.get('sale.order.line')
proc_obj = self.pool.get('procurement.order')
stock_obj = self.pool.get('stock.picking')
for sale in self.browse(cr, uid, ids, context=context):
for pick in sale.picking_ids:
if pick.state not in ('draft', 'cancel'):
raise UserError(_('Cannot cancel sales order!') + '\n' + _('You must first cancel all delivery order(s) attached to this sales order.'))
stock_obj.signal_workflow(cr, uid, [p.id for p in sale.picking_ids], 'button_cancel')
return super(sale_order, self).action_cancel(cr, uid, ids, context=context)
def action_wait(self, cr, uid, ids, context=None):
res = super(sale_order, self).action_wait(cr, uid, ids, context=context)
for o in self.browse(cr, uid, ids):
+1 -1
View File
@@ -11,7 +11,7 @@
<field name="name">Shared Group</field>
<field name="comment">
This group is a template group for shared users, giving basic access needs, the menus for instance.</field>
<field name="category_id" ref="module_category_share"/>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="share">1</field>
</record>
</data>
+1 -1
View File
@@ -186,7 +186,7 @@ class product_product(osv.osv):
operator = '=='
ids = []
if name == 'qty_available':
if name == 'qty_available' and (value != 0.0 or operator not in ('==', '>=', '<=')):
res.append(('id', 'in', self._search_qty_available(cr, uid, operator, value, context)))
else:
product_ids = self.search(cr, uid, [], context=context)
+24 -41
View File
@@ -2660,14 +2660,6 @@ class stock_inventory(osv.osv):
self.pool.get('stock.inventory.line').write(cr, uid, line_ids, {'product_qty': 0})
return True
def _inventory_line_hook(self, cr, uid, inventory_line, move_vals):
""" Creates a stock move from an inventory line
@param inventory_line:
@param move_vals:
@return:
"""
return self.pool.get('stock.move').create(cr, uid, move_vals)
def action_done(self, cr, uid, ids, context=None):
""" Finish the inventory
@return: True
@@ -2686,35 +2678,7 @@ class stock_inventory(osv.osv):
#as they will be moved to inventory loss, and other quants will be created to the encoded quant location. This is a normal behavior
#as quants cannot be reuse from inventory location (users can still manually move the products before/after the inventory if they want).
move_obj = self.pool.get('stock.move')
move_obj.action_done(cr, uid, [x.id for x in inv.move_ids], context=context)
def _create_stock_move(self, cr, uid, inventory, todo_line, context=None):
stock_move_obj = self.pool.get('stock.move')
product_obj = self.pool.get('product.product')
inventory_location_id = product_obj.browse(cr, uid, todo_line['product_id'], context=context).property_stock_inventory.id
vals = {
'name': _('INV:') + (inventory.name or ''),
'product_id': todo_line['product_id'],
'product_uom': todo_line['product_uom_id'],
'date': inventory.date,
'company_id': inventory.company_id.id,
'inventory_id': inventory.id,
'state': 'assigned',
'restrict_lot_id': todo_line.get('prod_lot_id'),
'restrict_partner_id': todo_line.get('partner_id'),
}
if todo_line['product_qty'] < 0:
#found more than expected
vals['location_id'] = inventory_location_id
vals['location_dest_id'] = todo_line['location_id']
vals['product_uom_qty'] = -todo_line['product_qty']
else:
#found less than expected
vals['location_id'] = todo_line['location_id']
vals['location_dest_id'] = inventory_location_id
vals['product_uom_qty'] = todo_line['product_qty']
return stock_move_obj.create(cr, uid, vals, context=context)
move_obj.action_done(cr, uid, [x.id for x in inv.move_ids if x.state != 'done'], context=context)
def action_check(self, cr, uid, ids, context=None):
""" Checks the inventory and computes the stock move to do
@@ -2728,13 +2692,14 @@ class stock_inventory(osv.osv):
stock_move_obj.unlink(cr, uid, move_ids, context=context)
for line in inventory.line_ids:
#compare the checked quantities on inventory lines to the theorical one
inventory_line_obj._resolve_inventory_line(cr, uid, line, context=context)
stock_move = inventory_line_obj._resolve_inventory_line(cr, uid, line, context=context)
def action_cancel_draft(self, cr, uid, ids, context=None):
""" Cancels the stock move and change inventory state to draft.
@return: True
"""
for inv in self.browse(cr, uid, ids, context=context):
self.write(cr, uid, [inv.id], {'line_ids': [(5,)]}, context=context)
self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in inv.move_ids], context=context)
self.write(cr, uid, [inv.id], {'state': 'draft'}, context=context)
return True
@@ -2862,7 +2827,7 @@ class stock_inventory_line(osv.osv):
def _get_quants(self, cr, uid, line, context=None):
quant_obj = self.pool["stock.quant"]
dom = [('company_id', '=', line.company_id.id), ('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id),
dom = [('company_id', '=', line.company_id.id), ('location_id', '=', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id),
('product_id','=', line.product_id.id), ('owner_id', '=', line.partner_id.id), ('package_id', '=', line.package_id.id)]
quants = quant_obj.search(cr, uid, dom, context=context)
return quants
@@ -2882,7 +2847,7 @@ class stock_inventory_line(osv.osv):
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
if not company_id:
company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
dom = [('company_id', '=', company_id), ('location_id', 'child_of', location_id), ('lot_id', '=', prod_lot_id),
dom = [('company_id', '=', company_id), ('location_id', '=', location_id), ('lot_id', '=', prod_lot_id),
('product_id','=', product_id), ('owner_id', '=', partner_id), ('package_id', '=', package_id)]
quants = quant_obj.search(cr, uid, dom, context=context)
th_qty = sum([x.qty for x in quant_obj.browse(cr, uid, quants, context=context)])
@@ -2893,6 +2858,7 @@ class stock_inventory_line(osv.osv):
def _resolve_inventory_line(self, cr, uid, inventory_line, context=None):
stock_move_obj = self.pool.get('stock.move')
quant_obj = self.pool.get('stock.quant')
diff = inventory_line.theoretical_qty - inventory_line.product_qty
if not diff:
return
@@ -2919,7 +2885,24 @@ class stock_inventory_line(osv.osv):
vals['location_id'] = inventory_line.location_id.id
vals['location_dest_id'] = inventory_location_id
vals['product_uom_qty'] = diff
return stock_move_obj.create(cr, uid, vals, context=context)
move_id = stock_move_obj.create(cr, uid, vals, context=context)
move = stock_move_obj.browse(cr, uid, move_id, context=context)
if diff > 0:
domain = [('qty', '>', 0.0), ('package_id', '=', inventory_line.package_id.id), ('lot_id', '=', inventory_line.prod_lot_id.id), ('location_id', '=', inventory_line.location_id.id)]
preferred_domain_list = [[('reservation_id', '=', False)], [('reservation_id.inventory_id', '!=', inventory_line.inventory_id.id)]]
quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, move.product_qty, domain=domain, prefered_domain_list=preferred_domain_list, restrict_partner_id=move.restrict_partner_id.id, context=context)
quant_obj.quants_reserve(cr, uid, quants, move, context=context)
elif inventory_line.package_id:
stock_move_obj.action_done(cr, uid, move_id, context=context)
quants = [x.id for x in move.quant_ids]
quant_obj.write(cr, uid, quants, {'package_id': inventory_line.package_id.id}, context=context)
res = quant_obj.search(cr, uid, [('qty', '<', 0.0), ('product_id', '=', move.product_id.id),
('location_id', '=', move.location_dest_id.id), ('package_id', '!=', False)], limit=1, context=context)
if res:
for quant in move.quant_ids:
if quant.location_id.id == move.location_dest_id.id: #To avoid we take a quant that was reconcile already
quant_obj._quant_reconcile_negative(cr, uid, quant, move, context=context)
return move_id
# Should be left out in next version
def restrict_change(self, cr, uid, ids, theoretical_qty, context=None):
+52
View File
@@ -1095,3 +1095,55 @@ class TestStockFlow(TestStockCommon):
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))
#--------------------------------------------------------
# TEST PARTIAL INVENTORY WITH PACKS and LOTS
#---------------------------------------------------------
packproduct = self.ProductObj.create({'name': 'Pack Product', 'uom_id': self.uom_unit.id, 'uom_po_id': self.uom_unit.id})
lotproduct = self.ProductObj.create({'name': 'Lot Product', 'uom_id': self.uom_unit.id, 'uom_po_id': self.uom_unit.id})
inventory = self.InvObj.create({'name': 'Test Partial and Pack',
'filter': 'partial',
'location_id': self.stock_location})
inventory.prepare_inventory()
pack_obj = self.env['stock.quant.package']
lot_obj = self.env['stock.production.lot']
pack1 = pack_obj.create({'name': 'PACK00TEST1'})
pack2 = pack_obj.create({'name': 'PACK00TEST2'})
lot1 = lot_obj.create({'name': 'Lot001', 'product_id': lotproduct.id})
move = self.MoveObj.search([('product_id', '=', productKG.id), ('inventory_id', '=', inventory.id)], limit=1)
self.assertEqual(len(move), 0, "Partial filter should not create a lines upon prepare")
line_vals = []
line_vals += [{'location_id': self.stock_location, 'product_id': packproduct.id, 'product_qty': 10, 'product_uom_id': packproduct.uom_id.id}]
line_vals += [{'location_id': self.stock_location, 'product_id': packproduct.id, 'product_qty': 20, 'product_uom_id': packproduct.uom_id.id, 'package_id': pack1.id}]
line_vals += [{'location_id': self.stock_location, 'product_id': lotproduct.id, 'product_qty': 30, 'product_uom_id': lotproduct.uom_id.id, 'prod_lot_id': lot1.id}]
line_vals += [{'location_id': self.stock_location, 'product_id': lotproduct.id, 'product_qty': 25, 'product_uom_id': lotproduct.uom_id.id, 'prod_lot_id': False}]
inventory.write({'line_ids': [(0, 0, x) for x in line_vals]})
inventory.action_done()
self.assertEqual(packproduct.qty_available, 30, "Wrong qty available for packproduct")
self.assertEqual(lotproduct.qty_available, 55, "Wrong qty available for lotproduct")
quants = self.StockQuantObj.search([('product_id', '=', packproduct.id), ('location_id', '=', self.stock_location), ('package_id', '=', pack1.id)])
total_qty = sum([quant.qty for quant in quants])
self.assertEqual(total_qty, 20, 'Expecting 20 units on package 1 of packproduct, but we got %.4f on location stock!' % (total_qty))
#Create an inventory that will put the lots without lot to 0 and check that taking without pack will not take it from the pack
inventory2 = self.InvObj.create({'name': 'Test Partial Lot and Pack2',
'filter': 'partial',
'location_id': self.stock_location})
inventory2.prepare_inventory()
line_vals = []
line_vals += [{'location_id': self.stock_location, 'product_id': packproduct.id, 'product_qty': 20, 'product_uom_id': packproduct.uom_id.id}]
line_vals += [{'location_id': self.stock_location, 'product_id': lotproduct.id, 'product_qty': 0, 'product_uom_id': lotproduct.uom_id.id, 'prod_lot_id': False}]
line_vals += [{'location_id': self.stock_location, 'product_id': lotproduct.id, 'product_qty': 10, 'product_uom_id': lotproduct.uom_id.id, 'prod_lot_id': lot1.id}]
inventory2.write({'line_ids': [(0, 0, x) for x in line_vals]})
inventory2.action_done()
self.assertEqual(packproduct.qty_available, 40, "Wrong qty available for packproduct")
self.assertEqual(lotproduct.qty_available, 10, "Wrong qty available for lotproduct")
quants = self.StockQuantObj.search([('product_id', '=', lotproduct.id), ('location_id', '=', self.stock_location), ('lot_id', '=', lot1.id)])
total_qty = sum([quant.qty for quant in quants])
self.assertEqual(total_qty, 10, 'Expecting 0 units lot of lotproduct, but we got %.4f on location stock!' % (total_qty))
quants = self.StockQuantObj.search([('product_id', '=', lotproduct.id), ('location_id', '=', self.stock_location), ('lot_id', '=', False)])
total_qty = sum([quant.qty for quant in quants])
self.assertEqual(total_qty, 0, 'Expecting 0 units lot of lotproduct, but we got %.4f on location stock!' % (total_qty))
+18 -9
View File
@@ -83,15 +83,24 @@
</thead>
<tbody>
<tr t-foreach="o.move_lines" t-as="move">
<td><span t-field="move.product_id"/></td>
<td class="text-right"><span t-field="move.product_uom_qty"/> <span t-field="move.product_uom" groups="product.group_uom"/></td>
<t t-if="o.picking_type_id.code != 'incoming'"><td><span t-field="move.location_id"/></td></t>
<td>
<span t-if="move.product_id and move.product_id.barcode">
<img t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('Barcode', move.product_id.barcode, 600, 100)" style="width:300px;height:50px"/>
</span>
</td>
<t t-if="o.picking_type_id.code != 'outgoing'"><td><span t-field="move.location_dest_id"/></td></t>
<t t-if="(o.state != 'partially_available') or (move.reserved_availability > 0)">
<td><span t-field="move.product_id"/></td>
<td class="text-right">
<t t-if="o.state == 'partially_available'">
<span t-field="move.reserved_availability"/> <span t-field="move.product_id.uom_id" groups="product.group_uom"/>
</t>
<t t-if="o.state != 'partially_available'">
<span t-field="move.product_uom_qty"/> <span t-field="move.product_uom" groups="product.group_uom"/>
</t>
</td>
<t t-if="o.picking_type_id.code != 'incoming'"><td><span t-field="move.location_id"/></td></t>
<td>
<span t-if="move.product_id and move.product_id.barcode">
<img t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('Barcode', move.product_id.barcode, 600, 100)" style="width:300px;height:50px"/>
</span>
</td>
<t t-if="o.picking_type_id.code != 'outgoing'"><td><span t-field="move.location_dest_id"/></td></t>
</t>
</tr>
</tbody>
</table>
+8 -2
View File
@@ -28,6 +28,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
this.group_by_field = {};
this.grouped_by_m2o = false;
this.many2manys = [];
this.m2m_context = {};
this.state = {
groups : {},
records : {}
@@ -107,6 +108,11 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
this.transform_qweb_template(child);
this.qweb.add_template(instance.web.json_node_to_xml(child));
break;
} else if (child.tag === 'field') {
var ftype = child.attrs.widget || this.fields_view.fields[child.attrs.name].type;
if(ftype == "many2many" && "context" in child.attrs) {
this.m2m_context[child.attrs.name] = child.attrs.context;
}
}
}
},
@@ -570,7 +576,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
return;
}
if (!relations[field.relation]) {
relations[field.relation] = { ids: [], elements: {}};
relations[field.relation] = { ids: [], elements: {}, context: self.m2m_context[name]};
}
var rel = relations[field.relation];
field.raw_value.forEach(function(id) {
@@ -584,7 +590,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
});
});
_.each(relations, function(rel, rel_name) {
var dataset = new instance.web.DataSetSearch(self, rel_name, self.dataset.get_context());
var dataset = new instance.web.DataSetSearch(self, rel_name, self.dataset.get_context(rel.context));
dataset.name_get(_.uniq(rel.ids)).done(function(result) {
result.forEach(function(nameget) {
$(rel.elements[nameget[0]]).append('<span class="oe_tag">' + _.str.escapeHTML(nameget[1]) + '</span>');
+4 -1
View File
@@ -480,7 +480,10 @@ class Website(openerp.addons.web.controllers.main.Home):
#------------------------------------------------------
# Server actions
#------------------------------------------------------
@http.route('/website/action/<path_or_xml_id_or_id>', type='http', auth="public", website=True)
@http.route([
'/website/action/<path_or_xml_id_or_id>',
'/website/action/<path_or_xml_id_or_id>/<path:path>',
], type='http', auth="public", website=True)
def actions_server(self, path_or_xml_id_or_id, **post):
cr, uid, context = request.cr, request.uid, request.context
res, action_id, action = None, None, None
+3 -1
View File
@@ -323,7 +323,9 @@
function treat_node(node){
if(node.nodeType === 3) {
if(node.nodeValue.match(/\S/)){
node.nodeValue = openerp._t($.trim(node.nodeValue));
var text_value = $.trim(node.nodeValue);
var spaces = node.nodeValue.split(text_value);
node.nodeValue = spaces[0] + openerp._t(text_value) + spaces[1];
}
}
else if(node.nodeType === 1 && node.hasChildNodes()) {
+3 -2
View File
@@ -28,6 +28,7 @@ class Website(Website):
@route()
def customize_template_get(self, key, full=False, bundles=False):
res = super(Website, self).customize_template_get(key, full=full, bundles=bundles)
if full and request.session.get('report_view_ids'):
res += request.session['report_view_ids']
if full:
for r in request.session.get('report_view_ids', []):
res += super(Website, self).customize_template_get(r.get('xml_id'), full=full)
return res
+20
View File
@@ -0,0 +1,20 @@
Germany 2015-02-25
bloopark systems GmbH & Co. KG agrees to the terms of the Odoo Corporate
Contributor License Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
Jens Holze jholze@bloopark.de https://github.com/jholze
List of contributors:
Jens Holze jholze@bloopark.de https://github.com/jholze
Christoph Giesel cgiesel@bloopark.de https://github.com/christophlsa
Benjamin Bachmann bBachmann@bloopark.de https://github.com/benniphx
Robert Rübner rruebner@bloopark.de https://github.com/rruebner
Florian Fischer ffischer@bloopark.de https://github.com/florianfischer
Mercerdes Scenna mscenna@bloopark.de https://github.com/mscenna
+21
View File
@@ -0,0 +1,21 @@
Netherlands, 2015-03-09
Therp BV agrees to the terms of the Odoo Corporate
Contributor License Agreement v1.0.
We declare that we are authorized and able to make this agreement and sign
this declaration.
Signed,
* Ronald Portier rportier@therp.nl https://github.com/nl66278
* Stefan Rijnhart stefan@therp.nl https://github.com/stefanrijnhart
List of contributors:
* Giovanni Capalbo giovanni@therp.nl https://github.com/gfcapalbo
* Holger Brunn hbrunn@therp.nl https://github.com/hbrunn
* Lara Freeke lfreeke@therp.nl https://github.com/lfreeke
* Ronald Portier rportier@therp.nl https://github.com/nl66278
* Stefan Rijnhart stefan@therp.nl https://github.com/stefanrijnhart
+11
View File
@@ -0,0 +1,11 @@
Italy, 2015-02-18
I hereby agree to the terms of the Odoo Individual Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
Lorenzo Battistini lorenzo.battistini@agilebg.com https://github.com/eLBati
+11
View File
@@ -0,0 +1,11 @@
United Kingdom, 13.03.2015
I hereby agree to the terms of the Odoo Individual Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
Matthieu Choplin choplin.mat@gmail.com https://github.com/matthieuchoplin
+1 -1
View File
@@ -1086,7 +1086,7 @@ class ir_server_object_lines(osv.osv):
_sequence = 'ir_actions_id_seq'
_columns = {
'server_id': fields.many2one('ir.actions.server', 'Related Server Action'),
'server_id': fields.many2one('ir.actions.server', 'Related Server Action', ondelete='cascade'),
'col1': fields.many2one('ir.model.fields', 'Field', required=True),
'value': fields.text('Value', required=True, help="Expression containing a value specification. \n"
"When Formula type is selected, this field may be a Python expression "
+2 -2
View File
@@ -382,13 +382,13 @@ class ir_model_fields(osv.osv):
if vals.get('relation',False) and not self.pool['ir.model'].search(cr, user, [('model','=',vals['relation'])]):
raise UserError(_("Model %s does not exist!") % vals['relation'])
self.pool.clear_manual_fields()
if vals['model'] in self.pool:
model = self.pool[vals['model']]
if vals['model'].startswith('x_') and vals['name'] == 'x_name':
model._rec_name = 'x_name'
self.pool.clear_manual_fields()
# re-initialize model in registry
model.__init__(self.pool, cr)
self.pool.setup_models(cr, partial=(not self.pool.ready))
+1 -1
View File
@@ -179,7 +179,7 @@ class ir_sequence(models.Model):
if new_implementation in ('standard', None):
# Implementation has NOT changed.
# Only change sequence if really requested.
if seq.number_next != n:
if values.get('number_next'):
_alter_sequence(self.env.cr, "ir_sequence_%03d" % seq.id, number_next=n)
if seq.number_increment != i:
_alter_sequence(self.env.cr, "ir_sequence_%03d" % seq.id, number_increment=i)
+29 -1
View File
@@ -196,5 +196,33 @@ class test_ir_sequence_generate(unittest2.TestCase):
drop_sequence('test_sequence_type_6')
if __name__ == '__main__':
class Test_ir_sequence_init(common.TransactionCase):
def test_00(self):
registry, cr, uid = self.registry, self.cr, self.uid
# test if read statement return the good number_next value (from postgreSQL sequence and not ir_sequence value)
sequence = registry('ir.sequence')
# first creation of sequence (normal)
values = {'number_next': 1,
'company_id': 1,
'padding': 4,
'number_increment': 1,
'implementation': 'standard',
'name': 'test-sequence-00'}
seq_id = sequence.create(cr, uid, values)
# Call get next 4 times
sequence.next_by_id(cr, uid, seq_id)
sequence.next_by_id(cr, uid, seq_id)
sequence.next_by_id(cr, uid, seq_id)
read_sequence = sequence.next_by_id(cr, uid, seq_id)
# Read the value of the current sequence
assert read_sequence == "0004", 'The actual sequence value must be 4. reading : %s' % read_sequence
# reset sequence to 1 by write method calling
sequence.write(cr, uid, [seq_id], {'number_next': 1})
# Read the value of the current sequence
read_sequence = sequence.next_by_id(cr, uid, seq_id)
assert read_sequence == "0001", 'The actual sequence value must be 1. reading : %s' % read_sequence
if __name__ == "__main__":
unittest2.main()
+1 -1
View File
@@ -177,7 +177,7 @@ class wkf_workitem(osv.osv):
_columns = {
'act_id': fields.many2one('workflow.activity', 'Activity', required=True, ondelete="cascade", select=True),
'wkf_id': fields.related('act_id','wkf_id', type='many2one', relation='workflow', string='Workflow'),
'subflow_id': fields.many2one('workflow.instance', 'Subflow', ondelete="cascade", select=True),
'subflow_id': fields.many2one('workflow.instance', 'Subflow', ondelete="set null", select=True),
'inst_id': fields.many2one('workflow.instance', 'Instance', required=True, ondelete="cascade", select=True),
'state': fields.char('Status', select=True),
}
+3 -5
View File
@@ -1267,20 +1267,18 @@ class Selection(Field):
def set_class_name(self, cls, name):
super(Selection, self).set_class_name(cls, name)
# determine selection (applying 'selection_add' extensions)
selection = None
for field in resolve_all_mro(cls, name, reverse=True):
if isinstance(field, type(self)):
# We cannot use field.selection or field.selection_add here
# because those attributes are overridden by `set_class_name`.
if 'selection' in field._attrs:
selection = field._attrs['selection']
self.selection = field._attrs['selection']
if 'selection_add' in field._attrs:
# use an OrderedDict to update existing values
selection_add = field._attrs['selection_add']
selection = OrderedDict(selection + selection_add).items()
self.selection = OrderedDict(self.selection + selection_add).items()
else:
selection = None
self.selection = selection
self.selection = None
def _description_selection(self, env):
""" return the selection list (pairs (value, label)); labels are
+8 -1
View File
@@ -1599,6 +1599,7 @@ class BaseModel(object):
'views': [(view_id, 'form')],
'target': 'current',
'res_id': id,
'context': context,
}
def get_access_action(self, cr, uid, id, context=None):
@@ -2472,7 +2473,9 @@ class BaseModel(object):
self._o2m_raise_on_missing_reference(cr, f)
elif isinstance(f, fields.many2many):
self._m2m_raise_or_create_relation(cr, f)
res = self._m2m_raise_or_create_relation(cr, f)
if res and self._fields[k].depends:
stored_fields.append(self._fields[k])
else:
res = column_data.get(k)
@@ -2772,6 +2775,9 @@ class BaseModel(object):
raise UserError(_("There is no reference field '%s' found for '%s'") % (f._fields_id, f._obj))
def _m2m_raise_or_create_relation(self, cr, f):
""" Create the table for the relation if necessary.
Return ``True`` if the relation had to be created.
"""
m2m_tbl, col1, col2 = f._sql_names(self)
# do not create relations for custom fields as they do not belong to a module
# they will be automatically removed when dropping the corresponding ir.model.field
@@ -2798,6 +2804,7 @@ class BaseModel(object):
cr.execute("COMMENT ON TABLE \"%s\" IS 'RELATION BETWEEN %s AND %s'" % (m2m_tbl, self._table, ref))
cr.commit()
_schema.debug("Create table '%s': m2m relation between '%s' and '%s'", m2m_tbl, self._table, ref)
return True
def _add_sql_constraints(self, cr):
+1 -1
View File
@@ -533,7 +533,7 @@ ALL_LANGUAGES = {
'mn_MN': u'Mongolian / монгол',
'nb_NO': u'Norwegian Bokmål / Norsk bokmål',
'nl_NL': u'Dutch / Nederlands',
'nl_BE': u'Flemish (BE) / Vlaams (BE)',
'nl_BE': u'Dutch (BE) / Nederlands (BE)',
'oc_FR': u'Occitan (FR, post 1500) / Occitan',
'pl_PL': u'Polish / Język polski',
'pt_BR': u'Portuguese (BR) / Português (BR)',