[MERGE] forward port of branch 8.0 up to bff6dff

This commit is contained in:
Christophe Simonis
2015-05-11 15:12:24 +02:00
58 changed files with 516 additions and 163 deletions
+7 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+11 -6
View File
@@ -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)
@@ -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)
@@ -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)
@@ -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):
+3
View File
@@ -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:
+2 -2
View File
@@ -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')]"/>
+3 -1
View File
@@ -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,
}
@@ -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'
+2 -2
View File
@@ -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 ''
+1
View File
@@ -37,6 +37,7 @@ such as the Mettler Toledo Ariva.
""",
'author': 'OpenERP SA',
'depends': ['hw_proxy'],
'external_dependencies': {'python': ['serial']},
'test': [
],
'installable': True,
+13 -7
View File
@@ -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
+1
View File
@@ -38,6 +38,7 @@ point of sale module.
""",
'author': 'OpenERP SA',
'depends': ['hw_proxy'],
'external_dependencies': {'python': ['evdev']},
'test': [
],
'installable': True,
+6 -9
View File
@@ -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
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+6 -4
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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):
+1 -7
View File
@@ -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:
+1 -1
View File
@@ -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){
+45 -6
View File
@@ -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:
+1 -1
View File
@@ -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'.
-
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 report_layout_category_1 report_layout_category_1 model_sale_layout_category base.group_sale_manager 1 1 1 1
3 report_layout_category_2 report_layout_category_2 model_sale_layout_category account.group_account_manager 1 1 1 1
4 report_layout_category_3 report_layout_category_3 model_sale_layout_category base.group_sale_salesman 1 1 1 O 0
5 report_layout_category_4 report_layout_category_4 model_sale_layout_category base.group_sale_salesman_all_leads 1 1 1 0
6 report_layout_category_5 report_layout_category_5 model_sale_layout_category account.group_account_invoice 1 0 0 0
+43 -8
View File
@@ -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"
+1 -1
View File
@@ -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"/>
+2
View File
@@ -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.
@@ -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)
+13
View File
@@ -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]
@@ -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
+51 -35
View File
@@ -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)
+1 -1
View File
@@ -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
+1
View File
@@ -1 +1,2 @@
import controllers
import models
+40 -11
View File
@@ -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)
partners = dict((p.id, p) for p in partner_obj.browse(request.cr, SUPERUSER_ID, list(set(partner_ids + free_partner_ids)), request.context))
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)}
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,
@@ -0,0 +1 @@
import membership
@@ -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()]
+4
View File
@@ -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):
@@ -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;
@@ -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>
+5 -2
View File
@@ -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}
@@ -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));
+11
View File
@@ -0,0 +1,11 @@
China, 2015-04-25
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,
Jeffery Chen Fan jeffery9@gmail.com https://github.com/jeffery9
+11
View File
@@ -0,0 +1,11 @@
La Habana, 2015-04-30
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,
Manuel Vázquez Acosta mva.led@gmail.com https://github.com/mvaled
+11
View File
@@ -0,0 +1,11 @@
Brazil, 2015-04-28
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,
Rui Andrada shingonoide@gmail.com https://github.com/shingonoide
+1
View File
@@ -764,6 +764,7 @@ method should simply set the value of the field to compute on every record in
name = fields.Char(compute='_compute_name')
@api.multi
def _compute_name(self):
for record in self:
record.name = str(random.randint(1, 1e6))
+4 -5
View File
@@ -35,7 +35,7 @@ Index: addons/openacademy/views/openacademy.xml
===================================================================
--- addons.orig/openacademy/views/openacademy.xml 2014-08-28 14:21:46.543015785 +0200
+++ addons/openacademy/views/openacademy.xml 2014-08-28 14:21:46.539015785 +0200
@@ -145,11 +145,24 @@
@@ -145,11 +145,23 @@
</field>
</record>
@@ -44,10 +44,9 @@ Index: addons/openacademy/views/openacademy.xml
+ <field name="model">openacademy.session</field>
+ <field name="arch" type="xml">
+ <gantt string="Session Gantt" color="course_id"
+ date_start="start_date" date_delay="hours">
+ <level object="res.partner" link="instructor_id">
+ <field name="name"/>
+ </level>
+ date_start="start_date" date_delay="hours"
+ default_group_by='instructor_id'>
+ <field name="name"/>
+ </gantt>
+ </field>
+ </record>
@@ -5,7 +5,7 @@ Index: addons/openacademy/views/session_workflow.xml
===================================================================
--- addons.orig/openacademy/views/session_workflow.xml 2014-08-26 17:26:17.339783114 +0200
+++ addons/openacademy/views/session_workflow.xml 2014-08-26 17:26:17.331783114 +0200
@@ -6,24 +6,53 @@
@@ -6,24 +6,50 @@
<field name="on_create">True</field>
</record>
@@ -13,8 +13,7 @@ Index: addons/openacademy/views/session_workflow.xml
+ <field name="name">Set session to Draft</field>
+ <field name="model_id" ref="model_openacademy_session"/>
+ <field name="code">
+recs = self.browse(cr, uid, context['active_ids'], context=context)
+recs.action_draft()
+model.search([('id', 'in', context['active_ids'])]).action_draft()
+ </field>
+ </record>
<record model="workflow.activity" id="draft">
@@ -32,8 +31,7 @@ Index: addons/openacademy/views/session_workflow.xml
+ <field name="name">Set session to Confirmed</field>
+ <field name="model_id" ref="model_openacademy_session"/>
+ <field name="code">
+recs = self.browse(cr, uid, context['active_ids'], context=context)
+recs.action_confirm()
+model.search([('id', 'in', context['active_ids'])]).action_confirm()
+ </field>
</record>
<record model="workflow.activity" id="confirmed">
@@ -50,8 +48,7 @@ Index: addons/openacademy/views/session_workflow.xml
+ <field name="name">Set session to Done</field>
+ <field name="model_id" ref="model_openacademy_session"/>
+ <field name="code">
+recs = self.browse(cr, uid, context['active_ids'], context=context)
+recs.action_done()
+model.search([('id', 'in', context['active_ids'])]).action_done()
+ </field>
</record>
<record model="workflow.activity" id="done">
+5 -5
View File
@@ -1046,7 +1046,7 @@ class ir_model_data(osv.osv):
if action_id and res_id:
model_obj.write(cr, uid, [res_id], values, context=context)
self.write(cr, uid, [action_id], {
self.write(cr, SUPERUSER_ID, [action_id], {
'date_update': time.strftime('%Y-%m-%d %H:%M:%S'),
},context=context)
elif res_id:
@@ -1056,14 +1056,14 @@ class ir_model_data(osv.osv):
for table in model_obj._inherits:
inherit_id = model_obj.browse(cr, uid,
res_id,context=context)[model_obj._inherits[table]]
self.create(cr, uid, {
self.create(cr, SUPERUSER_ID, {
'name': xml_id + '_' + table.replace('.', '_'),
'model': table,
'module': module,
'res_id': inherit_id.id,
'noupdate': noupdate,
},context=context)
self.create(cr, uid, {
self.create(cr, SUPERUSER_ID, {
'name': xml_id,
'model': model,
'module':module,
@@ -1078,14 +1078,14 @@ class ir_model_data(osv.osv):
for table in model_obj._inherits:
inherit_id = model_obj.browse(cr, uid,
res_id,context=context)[model_obj._inherits[table]]
self.create(cr, uid, {
self.create(cr, SUPERUSER_ID, {
'name': xml_id + '_' + table.replace('.', '_'),
'model': table,
'module': module,
'res_id': inherit_id.id,
'noupdate': noupdate,
},context=context)
self.create(cr, uid, {
self.create(cr, SUPERUSER_ID, {
'name': xml_id,
'model': model,
'module': module,
+8
View File
@@ -907,6 +907,13 @@ class Environment(object):
finally:
self.all.recompute = tmp
@property
def recompute_old(self):
return self.all.recompute_old
def clear_recompute_old(self):
del self.all.recompute_old[:]
class Environments(object):
""" A common object for all environments in a request. """
@@ -915,6 +922,7 @@ class Environments(object):
self.todo = {} # recomputations {field: [records]}
self.mode = False # flag for draft/onchange
self.recompute = True
self.recompute_old = [] # list of old api compute fields to recompute
def add(self, env):
""" Add the environment `env`. """
+1 -4
View File
@@ -1054,10 +1054,7 @@ class Float(Field):
@property
def digits(self):
if callable(self._digits):
# retrieve a cursor from any environment
from openerp.api import Environment
cr = next(iter(Environment.envs)).cr
return self._digits(cr)
return self._digits(fields.get_cursor())
else:
return self._digits
+2 -1
View File
@@ -907,7 +907,8 @@ class Model(object):
raise Exception("Access denied")
mod = request.registry[self.model]
meth = getattr(mod, method)
cr = request.cr
# make sure to instantiate an environment
cr = request.env.cr
result = meth(cr, request.uid, *args, **kw)
# reorder read
if method == "read":
+14 -7
View File
@@ -4004,10 +4004,10 @@ class BaseModel(object):
recs.invalidate_cache(['parent_left', 'parent_right'])
result += self._store_get_values(cr, user, ids, vals.keys(), context)
result.sort()
done = {}
for order, model_name, ids_to_update, fields_to_recompute in result:
recs.env.recompute_old.extend(result)
for order, model_name, ids_to_update, fields_to_recompute in sorted(recs.env.recompute_old):
key = (model_name, tuple(fields_to_recompute))
done.setdefault(key, {})
# avoid to do several times the same computation
@@ -4018,6 +4018,7 @@ class BaseModel(object):
if id not in deleted_related[model_name]:
todo.append(id)
self.pool[model_name]._store_set_values(cr, user, todo, fields_to_recompute, context)
recs.env.clear_recompute_old()
# recompute new-style fields
if recs.env.recompute and context.get('recompute', True):
@@ -4267,16 +4268,18 @@ class BaseModel(object):
# check Python constraints
recs._validate_fields(vals)
if recs.env.recompute and context.get('recompute', True):
result += self._store_get_values(cr, user, [id_new],
result += self._store_get_values(cr, user, [id_new],
list(set(vals.keys() + self._inherits.values())),
context)
result.sort()
recs.env.recompute_old.extend(result)
if recs.env.recompute and context.get('recompute', True):
done = []
for order, model_name, ids, fields2 in result:
for order, model_name, ids, fields2 in sorted(recs.env.recompute_old):
if not (model_name, ids, fields2) in done:
self.pool[model_name]._store_set_values(cr, user, ids, fields2, context)
done.append((model_name, ids, fields2))
recs.env.clear_recompute_old()
# recompute new-style fields
recs.recompute()
@@ -5677,7 +5680,11 @@ class BaseModel(object):
while self.env.has_todo():
field, recs = self.env.get_todo()
# evaluate the fields to recompute, and save them to database
names = [f.name for f in field.computed_fields if f.store]
names = [
f.name
for f in field.computed_fields
if f.store and self.env.field_todo(f)
]
for rec in recs:
try:
values = rec._convert_to_write({
+2
View File
@@ -173,6 +173,8 @@ class Registry(Mapping):
:param partial: ``True`` if all models have not been loaded yet.
"""
lazy_property.reset_all(self)
# load custom models
ir_model = self['ir.model']
cr.execute('select model from ir_model where state=%s', ('manual',))
+9 -2
View File
@@ -78,7 +78,7 @@ class PostgreSQLHandler(logging.Handler):
def emit(self, record):
ct = threading.current_thread()
ct_db = getattr(ct, 'dbname', None)
dbname = tools.config['log_db'] or ct_db
dbname = tools.config['log_db'] if tools.config['log_db'] and tools.config['log_db'] != '%d' else ct_db
if not dbname:
return
with tools.ignore(Exception), tools.mute_logger('openerp.sql_db'), sql_db.db_connect(dbname, allow_uri=True).cursor() as cr:
@@ -185,8 +185,15 @@ def init_logger():
logging.getLogger().addHandler(handler)
if tools.config['log_db']:
db_levels = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL,
}
postgresqlHandler = PostgreSQLHandler()
postgresqlHandler.setLevel(25)
postgresqlHandler.setLevel(int(db_levels.get(tools.config['log_db_level'], tools.config['log_db_level'])))
logging.getLogger().addHandler(postgresqlHandler)
# Configure loggers levels
+10 -8
View File
@@ -52,6 +52,14 @@ from openerp.tools import float_repr, float_round, frozendict, html_sanitize
import simplejson
from openerp import SUPERUSER_ID, registry
def get_cursor():
# retrieve a valid cursor from any environment
from openerp.api import Environment
for env in Environment.envs:
if not env.cr.closed:
return env.cr
raise RuntimeError("No valid cursor found")
EMPTY_DICT = frozendict()
_logger = logging.getLogger(__name__)
@@ -388,10 +396,7 @@ class float(_column):
@property
def digits(self):
if self._digits_compute:
# retrieve a cursor from any environment
from openerp.api import Environment
cr = next(iter(Environment.envs)).cr
return self._digits_compute(cr)
return self._digits_compute(get_cursor())
else:
return self._digits
@@ -1311,10 +1316,7 @@ class function(_column):
@property
def digits(self):
if self._digits_compute:
# retrieve a cursor from any environment
from openerp.api import Environment
cr = next(iter(Environment.envs)).cr
return self._digits_compute(cr)
return self._digits_compute(get_cursor())
else:
return self._digits
+6
View File
@@ -402,6 +402,12 @@ class Cursor(object):
def __getattr__(self, name):
return getattr(self._obj, name)
@property
def closed(self):
# NOTE: we cannot use `__getattr__` delegation as we
# should not `check` that the cursor is closed
return self._obj.closed
class TestCursor(Cursor):
""" A cursor to be used for tests. It keeps the transaction open across
several requests, and simulates committing, rolling back, and closing.
+2 -1
View File
@@ -179,6 +179,7 @@ class configmanager(object):
group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.http:DEBUG", help='shortcut for --log-handler=openerp.http:DEBUG')
group.add_option('--log-sql', action="append_const", dest="log_handler", const="openerp.sql_db:DEBUG", help='shortcut for --log-handler=openerp.sql_db:DEBUG')
group.add_option('--log-db', dest='log_db', help="Logging database", my_default=False)
group.add_option('--log-db-level', dest='log_db_level', my_default='warning', help="Logging database level")
# For backward-compatibility, map the old log levels to something
# quite close.
levels = [
@@ -383,7 +384,7 @@ class configmanager(object):
'db_maxconn', 'import_partial', 'addons_path',
'xmlrpc', 'syslog', 'without_demo',
'dbfilter', 'log_level', 'log_db',
'geoip_database',
'log_db_level', 'geoip_database',
]
for arg in keys: