[MERGE] forward port of branch 8.0 up to ed3065e
@@ -1326,9 +1326,9 @@ class account_invoice_line(models.Model):
|
||||
raise UserError(_("You must first select a partner!"))
|
||||
if not product:
|
||||
if type in ('in_invoice', 'in_refund'):
|
||||
return {'value': {}, 'domain': {'product_uom': []}}
|
||||
return {'value': {}, 'domain': {'uos_id': []}}
|
||||
else:
|
||||
return {'value': {'price_unit': 0.0}, 'domain': {'product_uom': []}}
|
||||
return {'value': {'price_unit': 0.0}, 'domain': {'uos_id': []}}
|
||||
|
||||
values = {}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="get_lines(data['form']['based_on'], data['form']['company_id'])" t-as="taxline">
|
||||
<tr t-foreach="get_lines(data['form']['based_on'], data['form']['company_id'], data['form']['chart_tax_id'])" t-as="taxline">
|
||||
<t t-if="len(taxline['level']) > 5">
|
||||
<t t-set="style" t-value="'font-weight:normal;'"/>
|
||||
</t>
|
||||
|
||||
@@ -114,6 +114,7 @@ class AuthSignupHome(openerp.addons.web.controllers.main.Home):
|
||||
values = dict((key, qcontext.get(key)) for key in ('login', 'name', 'password'))
|
||||
assert any([k for k in values.values()]), "The form was not properly filled in."
|
||||
assert values.get('password') == qcontext.get('confirm_password'), "Passwords do not match; please retype them."
|
||||
values['lang'] = request.lang
|
||||
self._signup_with_values(qcontext.get('token'), values)
|
||||
request.cr.commit()
|
||||
|
||||
|
||||
@@ -657,18 +657,18 @@ class ir_model(osv.Model):
|
||||
original_exp_report = openerp.service.report.exp_report
|
||||
|
||||
|
||||
def exp_report(db, uid, object, ids, data=None, context=None):
|
||||
def exp_report(db, uid, object, ids, datas=None, context=None):
|
||||
"""
|
||||
Export Report
|
||||
"""
|
||||
if object == 'printscreen.list':
|
||||
original_exp_report(db, uid, object, ids, data, context)
|
||||
original_exp_report(db, uid, object, ids, datas, context)
|
||||
new_ids = []
|
||||
for id in ids:
|
||||
new_ids.append(calendar_id2real_id(id))
|
||||
if data.get('id', False):
|
||||
data['id'] = calendar_id2real_id(data['id'])
|
||||
return original_exp_report(db, uid, object, new_ids, data, context)
|
||||
if datas.get('id', False):
|
||||
datas['id'] = calendar_id2real_id(datas['id'])
|
||||
return original_exp_report(db, uid, object, new_ids, datas, context)
|
||||
|
||||
|
||||
openerp.service.report.exp_report = exp_report
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<field name="name">Declarantnum</field>
|
||||
<field name="code">declarantnum</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -388,13 +388,6 @@ class mail_thread(osv.AbstractModel):
|
||||
name = ir_model_pool.read(cr, uid, ids, ['name'], context=context)[0]['name']
|
||||
self.message_post(cr, uid, thread_id, body=_('%s created') % name, context=context)
|
||||
|
||||
# auto_subscribe: take values and defaults into account
|
||||
create_values = dict(values)
|
||||
for key, val in context.iteritems():
|
||||
if key.startswith('default_') and key[8:] not in create_values:
|
||||
create_values[key[8:]] = val
|
||||
self.message_auto_subscribe(cr, uid, [thread_id], create_values.keys(), context=context, values=create_values)
|
||||
|
||||
# track values
|
||||
track_ctx = dict(context)
|
||||
if 'lang' not in track_ctx:
|
||||
@@ -404,6 +397,14 @@ class mail_thread(osv.AbstractModel):
|
||||
if tracked_fields:
|
||||
initial_values = {thread_id: dict.fromkeys(tracked_fields, False)}
|
||||
self.message_track(cr, uid, [thread_id], tracked_fields, initial_values, context=track_ctx)
|
||||
|
||||
# auto_subscribe: take values and defaults into account
|
||||
create_values = dict(values)
|
||||
for key, val in context.iteritems():
|
||||
if key.startswith('default_') and key[8:] not in create_values:
|
||||
create_values[key[8:]] = val
|
||||
self.message_auto_subscribe(cr, uid, [thread_id], create_values.keys(), context=context, values=create_values)
|
||||
|
||||
return thread_id
|
||||
|
||||
def write(self, cr, uid, ids, values, context=None):
|
||||
@@ -428,14 +429,16 @@ class mail_thread(osv.AbstractModel):
|
||||
initial_values = dict((record.id, dict((key, getattr(record, key)) for key in tracked_fields))
|
||||
for record in records)
|
||||
|
||||
# Perform write, update followers
|
||||
# Perform write
|
||||
result = super(mail_thread, self).write(cr, uid, ids, values, context=context)
|
||||
self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context, values=values)
|
||||
|
||||
# Perform the tracking
|
||||
if tracked_fields:
|
||||
self.message_track(cr, uid, ids, tracked_fields, initial_values, context=track_ctx)
|
||||
|
||||
# update followers
|
||||
self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context, values=values)
|
||||
|
||||
return result
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -157,7 +157,8 @@
|
||||
<field name="priority">50</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Membership Partners">
|
||||
<field name="name" string="Members"/>
|
||||
<field name="name"
|
||||
filter_domain="['|','|',('name','ilike',self),('parent_id','ilike',self),('ref','=',self)]"/>
|
||||
<field name="category_id"/>
|
||||
<field name="membership_start" invisible="1"/>
|
||||
<field name="membership_stop" string="End Membership Date"/>
|
||||
|
||||
@@ -975,7 +975,7 @@ 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 not float_is_zero(remaining_qty, precision_rounding=precision):
|
||||
if not float_is_zero(remaining_qty, precision_digits=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,
|
||||
@@ -1006,7 +1006,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 not float_is_zero(remaining_qty, precision_rounding=precision):
|
||||
if not float_is_zero(remaining_qty, precision_digits=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)
|
||||
|
||||
@@ -576,6 +576,16 @@ class pos_order(osv.osv):
|
||||
_description = "Point of Sale"
|
||||
_order = "id desc"
|
||||
|
||||
def _amount_line_tax(self, cr, uid, line, context=None):
|
||||
account_tax_obj = self.pool['account.tax']
|
||||
taxes_ids = [tax for tax in line.product_id.taxes_id if tax.company_id.id == line.order_id.company_id.id]
|
||||
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||
taxes = account_tax_obj.compute_all(cr, uid, taxes_ids, price, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)['taxes']
|
||||
val = 0.0
|
||||
for c in taxes:
|
||||
val += c.get('amount', 0.0)
|
||||
return val
|
||||
|
||||
def _order_fields(self, cr, uid, ui_order, context=None):
|
||||
process_line = partial(self.pool['pos.order.line']._order_line_fields, cr, uid, context=context)
|
||||
return {
|
||||
@@ -695,10 +705,10 @@ class pos_order(osv.osv):
|
||||
res[order.id]['amount_paid'] += payment.amount
|
||||
res[order.id]['amount_return'] += (payment.amount < 0 and payment.amount or 0)
|
||||
for line in order.lines:
|
||||
val1 += line.price_subtotal_incl
|
||||
val1 += self._amount_line_tax(cr, uid, line, context=context)
|
||||
val2 += line.price_subtotal
|
||||
res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val1-val2)
|
||||
res[order.id]['amount_total'] = cur_obj.round(cr, uid, cur, val1)
|
||||
res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val1)
|
||||
res[order.id]['amount_total'] = cur_obj.round(cr, uid, cur, val1+val2)
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
@@ -1321,8 +1331,8 @@ class pos_order_line(osv.osv):
|
||||
taxes = account_tax_obj.compute_all(cr, uid, taxes_ids, price, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)
|
||||
|
||||
cur = line.order_id.pricelist_id.currency_id
|
||||
res[line.id]['price_subtotal'] = cur_obj.round(cr, uid, cur, taxes['total'])
|
||||
res[line.id]['price_subtotal_incl'] = cur_obj.round(cr, uid, cur, taxes['total_included'])
|
||||
res[line.id]['price_subtotal'] = taxes['total']
|
||||
res[line.id]['price_subtotal_incl'] = taxes['total_included']
|
||||
return res
|
||||
|
||||
def onchange_product_id(self, cr, uid, ids, pricelist, product_id, qty=0, partner_id=False, context=None):
|
||||
|
||||
@@ -65,6 +65,10 @@
|
||||
<field name="name">Food</field>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/food-image.jpg"/>
|
||||
</record>
|
||||
<record id="partner_services" model="pos.category">
|
||||
<field name="name">Partner Services</field>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/services.jpg"/>
|
||||
</record>
|
||||
<record id="fruits" model="pos.category">
|
||||
<field name="name">Fresh Fruits</field>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/fruits-image.jpg"/>
|
||||
@@ -1085,6 +1089,114 @@
|
||||
<field name="pos_categ_id" ref="food"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/ijsboerke_dame_blanche_2,5l-image.jpg"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_1" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">850.00</field>
|
||||
<field name="name">Odoo Start Workshop</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/openbig.jpg"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_2" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">999.99</field>
|
||||
<field name="name">Odoo Consulting</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/bhc.jpg"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_3" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">999.99</field>
|
||||
<field name="name">Eezee-It</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/eezee-it.png"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_4" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">999.99</field>
|
||||
<field name="name">Ekomurz.nl</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/ekomurz.gif"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_5" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">999.99</field>
|
||||
<field name="name">Acsone.eu</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/acsone.png"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_6" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">999.99</field>
|
||||
<field name="name">Vauxoo.com</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/vauxoo.png"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_7" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">999.99</field>
|
||||
<field name="name">Datalp.com</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/datalp.jpg"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_8" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">999.99</field>
|
||||
<field name="name">camptocamp.com</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/camptocamp.png"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_9" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">999.99</field>
|
||||
<field name="name">EGGS-solutions.fr</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/eggs-solutions.jpg"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_10" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">3500.00</field>
|
||||
<field name="name">Odoo primary scoping & analysis package (£900, £450, £30)</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/opusvl.png"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_11" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">900.00</field>
|
||||
<field name="name">3rd party system analysis</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/opusvl.png"/>
|
||||
</record>
|
||||
|
||||
<record id="partner_product_12" model="product.product">
|
||||
<field name="available_in_pos">True</field>
|
||||
<field name="list_price">30.00</field>
|
||||
<field name="name">Odoo enterprise agreement</field>
|
||||
<field name="type">service</field>
|
||||
<field name="pos_categ_id" ref="partner_services"/>
|
||||
<field name="image" type="base64" file="point_of_sale/static/img/partners/opusvl.png"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- The electronic products shouldn't be present in the point of sale for the demo -->
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
@@ -25,8 +25,7 @@ import time
|
||||
from _common import ceiling
|
||||
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp import tools
|
||||
from openerp import api, tools, SUPERUSER_ID
|
||||
from openerp.osv import osv, fields, expression
|
||||
from openerp.tools.translate import _
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
@@ -225,19 +224,17 @@ class product_ul(osv.osv):
|
||||
#----------------------------------------------------------
|
||||
class product_category(osv.osv):
|
||||
|
||||
def name_get(self, cr, uid, ids, context=None):
|
||||
if isinstance(ids, (list, tuple)) and not len(ids):
|
||||
return []
|
||||
if isinstance(ids, (long, int)):
|
||||
ids = [ids]
|
||||
reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
|
||||
res = []
|
||||
for record in reads:
|
||||
name = record['name']
|
||||
if record['parent_id']:
|
||||
name = record['parent_id'][1]+' / '+name
|
||||
res.append((record['id'], name))
|
||||
return res
|
||||
@api.multi
|
||||
def name_get(self):
|
||||
def get_names(cat):
|
||||
""" Return the list [cat.name, cat.parent_id.name, ...] """
|
||||
res = []
|
||||
while cat:
|
||||
res.append(cat.name)
|
||||
cat = cat.parent_id
|
||||
return res
|
||||
|
||||
return [(cat.id, " / ".join(reversed(get_names(cat)))) for cat in self]
|
||||
|
||||
def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
|
||||
if not args:
|
||||
|
||||
@@ -1259,8 +1259,12 @@ class procurement_order(osv.osv):
|
||||
return super(procurement_order, self).run(cr, uid, list(set_others), context=context)
|
||||
|
||||
def _check(self, cr, uid, procurement, context=None):
|
||||
if procurement.purchase_line_id and procurement.purchase_line_id.order_id.shipped: # TOCHECK: does it work for several deliveries?
|
||||
return True
|
||||
if procurement.purchase_line_id:
|
||||
if procurement.purchase_line_id.order_id.shipped:
|
||||
return True
|
||||
elif procurement.move_ids:
|
||||
moves = self.pool.get('stock.move').browse(cr, uid, [x.id for x in procurement.move_ids], context=context)
|
||||
return all(move.state == 'done' for move in moves)
|
||||
return super(procurement_order, self)._check(cr, uid, procurement, context=context)
|
||||
|
||||
def _check_supplier_info(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -295,12 +295,11 @@ class sale_order(osv.osv):
|
||||
|
||||
def onchange_delivery_id(self, cr, uid, ids, company_id, partner_id, delivery_id, fiscal_position, context=None):
|
||||
r = {'value': {}}
|
||||
if not fiscal_position:
|
||||
if not company_id:
|
||||
company_id = self._get_default_company(cr, uid, context=context)
|
||||
fiscal_position = self.pool['account.fiscal.position'].get_fiscal_position(cr, uid, company_id, partner_id, delivery_id, context=context)
|
||||
if fiscal_position:
|
||||
r['value']['fiscal_position'] = fiscal_position
|
||||
if not company_id:
|
||||
company_id = self._get_default_company(cr, uid, context=context)
|
||||
fiscal_position = self.pool['account.fiscal.position'].get_fiscal_position(cr, uid, company_id, partner_id, delivery_id, context=context)
|
||||
if fiscal_position:
|
||||
r['value']['fiscal_position'] = fiscal_position
|
||||
return r
|
||||
|
||||
def onchange_partner_id(self, cr, uid, ids, part, context=None):
|
||||
|
||||
@@ -1952,7 +1952,7 @@ class stock_move(osv.osv):
|
||||
self.write(cr, uid, [move.id], {'state': 'confirmed'}, context=context)
|
||||
|
||||
def _prepare_procurement_from_move(self, cr, uid, move, context=None):
|
||||
origin = (move.group_id and (move.group_id.name + ":") or "") + (move.rule_id and move.rule_id.name or move.origin or "/")
|
||||
origin = (move.group_id and (move.group_id.name + ":") or "") + (move.rule_id and move.rule_id.name or move.origin or move.picking_id.name or "/")
|
||||
group_id = move.group_id and move.group_id.id or False
|
||||
if move.rule_id:
|
||||
if move.rule_id.group_propagation_option == 'fixed' and move.rule_id.group_id:
|
||||
@@ -2155,6 +2155,19 @@ class stock_move(osv.osv):
|
||||
result['location_dest_id'] = loc_dest_id
|
||||
return {'value': result}
|
||||
|
||||
def _prepare_picking_assign(self, cr, uid, move, context=None):
|
||||
""" Prepares a new picking for this move as it could not be assigned to
|
||||
another picking. This method is designed to be inherited.
|
||||
"""
|
||||
values = {
|
||||
'origin': move.origin,
|
||||
'company_id': move.company_id and move.company_id.id or False,
|
||||
'move_type': move.group_id and move.group_id.move_type or 'direct',
|
||||
'partner_id': move.partner_id.id or False,
|
||||
'picking_type_id': move.picking_type_id and move.picking_type_id.id or False,
|
||||
}
|
||||
return values
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def _picking_assign(self, cr, uid, move_ids, procurement_group, location_from, location_to, context=None):
|
||||
"""Assign a picking on the given move_ids, which is a list of move supposed to share the same procurement_group, location_from and location_to
|
||||
@@ -2170,13 +2183,7 @@ class stock_move(osv.osv):
|
||||
pick = picks[0]
|
||||
else:
|
||||
move = self.browse(cr, uid, move_ids, context=context)[0]
|
||||
values = {
|
||||
'origin': move.origin,
|
||||
'company_id': move.company_id and move.company_id.id or False,
|
||||
'move_type': move.group_id and move.group_id.move_type or 'direct',
|
||||
'partner_id': move.partner_id.id or False,
|
||||
'picking_type_id': move.picking_type_id and move.picking_type_id.id or False,
|
||||
}
|
||||
values = self._prepare_picking_assign(cr, uid, move, context=context)
|
||||
pick = pick_obj.create(cr, uid, values, context=context)
|
||||
return self.write(cr, uid, move_ids, {'picking_id': pick}, context=context)
|
||||
|
||||
|
||||
@@ -46,21 +46,39 @@ class stock_history(osv.osv):
|
||||
if context is None:
|
||||
context = {}
|
||||
date = context.get('history_date')
|
||||
prod_dict = {}
|
||||
if 'inventory_value' in fields:
|
||||
group_lines = {}
|
||||
for line in res:
|
||||
domain = line.get('__domain', [])
|
||||
group_lines.setdefault(str(domain), self.search(cr, uid, domain, context=context))
|
||||
line_ids = set()
|
||||
for ids in group_lines.values():
|
||||
for product_id in ids:
|
||||
line_ids.add(product_id)
|
||||
line_ids = list(line_ids)
|
||||
cr.execute('SELECT id, product_id, price_unit_on_quant, company_id, quantity FROM stock_history WHERE id in %s', (tuple(line_ids),))
|
||||
lines_rec = cr.dictfetchall()
|
||||
lines_dict = dict((line['id'], line) for line in lines_rec)
|
||||
product_ids = list(set(line_rec['product_id'] for line_rec in lines_rec))
|
||||
products_rec = self.pool['product.product'].read(cr, uid, product_ids, ['cost_method', 'product_tmpl_id'], context=context)
|
||||
products_dict = dict((product['id'], product) for product in products_rec)
|
||||
cost_method_product_tmpl_ids = list(set(product['product_tmpl_id'][0] for product in products_rec if product['cost_method'] != 'real'))
|
||||
cr.execute('SELECT DISTINCT ON (product_template_id, company_id) product_template_id, company_id, cost FROM product_price_history WHERE product_template_id in %s AND datetime <= %s ORDER BY product_template_id, company_id, datetime DESC', (tuple(cost_method_product_tmpl_ids), date))
|
||||
histories = cr.dictfetchall()
|
||||
histories_dict = {}
|
||||
for history in histories:
|
||||
histories_dict[(history['product_template_id'], history['company_id'])] = history['cost']
|
||||
for line in res:
|
||||
lines = self.search(cr, uid, line.get('__domain', []), context=context)
|
||||
inv_value = 0.0
|
||||
product_tmpl_obj = self.pool.get("product.template")
|
||||
lines_rec = self.browse(cr, uid, lines, context=context)
|
||||
for line_rec in lines_rec:
|
||||
if line_rec.product_id.cost_method == 'real':
|
||||
price = line_rec.price_unit_on_quant
|
||||
lines = group_lines.get(str(line.get('__domain', [])))
|
||||
for line_id in lines:
|
||||
line_rec = lines_dict[line_id]
|
||||
product = products_dict[line_rec['product_id']]
|
||||
if product['cost_method'] == 'real':
|
||||
price = line_rec['price_unit_on_quant']
|
||||
else:
|
||||
if not line_rec.product_id.id in prod_dict:
|
||||
prod_dict[line_rec.product_id.id] = product_tmpl_obj.get_history_price(cr, uid, line_rec.product_id.product_tmpl_id.id, line_rec.company_id.id, date=date, context=context)
|
||||
price = prod_dict[line_rec.product_id.id]
|
||||
inv_value += price * line_rec.quantity
|
||||
price = histories_dict.get((product['product_tmpl_id'][0], line_rec['company_id']), 0.0)
|
||||
inv_value += price * line_rec['quantity']
|
||||
line['inventory_value'] = inv_value
|
||||
return res
|
||||
|
||||
|
||||
@@ -312,12 +312,12 @@ def set_cookie_and_redirect(redirect_url):
|
||||
|
||||
def login_redirect():
|
||||
url = '/web/login?'
|
||||
if request.debug:
|
||||
url += 'debug&'
|
||||
# built the redirect url, keeping all the query parameters of the url
|
||||
redirect_url = '%s?%s' % (request.httprequest.base_url, werkzeug.urls.url_encode(request.params))
|
||||
return """<html><head><script>
|
||||
window.location = '%sredirect=' + encodeURIComponent(window.location);
|
||||
window.location = '%sredirect=' + encodeURIComponent("%s");
|
||||
</script></head></html>
|
||||
""" % (url,)
|
||||
""" % (url, redirect_url)
|
||||
|
||||
def load_actions_from_ir_values(key, key2, models, meta):
|
||||
Values = request.session.model('ir.values')
|
||||
|
||||
@@ -577,6 +577,7 @@
|
||||
border-radius: 3px;
|
||||
background: #f0f0fa;
|
||||
color: #4C4C4C;
|
||||
word-break: break-word;
|
||||
}
|
||||
.openerp .oe_tag_dark {
|
||||
background: #7C7BAD;
|
||||
@@ -620,7 +621,8 @@
|
||||
width: 100% !important;
|
||||
}
|
||||
.openerp .oe_tags .text-core {
|
||||
min-height: 22px;
|
||||
height: initial !important;
|
||||
word-break: break-word;
|
||||
}
|
||||
.openerp .oe_tags .text-core .text-wrap .text-dropdown .text-list .text-suggestion em {
|
||||
font-style: italic;
|
||||
|
||||
@@ -514,6 +514,7 @@ $sheet-padding: 16px
|
||||
@include radius(3px)
|
||||
background: $tag-bg-light
|
||||
color: #4C4C4C
|
||||
word-break: break-word
|
||||
.oe_tag_dark
|
||||
background: $tag-bg-dark
|
||||
color: #eee
|
||||
@@ -547,7 +548,8 @@ $sheet-padding: 16px
|
||||
textarea
|
||||
width: 100% !important
|
||||
.text-core
|
||||
min-height: 22px
|
||||
height: initial !important
|
||||
word-break: break-word
|
||||
.text-wrap .text-dropdown .text-list .text-suggestion em
|
||||
font-style: italic
|
||||
text-decoration: none
|
||||
|
||||
@@ -308,7 +308,7 @@ class WebsiteForum(http.Controller):
|
||||
})
|
||||
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), post_parent and slug(post_parent) or new_question.id))
|
||||
|
||||
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment', type='http', auth="public", methods=['POST'], website=True)
|
||||
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment', type='http', auth="public", website=True)
|
||||
def post_comment(self, forum, post, **kwargs):
|
||||
if not request.session.uid:
|
||||
return login_redirect()
|
||||
|
||||
@@ -280,7 +280,7 @@ class website_sale(http.Controller):
|
||||
}
|
||||
return request.website.render("website_sale.product", values)
|
||||
|
||||
@http.route(['/shop/product/comment/<int:product_template_id>'], type='http', auth="public", methods=['POST'], website=True)
|
||||
@http.route(['/shop/product/comment/<int:product_template_id>'], type='http', auth="public", website=True)
|
||||
def product_comment(self, product_template_id, **post):
|
||||
if not request.session.uid:
|
||||
return login_redirect()
|
||||
@@ -292,7 +292,7 @@ class website_sale(http.Controller):
|
||||
type='comment',
|
||||
subtype='mt_comment',
|
||||
context=dict(context, mail_create_nosubscribe=True))
|
||||
return werkzeug.utils.redirect(request.httprequest.referrer + "#comments")
|
||||
return werkzeug.utils.redirect('/shop/product/%s#comments' % product_template_id)
|
||||
|
||||
@http.route(['/shop/pricelist'], type='http', auth="public", website=True)
|
||||
def pricelist(self, promo, **post):
|
||||
@@ -716,6 +716,7 @@ class website_sale(http.Controller):
|
||||
if tx.state == 'draft': # button cliked but no more info -> rewrite on tx or create a new one ?
|
||||
tx.write({
|
||||
'acquirer_id': acquirer_id,
|
||||
'amount': order.amount_total,
|
||||
})
|
||||
tx_id = tx.id
|
||||
else:
|
||||
|
||||
@@ -197,7 +197,8 @@ class website(orm.Model):
|
||||
values.update(sale_order.onchange_pricelist_id(pricelist_id, None)['value'])
|
||||
sale_order.write(values)
|
||||
for line in sale_order.order_line:
|
||||
sale_order._cart_update(product_id=line.product_id.id, line_id=line.id, add_qty=0)
|
||||
if line.exists():
|
||||
sale_order._cart_update(product_id=line.product_id.id, line_id=line.id, add_qty=0)
|
||||
|
||||
# update browse record
|
||||
if (code and code != sale_order.pricelist_id.code) or sale_order.partner_id.id != partner.id:
|
||||
|
||||
@@ -124,3 +124,15 @@ class SaleOrder(orm.Model):
|
||||
|
||||
values['deliveries'] = DeliveryCarrier.browse(cr, SUPERUSER_ID, delivery_ids, context=delivery_ctx)
|
||||
return values
|
||||
|
||||
def _cart_update(self, cr, uid, ids, product_id=None, line_id=None, add_qty=0, set_qty=0, context=None, **kwargs):
|
||||
""" Override to update carrier quotation if quantity changed """
|
||||
|
||||
values = super(SaleOrder, self)._cart_update(
|
||||
cr, uid, ids, product_id, line_id, add_qty, set_qty, context, **kwargs)
|
||||
|
||||
if add_qty or set_qty is not None:
|
||||
for sale_order in self.browse(cr, uid, ids, context=context):
|
||||
self._check_carrier_quotation(cr, uid, sale_order, context=context)
|
||||
|
||||
return values
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Spain, 2015-04-20
|
||||
|
||||
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,
|
||||
|
||||
Antonio Espinosa antespi@gmail.com https://github.com/antespi
|
||||
@@ -0,0 +1,11 @@
|
||||
Poland, 2015-06-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,
|
||||
|
||||
Bartłomiej Wójcicki bartwojcicki@gmail.com https://github.com/bartekwojcicki
|
||||
@@ -0,0 +1,11 @@
|
||||
United Kingdom, 15.06.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,
|
||||
|
||||
Csaba Palankai csaba.palankai@gmail.com https://github.com/palankai
|
||||
@@ -0,0 +1,11 @@
|
||||
United Kingdom, 2015-06-18
|
||||
|
||||
I hereby agree to the terms of the Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and
|
||||
sign this declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Will Earp will.earp@icloud.com https://github.com/wearp
|
||||
@@ -621,7 +621,7 @@ instead of a single view its ``arch`` field is composed of any number of
|
||||
<!-- improved idea categories list -->
|
||||
<record id="idea_category_list2" model="ir.ui.view">
|
||||
<field name="name">id.category.list2</field>
|
||||
<field name="model">ir.ui.view</field>
|
||||
<field name="model">idea.category/field>
|
||||
<field name="inherit_id" ref="id_category_list"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- find field description inside tree, and add the field
|
||||
@@ -1074,7 +1074,7 @@ predefined searches. Filters must have one of the following attributes:
|
||||
domain="[('inventor_id', '=', uid)]"/>
|
||||
<group string="Group By">
|
||||
<filter name="group_by_inventor" string="Inventor"
|
||||
context="{'group_by': 'inventor'}"/>
|
||||
context="{'group_by': 'inventor_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ since "Carol" is not an integer, the route was ignored and no route could be
|
||||
found.
|
||||
|
||||
Odoo provides an additional converter called ``model`` which provides records
|
||||
directly when given their id, let's use that and create a generic page for
|
||||
directly when given their id. Let's use this to create a generic page for
|
||||
teacher biographies:
|
||||
|
||||
.. patch::
|
||||
|
||||
@@ -18,7 +18,8 @@ new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/academy/views.xml
|
||||
@@ -0,0 +1,13 @@
|
||||
+<openerp><data>
|
||||
+<openerp>
|
||||
+ <data>
|
||||
+ <record id="action_academy_teachers" model="ir.actions.act_window">
|
||||
+ <field name="name">Academy teachers</field>
|
||||
+ <field name="res_model">academy.teachers</field>
|
||||
@@ -30,4 +31,5 @@ new file mode 100644
|
||||
+ <menuitem id="menu_academy_content_teachers"
|
||||
+ parent="menu_academy_content"
|
||||
+ action="action_academy_teachers"/>
|
||||
+</data></openerp>
|
||||
+ </data>
|
||||
+</openerp>
|
||||
|
||||
@@ -230,8 +230,8 @@ Possible children elements of the list view are:
|
||||
Possible attributes are ``invisible`` (hides the button) and
|
||||
``readonly`` (disables the button but still shows it)
|
||||
``states``
|
||||
shorthand for ``invisible`` ``attrs``: a list of space, separated
|
||||
states, requires that the model has a ``state`` field and that it is
|
||||
shorthand for ``invisible`` ``attrs``: a list of states, comma separated,
|
||||
requires that the model has a ``state`` field and that it is
|
||||
used in the view.
|
||||
|
||||
Makes the button ``invisible`` if the record is *not* in one of the
|
||||
|
||||
@@ -176,11 +176,11 @@ class Field(object):
|
||||
@api.depends('name')
|
||||
def _compute_upper(self):
|
||||
for rec in self:
|
||||
self.upper = self.name.upper() if self.name else False
|
||||
rec.upper = rec.name.upper() if rec.name else False
|
||||
|
||||
def _inverse_upper(self):
|
||||
for rec in self:
|
||||
self.name = self.upper.lower() if self.upper else False
|
||||
rec.name = rec.upper.lower() if rec.upper else False
|
||||
|
||||
def _search_upper(self, operator, value):
|
||||
if operator == 'like':
|
||||
|
||||
@@ -44,6 +44,7 @@ except ImportError:
|
||||
|
||||
import openerp
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.service.server import memory_info
|
||||
from openerp.service import security, model as service_model
|
||||
from openerp.tools.func import lazy_property
|
||||
from openerp.tools import ustr
|
||||
@@ -97,7 +98,7 @@ def dispatch_rpc(service_name, method, params):
|
||||
start_time = time.time()
|
||||
start_rss, start_vms = 0, 0
|
||||
if psutil:
|
||||
start_rss, start_vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
start_rss, start_vms = memory_info(psutil.Process(os.getpid()))
|
||||
if rpc_request and rpc_response_flag:
|
||||
openerp.netsvc.log(rpc_request, logging.DEBUG, '%s.%s' % (service_name, method), replace_request_password(params))
|
||||
|
||||
@@ -119,7 +120,7 @@ def dispatch_rpc(service_name, method, params):
|
||||
end_time = time.time()
|
||||
end_rss, end_vms = 0, 0
|
||||
if psutil:
|
||||
end_rss, end_vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
end_rss, end_vms = memory_info(psutil.Process(os.getpid()))
|
||||
logline = '%s.%s time:%.3fs mem: %sk -> %sk (diff: %sk)' % (service_name, method, end_time - start_time, start_vms / 1024, end_vms / 1024, (end_vms - start_vms)/1024)
|
||||
if rpc_response_flag:
|
||||
openerp.netsvc.log(rpc_response, logging.DEBUG, logline, result)
|
||||
@@ -567,7 +568,7 @@ class JsonRequest(WebRequest):
|
||||
start_time = time.time()
|
||||
_, start_vms = 0, 0
|
||||
if psutil:
|
||||
_, start_vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
_, start_vms = memory_info(psutil.Process(os.getpid()))
|
||||
if rpc_request and rpc_response_flag:
|
||||
rpc_request.debug('%s: %s %s, %s',
|
||||
endpoint, model, method, pprint.pformat(args))
|
||||
@@ -578,7 +579,7 @@ class JsonRequest(WebRequest):
|
||||
end_time = time.time()
|
||||
_, end_vms = 0, 0
|
||||
if psutil:
|
||||
_, end_vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
_, end_vms = memory_info(psutil.Process(os.getpid()))
|
||||
logline = '%s: %s %s: time:%.3fs mem: %sk -> %sk (diff: %sk)' % (
|
||||
endpoint, model, method, end_time - start_time, start_vms / 1024, end_vms / 1024, (end_vms - start_vms)/1024)
|
||||
if rpc_response_flag:
|
||||
|
||||
@@ -52,6 +52,11 @@ except ImportError:
|
||||
|
||||
SLEEP_INTERVAL = 60 # 1 min
|
||||
|
||||
def memory_info(process):
|
||||
""" psutil < 2.0 does not have memory_info, >= 3.0 does not have
|
||||
get_memory_info """
|
||||
return (getattr(process, 'memory_info', None) or process.get_memory_info)()
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Werkzeug WSGI servers patched
|
||||
#----------------------------------------------------------
|
||||
@@ -570,6 +575,11 @@ class PreforkServer(CommonServer):
|
||||
for pid in self.workers.keys():
|
||||
self.worker_kill(pid, signal.SIGINT)
|
||||
while self.workers and time.time() < limit:
|
||||
try:
|
||||
self.process_signals()
|
||||
except KeyboardInterrupt:
|
||||
_logger.info("Forced shutdown.")
|
||||
break
|
||||
self.process_zombie()
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
@@ -650,7 +660,7 @@ class Worker(object):
|
||||
_logger.info("Worker (%d) max request (%s) reached.", self.pid, self.request_count)
|
||||
self.alive = False
|
||||
# Reset the worker if it consumes too much memory (e.g. caused by a memory leak).
|
||||
rss, vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
rss, vms = memory_info(psutil.Process(os.getpid()))
|
||||
if vms > config['limit_memory_soft']:
|
||||
_logger.info('Worker (%d) virtual memory limit (%s) reached.', self.pid, vms)
|
||||
self.alive = False # Commit suicide after the request.
|
||||
@@ -773,7 +783,7 @@ class WorkerCron(Worker):
|
||||
self.setproctitle(db_name)
|
||||
if rpc_request_flag:
|
||||
start_time = time.time()
|
||||
start_rss, start_vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
start_rss, start_vms = memory_info(psutil.Process(os.getpid()))
|
||||
|
||||
import openerp.addons.base as base
|
||||
base.ir.ir_cron.ir_cron._acquire_job(db_name)
|
||||
@@ -784,7 +794,7 @@ class WorkerCron(Worker):
|
||||
openerp.sql_db.close_db(db_name)
|
||||
if rpc_request_flag:
|
||||
run_time = time.time() - start_time
|
||||
end_rss, end_vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
end_rss, end_vms = memory_info(psutil.Process(os.getpid()))
|
||||
vms_diff = (end_vms - start_vms) / 1024
|
||||
logline = '%s time:%.3fs mem: %sk -> %sk (diff: %sk)' % \
|
||||
(db_name, run_time, start_vms / 1024, end_vms / 1024, vms_diff)
|
||||
|
||||