[MERGE] forward port of branch 8.0 up to d3e9bde

This commit is contained in:
Christophe Simonis
2015-08-06 11:54:32 +02:00
42 changed files with 247 additions and 190 deletions
+2 -1
View File
@@ -1547,7 +1547,8 @@ class account_invoice_tax(models.Model):
currency = self.env['res.currency'].browse(currency_id)
currency = currency.with_context(date=date_invoice or fields.Date.context_today(self))
amount = currency.compute(amount, company.currency_id, round=False)
return {'value': {'tax_amount': amount}}
tax_sign = (self.tax_amount / self.amount) if self.amount else 1
return {'value': {'tax_amount': amount * tax_sign}}
@api.v8
def compute(self, invoice):
+2 -2
View File
@@ -1210,8 +1210,8 @@
<field eval="1" name="priority"/>
<field name="arch" type="xml">
<tree colors="red:state == 'draft';black:state == 'valid'" string="Journal Items" create="true" on_write="on_create_write" editable="top">
<field name="journal_id" options='{"no_open":True}' invisible="context.get('journal_id',False)"/>
<field name="period_id" options='{"no_open":True}' invisible="context.get('period_id',False)"/>
<field name="journal_id" options='{"no_open":True}'/>
<field name="period_id" options='{"no_open":True}'/>
<field name="date"/>
<field name="name"/>
<field name="ref"/>
+2
View File
@@ -18,3 +18,5 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import models
@@ -34,6 +34,17 @@
</field>
</record>
<record id="bank_statement_draft_form_inherit" model="ir.ui.view">
<field name="name">bank.statement.draft.form.inherit</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='button_cancel']" position="after">
<button name="button_draft" states="confirm" string="Reset to New" type="object"/>
</xpath>
</field>
</record>
<record id="bank_statement_cancel_form_inherit" model="ir.ui.view">
<field name="name">bank.statement.cancel.form.inherit</field>
<field name="model">account.bank.statement</field>
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import account_bank_statement
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import models, api, _
from openerp.exceptions import Warning
class BankStatement(models.Model):
_inherit = 'account.bank.statement'
@api.multi
def button_draft(self):
self.state = 'draft'
class BankStatementLine(models.Model):
_inherit = 'account.bank.statement.line'
@api.multi
def cancel(self):
for line in self:
if line.statement_id.state == 'confirm':
raise Warning(_("Please set the bank statement to New before canceling."))
return super(BankStatementLine, self).cancel()
@@ -116,7 +116,7 @@ class hr_analytic_timesheet(osv.osv):
res ['value']['general_account_id']= a
return res
def on_change_user_id(self, cr, uid, ids, user_id, account_id, unit_amount=0):
def on_change_user_id(self, cr, uid, ids, user_id, account_id=False, unit_amount=0):
res = super(hr_analytic_timesheet, self).on_change_user_id(cr, uid, ids, user_id)
if account_id:
+1 -1
View File
@@ -66,7 +66,7 @@ class board_board(osv.osv):
res = {}
res = super(board_board, self).fields_view_get(cr, user, view_id, view_type,
context, toolbar=toolbar, submenu=submenu)
context=context, toolbar=toolbar, submenu=submenu)
CustView = self.pool.get('ir.ui.view.custom')
vids = CustView.search(cr, user, [('user_id', '=', user), ('ref_id', '=', view_id)], context=context)
@@ -205,7 +205,8 @@ class crm_lead(osv.osv):
# warning: point() type takes (longitude, latitude) as parameters in this order!
cr.execute("""SELECT id, distance
FROM (select id, (point(partner_longitude, partner_latitude) <-> point(%s,%s)) AS distance FROM res_partner
WHERE partner_longitude is not null
WHERE active
AND partner_longitude is not null
AND partner_latitude is not null
AND partner_weight > 0) AS d
ORDER BY distance LIMIT 1""", (longitude, latitude))
+4 -1
View File
@@ -82,7 +82,7 @@ class stock_picking(osv.osv):
"""
carrier_obj = self.pool.get('delivery.carrier')
grid_obj = self.pool.get('delivery.grid')
fpos_obj = self.pool['account.fiscal.position']
currency_obj = self.pool.get('res.currency')
if not picking.carrier_id or \
any(inv_line.product_id.id == picking.carrier_id.product_id.id
for inv_line in invoice.invoice_line):
@@ -95,6 +95,9 @@ class stock_picking(osv.osv):
price = grid_obj.get_price_from_picking(cr, uid, grid_id,
invoice.amount_untaxed, picking.weight, picking.volume,
quantity, context=context)
if invoice.company_id.currency_id.id != invoice.currency_id.id:
price = currency_obj.compute(cr, uid, invoice.company_id.currency_id.id, invoice.currency_id.id,
price, context=dict(context or {}, date=invoice.date_invoice))
account_id = picking.carrier_id.product_id.property_account_income.id
if not account_id:
account_id = picking.carrier_id.product_id.categ_id\
+3
View File
@@ -42,6 +42,7 @@ from openerp.exceptions import UserError
_logger = logging.getLogger(__name__)
MAX_POP_MESSAGES = 50
MAIL_TIMEOUT = 60
# Workaround for Python 2.7.8 bug https://bugs.python.org/issue23906
poplib._MAXLINE = 65536
@@ -155,6 +156,8 @@ openerp_mailgate: "|/path/to/openerp-mailgate.py --host=localhost -u %(uid)d -p
#connection.user("recent:"+server.user)
connection.user(server.user)
connection.pass_(server.password)
# Add timeout on socket
connection.sock.settimeout(MAIL_TIMEOUT)
return connection
def button_confirm_login(self, cr, uid, ids, context=None):
+3 -3
View File
@@ -163,13 +163,13 @@ class google_service(osv.osv_memory):
except:
pass
except urllib2.HTTPError, e:
if e.code in (400, 401, 410):
raise e
elif e.code in (204, 404):
if e.code in (204, 404):
status = e.code
response = ""
else:
_logger.exception("Bad google request : %s !" % e.read())
if e.code in (400, 401, 410):
raise e
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong with your request to google"), context=context)
return (status, response, ask_time)
+3 -1
View File
@@ -216,8 +216,10 @@ class google_calendar(osv.AbstractModel):
vstype = 'date'
attendee_list = []
for attendee in event.attendee_ids:
email = tools.email_split(attendee.email)
email = email[0] if email else 'NoEmail@mail.com'
attendee_list.append({
'email': attendee.email or 'NoEmail@mail.com',
'email': email,
'displayName': attendee.partner_id.name,
'responseStatus': attendee.state or 'needsAction',
})
@@ -221,17 +221,17 @@ class account_analytic_line(osv.osv):
if factor.customer_name:
factor_name += ' - ' + factor.customer_name
general_account = product.property_account_income or product.categ_id.property_account_income_categ
if not general_account:
raise UserError(_("Configuration Error!") + '\n' + _("Please define income account for product '%s'.") % product.name)
taxes = product.taxes_id or general_account.tax_ids
tax = self.pool['account.fiscal.position'].map_tax(cr, uid, account.partner_id.property_account_position, taxes)
curr_invoice_line.update({
'invoice_line_tax_id': [(6, 0, tax)],
'name': factor_name,
'invoice_line_tax_id': [(6, 0, tax)],
'account_id': general_account.id,
})
general_account = product.property_account_income or product.categ_id.property_account_income_categ
if not general_account:
raise UserError(_("Configuration Error!") + '\n' + _("Please define income account for product '%s'.") % product.name)
taxes = product.taxes_id or general_account.tax_ids
tax = self.pool['account.fiscal.position'].map_tax(cr, uid, account.partner_id.property_account_position, taxes)
curr_invoice_line.update({
'invoice_line_tax_id': [(6, 0, tax)],
'name': factor_name,
'invoice_line_tax_id': [(6, 0, tax)],
'account_id': general_account.id,
})
note = []
for line in analytic_lines:
+20 -20
View File
@@ -6,7 +6,7 @@
<record id="attn_VAT-OUT-21-S" model="account.tax.template">
<field name="sequence">10</field>
<field name="description">VAT-OUT-21-S</field>
<field name="name">VAT 21% - Services</field>
<field name="name">TVA 21% - Services</field>
<field name="account_collected_id" ref="a451054"/>
<field name="account_paid_id" ref="a451054"/>
<field name="price_include" eval="0"/>
@@ -27,7 +27,7 @@
<record id="attn_VAT-OUT-21-L" model="account.tax.template">
<field name="sequence">11</field>
<field name="description">VAT-OUT-21-L</field>
<field name="name">VAT 21%</field>
<field name="name">TVA 21%</field>
<field name="account_collected_id" ref="a451054"/>
<field name="account_paid_id" ref="a451054"/>
<field name="price_include" eval="0"/>
@@ -48,7 +48,7 @@
<record id="attn_VAT-OUT-12-S" model="account.tax.template">
<field name="sequence">20</field>
<field name="description">VAT-OUT-12-S</field>
<field name="name">VAT 12% - Services</field>
<field name="name">TVA 12% - Services</field>
<field name="account_collected_id" ref="a451054"/>
<field name="account_paid_id" ref="a451054"/>
<field name="price_include" eval="0"/>
@@ -69,7 +69,7 @@
<record id="attn_VAT-OUT-12-L" model="account.tax.template">
<field name="sequence">21</field>
<field name="description">VAT-OUT-12-L</field>
<field name="name">VAT 12%</field>
<field name="name">TVA 12%</field>
<field name="account_collected_id" ref="a451054"/>
<field name="account_paid_id" ref="a451054"/>
<field name="price_include" eval="0"/>
@@ -90,7 +90,7 @@
<record id="attn_VAT-OUT-06-S" model="account.tax.template">
<field name="sequence">30</field>
<field name="description">VAT-OUT-06-S</field>
<field name="name">VAT 6% - Services</field>
<field name="name">TVA 6% - Services</field>
<field name="account_collected_id" ref="a451054"/>
<field name="account_paid_id" ref="a451054"/>
<field name="price_include" eval="0"/>
@@ -111,7 +111,7 @@
<record id="attn_VAT-OUT-06-L" model="account.tax.template">
<field name="sequence">31</field>
<field name="description">VAT-OUT-06-L</field>
<field name="name">VAT 6%</field>
<field name="name">TVA 6%</field>
<field name="account_collected_id" ref="a451054"/>
<field name="account_paid_id" ref="a451054"/>
<field name="price_include" eval="0"/>
@@ -132,7 +132,7 @@
<record id="attn_VAT-OUT-00-S" model="account.tax.template">
<field name="sequence">40</field>
<field name="description">VAT-OUT-00-S</field>
<field name="name">VAT 0% - Services</field>
<field name="name">TVA 0% - Services</field>
<field name="price_include" eval="0"/>
<field name="amount">0</field>
<field name="type">percent</field>
@@ -149,7 +149,7 @@
<record id="attn_VAT-OUT-00-L" model="account.tax.template">
<field name="sequence">41</field>
<field name="description">VAT-OUT-00-L</field>
<field name="name">VAT 0%</field>
<field name="name">TVA 0%</field>
<field name="price_include" eval="0"/>
<field name="amount">0</field>
<field name="type">percent</field>
@@ -166,7 +166,7 @@
<record id="attn_VAT-OUT-00-CC" model="account.tax.template">
<field name="sequence">50</field>
<field name="description">VAT-OUT-00-CC</field>
<field name="name">VAT 0% Cocontractant</field>
<field name="name">TVA 0% Cocontractant</field>
<field name="price_include" eval="0"/>
<field name="amount">0</field>
<field name="type">percent</field>
@@ -183,7 +183,7 @@
<record id="attn_VAT-OUT-00-EU-S" model="account.tax.template">
<field name="sequence">60</field>
<field name="description">VAT-OUT-00-EU-S</field>
<field name="name">Services EU</field>
<field name="name">TVA services EU</field>
<field name="price_include" eval="0"/>
<field name="amount">0</field>
<field name="type">percent</field>
@@ -200,7 +200,7 @@
<record id="attn_VAT-OUT-00-EU-L" model="account.tax.template">
<field name="sequence">61</field>
<field name="description">VAT-OUT-00-EU-L</field>
<field name="name">Marchandises EU (Code L)</field>
<field name="name">TVA Marchandises EU (Code L)</field>
<field name="price_include" eval="0"/>
<field name="amount">0</field>
<field name="type">percent</field>
@@ -217,7 +217,7 @@
<record id="attn_VAT-OUT-00-EU-T" model="account.tax.template">
<field name="sequence">62</field>
<field name="description">VAT-OUT-00-EU-T</field>
<field name="name">Marchandises EU (Code T)</field>
<field name="name">TVA marchandises EU (Code T)</field>
<field name="price_include" eval="0"/>
<field name="amount">0</field>
<field name="type">percent</field>
@@ -234,7 +234,7 @@
<record id="attn_VAT-OUT-00-ROW" model="account.tax.template">
<field name="sequence">70</field>
<field name="description">VAT-OUT-00-ROW</field>
<field name="name">Export hors EU</field>
<field name="name">TVA export hors EU</field>
<field name="price_include" eval="0"/>
<field name="amount">0</field>
<field name="type">percent</field>
@@ -1981,7 +1981,7 @@
<record id="attn_VAT-IN-V82-CAR-INC" model="account.tax.template">
<field name="sequence">710</field>
<field name="description">VAT-IN-V82-CAR-INC</field>
<field name="name">TVA Entrant - Frais de voiture - VAT 50% Non Deductible (Price Incl.)</field>
<field name="name">TVA entrante - Frais de voiture - TVA 50% Non Deductible (Price Incl.)</field>
<field name="price_include" eval="1"/>
<field name="amount">1</field>
<field name="type">percent</field>
@@ -1996,7 +1996,7 @@
<record id="attn_VAT-IN-V82-CAR-INC-C1" model="account.tax.template">
<field name="sequence">711</field>
<field name="description">VAT-IN-V82-CAR-INC-C1</field>
<field name="name">Frais de voiture - VAT 50% Deductible</field>
<field name="name">Frais de voiture - TVA 50% Deductible</field>
<field name="parent_id" ref="attn_VAT-IN-V82-CAR-INC"/>
<field name="account_collected_id" ref="a411059"/>
<field name="account_paid_id" ref="a411059"/>
@@ -2018,7 +2018,7 @@
<record id="attn_VAT-IN-V82-CAR-INC-C2" model="account.tax.template">
<field name="sequence">712</field>
<field name="description">VAT-IN-V82-CAR-INC-C2</field>
<field name="name">Frais de voiture - Montant de la note de crédit</field>
<field name="name">TVA frais de voiture - Montant de la note de crédit</field>
<field name="parent_id" ref="attn_VAT-IN-V82-CAR-INC"/>
<field name="price_include" eval="1"/>
<field name="amount">0</field>
@@ -2035,7 +2035,7 @@
<record id="attn_VAT-IN-V82-CAR-EXC" model="account.tax.template">
<field name="sequence">720</field>
<field name="description">VAT-IN-V82-CAR-EXC</field>
<field name="name">TVA Entrant - Frais de voiture - VAT 50% Non Deductible (Price Excl.)</field>
<field name="name">TVA entrante - Frais de voiture - TVA 50% Non Déductible (Prix Excl.)</field>
<field name="price_include" eval="0"/>
<field name="amount">1</field>
<field name="type">percent</field>
@@ -2050,7 +2050,7 @@
<record id="attn_VAT-IN-V82-CAR-EXC-C1" model="account.tax.template">
<field name="sequence">721</field>
<field name="description">VAT-IN-V82-CAR-EXC-C1</field>
<field name="name">Frais de voiture - VAT 50% Non Deductible</field>
<field name="name">Frais de voiture - TVA 50% Non Deductible</field>
<field name="parent_id" ref="attn_VAT-IN-V82-CAR-EXC"/>
<field name="account_collected_id" ref="a64012"/>
<field name="account_paid_id" ref="a64012"/>
@@ -2072,7 +2072,7 @@
<record id="attn_VAT-IN-V82-CAR-EXC-C2" model="account.tax.template">
<field name="sequence">722</field>
<field name="description">VAT-IN-V82-CAR-EXC-C2</field>
<field name="name">Frais de voiture - VAT 50% Deductible (Price Excl.)</field>
<field name="name">Frais de voiture - TVA 50% Deductible (Prix Excl.)</field>
<field name="parent_id" ref="attn_VAT-IN-V82-CAR-EXC"/>
<field name="account_collected_id" ref="a411059"/>
<field name="account_paid_id" ref="a411059"/>
@@ -2092,7 +2092,7 @@
<record id="attn_VAT-IN-V82-CAR-EXC-C3" model="account.tax.template">
<field name="sequence">723</field>
<field name="description">VAT-IN-V82-CAR-EXC-C3</field>
<field name="name">Frais de voiture - Montant de la note de crédit (Price Excl.)</field>
<field name="name">TVA frais de voiture - Montant de la note de crédit (Prix Excl.)</field>
<field name="parent_id" ref="attn_VAT-IN-V82-CAR-EXC"/>
<field name="price_include" eval="0"/>
<field name="amount">0</field>
@@ -45,6 +45,7 @@
<field name="partner_id" ref="base.res_partner_9"/>
<field name="reference_type">bba</field>
<field name="reference">+++240/2838/42818+++</field>
<field name="date_invoice">2011-01-20</field>
</record>
<record id="invoice_1_line_1" model="account.invoice.line">
<field name="name">Otpez Laptop without OS</field>
+1 -1
View File
@@ -436,7 +436,7 @@ openerp.mail = function (session) {
*/
on_attachment_loaded: function (event, result) {
if (result.erorr || !result.id ) {
if (result.error || !result.id ) {
this.do_warn( session.web.qweb.render('mail.error_upload'), result.error);
this.attachment_ids = _.filter(this.attachment_ids, function (val) { return !val.upload; });
} else {
-8
View File
@@ -94,23 +94,15 @@ class mrp_production(osv.osv):
for sub_product in production.bom_id.sub_products:
product_uom_factor = product_uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, production.bom_id.product_uom.id)
qty1 = sub_product.product_qty
qty2 = production.product_uos and production.product_uos_qty or False
product_uos_factor = 0.0
if qty2 and production.bom_id.product_uos.id:
product_uos_factor = product_uom_obj._compute_qty(cr, uid, production.product_uos.id, production.product_uos_qty, production.bom_id.product_uos.id)
if sub_product.subproduct_type == 'variable':
if production.product_qty:
qty1 *= product_uom_factor / (production.bom_id.product_qty or 1.0)
if production.product_uos_qty:
qty2 *= product_uos_factor / (production.bom_id.product_uos_qty or 1.0)
data = {
'name': 'PROD:'+production.name,
'date': production.date_planned,
'product_id': sub_product.product_id.id,
'product_uom_qty': qty1,
'product_uom': sub_product.product_uom.id,
'product_uos_qty': qty2,
'product_uos': production.product_uos and production.product_uos.id or False,
'location_id': source,
'location_dest_id': production.location_dest_id.id,
'move_dest_id': production.move_prod_id.id,
+1 -1
View File
@@ -381,7 +381,7 @@ class PaymentTransaction(osv.Model):
('done', 'Done'), ('error', 'Error'),
('cancel', 'Canceled')
], 'Status', required=True,
track_visiblity='onchange', copy=False),
track_visibility='onchange', copy=False),
'state_message': fields.text('Message',
help='Field used to store error and/or validation messages for information'),
# payment
+1 -1
View File
@@ -91,7 +91,7 @@
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form" />
<field name="priority" eval="20"/>
<field name="groups_id" eval="[(4, ref('account.group_account_user'))]"/>
<field name="groups_id" eval="[(4, ref('account.group_account_invoice'))]"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" name="%(purchase.act_res_partner_2_supplier_invoices)d" type="action"
+27 -16
View File
@@ -984,6 +984,30 @@ class purchase_order(osv.osv):
return orders_info
def _set_po_lines_invoiced(self, cr, uid, ids, context=None):
for po in self.browse(cr, uid, ids, context=context):
is_invoiced = []
if po.invoice_method == 'picking':
# We determine the invoiced state of the PO line based on the invoiced state
# of the associated moves. This should cover all possible cases:
# - all moves are done and invoiced
# - a PO line is split into multiple moves (e.g. if multiple pickings): some
# pickings are done, some are in progress, some are cancelled
for po_line in po.order_line:
if (po_line.move_ids and
all(move.state in ('done', 'cancel') for move in po_line.move_ids) and
not all(move.state == 'cancel' for move in po_line.move_ids) and
all(move.invoice_state == 'invoiced' for move in po_line.move_ids if move.state == 'done')):
is_invoiced.append(po_line.id)
else:
for po_line in po.order_line:
if (po_line.invoice_lines and
all(line.invoice_id.state not in ['draft', 'cancel'] for line in po_line.invoice_lines)):
is_invoiced.append(po_line.id)
if is_invoiced:
self.pool['purchase.order.line'].write(cr, uid, is_invoiced, {'invoiced': True})
workflow.trg_write(uid, 'purchase.order', po.id, cr)
class purchase_order_line(osv.osv):
def _amount_line(self, cr, uid, ids, prop, arg, context=None):
@@ -1707,22 +1731,9 @@ class account_invoice(osv.Model):
else:
user_id = uid
po_ids = purchase_order_obj.search(cr, user_id, [('invoice_ids', 'in', ids)], context=context)
for order in purchase_order_obj.browse(cr, user_id, po_ids, context=context):
purchase_order_obj.message_post(cr, user_id, order.id, body=_("Invoice received"), context=context)
invoiced = []
shipped = True
# for invoice method manual or order, don't care about shipping state
# for invoices based on incoming shippment, beware of partial deliveries
if (order.invoice_method == 'picking' and
not all(picking.invoice_state in ['invoiced'] for picking in order.picking_ids)):
shipped = False
for po_line in order.order_line:
if (po_line.invoice_lines and
all(line.invoice_id.state not in ['draft', 'cancel'] for line in po_line.invoice_lines)):
invoiced.append(po_line.id)
if invoiced and shipped:
self.pool['purchase.order.line'].write(cr, user_id, invoiced, {'invoiced': True})
workflow.trg_write(user_id, 'purchase.order', order.id, cr)
for po_id in po_ids:
purchase_order_obj.message_post(cr, user_id, po_id, body=_("Invoice received"), context=context)
purchase_order_obj._set_po_lines_invoiced(cr, user_id, [po_id], context=context)
return res
def confirm_paid(self, cr, uid, ids, context=None):
+9 -1
View File
@@ -45,15 +45,23 @@ class stock_move(osv.osv):
res = super(stock_move, self).write(cr, uid, ids, vals, context=context)
from openerp import workflow
if vals.get('state') in ['done', 'cancel']:
po_to_check = []
for move in self.browse(cr, uid, ids, context=context):
if move.purchase_line_id and move.purchase_line_id.order_id:
order_id = move.purchase_line_id.order_id.id
order = move.purchase_line_id.order_id
order_id = order.id
# update linked purchase order as superuser as the warehouse
# user may not have rights to access purchase.order
if self.pool.get('purchase.order').test_moves_done(cr, uid, [order_id], context=context):
workflow.trg_validate(SUPERUSER_ID, 'purchase.order', order_id, 'picking_done', cr)
if self.pool.get('purchase.order').test_moves_except(cr, uid, [order_id], context=context):
workflow.trg_validate(SUPERUSER_ID, 'purchase.order', order_id, 'picking_cancel', cr)
if order_id not in po_to_check and vals['state'] == 'cancel' and order.invoice_method == 'picking':
po_to_check.append(order_id)
# Some moves which are cancelled might be part of a PO line which is partially
# invoiced, so we check if some PO line can be set on "invoiced = True".
if po_to_check:
self.pool.get('purchase.order')._set_po_lines_invoiced(cr, uid, po_to_check, context=context)
return res
def copy(self, cr, uid, id, default=None, context=None):
+36 -41
View File
@@ -30,6 +30,41 @@ class purchase_line_invoice(osv.osv_memory):
_name = 'purchase.order.line_invoice'
_description = 'Purchase Order Line Make Invoice'
def _make_invoice_by_partner(self, cr, uid, partner, orders, lines_ids, context=None):
"""
create a new invoice for one supplier
@param cr : Cursor
@param uid : Id of current user
@param partner : The object partner
@param orders : The set of orders to add in the invoice
@param lines : The list of line's id
"""
purchase_obj = self.pool.get('purchase.order')
account_jrnl_obj = self.pool.get('account.journal')
invoice_obj = self.pool.get('account.invoice')
name = orders and orders[0].name or ''
journal_id = account_jrnl_obj\
.search(cr, uid, [('type', '=', 'purchase')], context=None)
journal_id = journal_id and journal_id[0] or False
a = partner.property_account_payable.id
inv = {
'name': name,
'origin': name,
'type': 'in_invoice',
'journal_id': journal_id,
'reference': partner.ref,
'account_id': a,
'partner_id': partner.id,
'invoice_line': [(6, 0, lines_ids)],
'currency_id': orders[0].currency_id.id,
'comment': " \n".join([order.notes for order in orders if order.notes]),
'payment_term': orders[0].payment_term_id.id,
'fiscal_position': partner.property_account_position.id
}
inv_id = invoice_obj.create(cr, uid, inv, context=context)
purchase_obj.write(cr, uid, [order.id for order in orders], {'invoice_ids': [(4, inv_id)]}, context=context)
return inv_id
def makeInvoices(self, cr, uid, ids, context=None):
"""
@@ -48,49 +83,9 @@ class purchase_line_invoice(osv.osv_memory):
if record_ids:
res = False
invoices = {}
invoice_obj = self.pool.get('account.invoice')
purchase_obj = self.pool.get('purchase.order')
purchase_line_obj = self.pool.get('purchase.order.line')
invoice_line_obj = self.pool.get('account.invoice.line')
account_jrnl_obj = self.pool.get('account.journal')
def multiple_order_invoice_notes(orders):
notes = ""
for order in orders:
notes += "%s \n" % order.notes
return notes
def make_invoice_by_partner(partner, orders, lines_ids):
"""
create a new invoice for one supplier
@param partner : The object partner
@param orders : The set of orders to add in the invoice
@param lines : The list of line's id
"""
name = orders and orders[0].name or ''
journal_id = account_jrnl_obj.search(cr, uid, [('type', '=', 'purchase')], context=None)
journal_id = journal_id and journal_id[0] or False
a = partner.property_account_payable.id
inv = {
'name': name,
'origin': name,
'type': 'in_invoice',
'journal_id':journal_id,
'reference' : partner.ref,
'account_id': a,
'partner_id': partner.id,
'invoice_line': [(6,0,lines_ids)],
'currency_id' : orders[0].currency_id.id,
'comment': multiple_order_invoice_notes(orders),
'payment_term': orders[0].payment_term_id.id,
'fiscal_position': partner.property_account_position.id
}
inv_id = invoice_obj.create(cr, uid, inv)
for order in orders:
order.write({'invoice_ids': [(4, inv_id)]})
return inv_id
for line in purchase_line_obj.browse(cr, uid, record_ids, context=context):
if (not line.invoiced) and (line.state not in ('draft', 'cancel')):
@@ -108,7 +103,7 @@ class purchase_line_invoice(osv.osv_memory):
il = map(lambda x: x[1], result)
orders = list(set(map(lambda x : x[0].order_id, result)))
res.append(make_invoice_by_partner(orders[0].partner_id, orders, il))
res.append(self._make_invoice_by_partner(cr, uid, orders[0].partner_id, orders, il, context=context))
return {
'domain': "[('id','in', ["+','.join(map(str,res))+"])]",
+2
View File
@@ -89,6 +89,8 @@ Example: 10% for retailers, promotion of 5 EUR on this product, etc."""),
return {}
def onchange_task_work(self, cr, uid, ids, task_work, context=None):
if not task_work:
return {'value': {}}
return {'value': {
'module_project_timesheet': task_work,
'module_sale_service': task_work,
+9 -1
View File
@@ -21,6 +21,7 @@
from datetime import datetime, timedelta
import time
from openerp import SUPERUSER_ID
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
@@ -1104,7 +1105,14 @@ class sale_order_line(osv.osv):
else:
fpos = self.pool['account.fiscal.position'].browse(cr, uid, fiscal_position)
if update_tax: # The quantity only have changed
result['tax_id'] = self.pool['account.fiscal.position'].map_tax(cr, uid, fpos, product_obj.taxes_id)
# The superuser is used by website_sale in order to create a sale order. We need to make
# sure we only select the taxes related to the company of the partner. This should only
# apply if the partner is linked to a company.
if uid == SUPERUSER_ID and partner.company_id:
taxes = product_obj.taxes_id.filtered(lambda r: r.company_id == partner.company_id)
else:
taxes = product_obj.taxes_id
result['tax_id'] = self.pool['account.fiscal.position'].map_tax(cr, uid, fpos, taxes)
if not flag:
result['name'] = Product.name_get(cr, uid, [product_obj.id], context=ctx_product)[0][1]
+2
View File
@@ -432,6 +432,8 @@ class stock_move(osv.osv):
fp = move.picking_id.sale_id.fiscal_position
res = self.pool.get("account.invoice.line").product_id_change(cr, uid, [], move.product_id.id, None, partner_id=move.picking_id.partner_id.id, fposition_id=(fp and fp.id), context=context)
extra_move_tax[0, move.product_id] = [(6, 0, res['value']['invoice_line_tax_id'])]
else:
extra_move_tax[0, move.product_id] = [(6, 0, [x.id for x in move.product_id.product_tmpl_id.taxes_id])]
return (is_extra_move, extra_move_tax)
def _get_taxes(self, cr, uid, move, context=None):
+5 -5
View File
@@ -3437,11 +3437,11 @@ class stock_warehouse(osv.osv):
seq_obj = self.pool.get('ir.sequence')
picking_type_obj = self.pool.get('stock.picking.type')
#create new sequences
in_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': warehouse.name + _(' Sequence in'), 'prefix': warehouse.code + '/IN/', 'padding': 5}, context=context)
out_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': warehouse.name + _(' Sequence out'), 'prefix': warehouse.code + '/OUT/', 'padding': 5}, context=context)
pack_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': warehouse.name + _(' Sequence packing'), 'prefix': warehouse.code + '/PACK/', 'padding': 5}, context=context)
pick_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': warehouse.name + _(' Sequence picking'), 'prefix': warehouse.code + '/PICK/', 'padding': 5}, context=context)
int_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': warehouse.name + _(' Sequence internal'), 'prefix': warehouse.code + '/INT/', 'padding': 5}, context=context)
in_seq_id = seq_obj.create(cr, SUPERUSER_ID, {'name': warehouse.name + _(' Sequence in'), 'prefix': warehouse.code + '/IN/', 'padding': 5}, context=context)
out_seq_id = seq_obj.create(cr, SUPERUSER_ID, {'name': warehouse.name + _(' Sequence out'), 'prefix': warehouse.code + '/OUT/', 'padding': 5}, context=context)
pack_seq_id = seq_obj.create(cr, SUPERUSER_ID, {'name': warehouse.name + _(' Sequence packing'), 'prefix': warehouse.code + '/PACK/', 'padding': 5}, context=context)
pick_seq_id = seq_obj.create(cr, SUPERUSER_ID, {'name': warehouse.name + _(' Sequence picking'), 'prefix': warehouse.code + '/PICK/', 'padding': 5}, context=context)
int_seq_id = seq_obj.create(cr, SUPERUSER_ID, {'name': warehouse.name + _(' Sequence internal'), 'prefix': warehouse.code + '/INT/', 'padding': 5}, context=context)
wh_stock_loc = warehouse.lot_stock_id
wh_input_stock_loc = warehouse.wh_input_stock_loc_id
+1 -1
View File
@@ -653,7 +653,7 @@
<header>
<button name="action_confirm" states="draft" string="Mark as Todo" type="object" class="oe_highlight" groups="base.group_user"/>
<button name="action_assign" states="confirmed" string="Check Availability" type="object" class="oe_highlight" groups="base.group_user"/>
<button name="rereserve_pick" states="waiting,partially_available" string="Recheck Availability" type="object" class="oe_highlight" groups="base.group_user"/>
<button name="rereserve_pick" string="Recheck Availability" type="object" class="oe_highlight" groups="base.group_user" attrs="{'invisible': ['|', ('state', 'not in', ('waiting', 'partially_available')), ('pack_operation_exist','=',True)]}"/>
<button name="force_assign" states="confirmed,waiting,partially_available" string="Force Availability" type="object" groups="base.group_user"/>
<button name="do_enter_transfer_details" states="assigned,partially_available" string="Transfer" groups="stock.group_stock_user" type="object" class="oe_highlight"/>
<button name="do_print_picking" string="Print Picking List" groups="stock.group_stock_user" type="object" attrs="{'invisible': ['|', ('picking_type_code', '=', 'outgoing'), ('state', '!=', 'assigned')]}"/>
@@ -716,9 +716,9 @@ openerp.web_calendar = function(instance) {
var color_field = self.fields[self.color_field];
_.each(events, function (e) {
var key, val = null;
if (self.fields[self.color_field].type == "selection"){
if (color_field.type == "selection") {
key = e[self.color_field];
val = _.find( self.fields[self.color_field].selection, function(name){ return name[0] === key;});
val = _.find(color_field.selection, function(name){ return name[0] === key;});
}else{
key = e[self.color_field][0];
val = e[self.color_field];
@@ -743,7 +743,7 @@ openerp.web_calendar = function(instance) {
self.sidebar.filter.set_filters();
events = $.map(events, function (e) {
var key = self.fields[self.color_field].type == "selection"?e[self.color_field]:e[self.color_field][0];
var key = color_field.type == "selection" ? e[self.color_field] : e[self.color_field][0];
if (_.contains(self.now_filter_ids, key) && self.all_filters[key].is_checked) {
return e;
}
-4
View File
@@ -4,10 +4,6 @@ import os
import openerp.tests
class TestUi(openerp.tests.HttpCase):
def test_01_jsfile_ui_hello(self):
self.phantom_jsfile(os.path.join(os.path.dirname(__file__), 'test_ui_hello.js'))
def test_02_jsfile_ui_load(self):
self.phantom_jsfile(os.path.join(os.path.dirname(__file__), 'test_ui_load.js'))
def test_03_js_public(self):
self.phantom_js('/',"console.log('ok')","console")
def test_04_js_admin(self):
-5
View File
@@ -1,5 +0,0 @@
console.log("hello from test_phantom_hello.js");
console.log("ok");
// For a failure use:
//console.log("error");
phantom.exit();
-5
View File
@@ -1,5 +0,0 @@
// Load helper
var system = require('system');
phantom.injectJs(system.args[1]);
pt = new PhantomTest();
pt.run("/", "console.log('ok')", "console");
+2
View File
@@ -820,6 +820,8 @@ class website_sale(http.Controller):
# clean context and session, then redirect to the confirmation page
request.website.sale_reset(context=context)
if tx and tx.state == 'draft':
return request.redirect('/shop')
return request.redirect('/shop/confirmation')
@@ -4,6 +4,7 @@ from openerp.osv import orm, fields
from openerp import SUPERUSER_ID
from openerp.addons import decimal_precision
from openerp.exceptions import ValidationError
from openerp.tools.translate import _
class delivery_carrier(orm.Model):
@@ -110,7 +111,10 @@ class SaleOrder(orm.Model):
def _get_errors(self, cr, uid, order, context=None):
errors = super(SaleOrder, self)._get_errors(cr, uid, order, context=context)
if not self._get_delivery_methods(cr, uid, order, context=context):
errors.append(('No delivery method available', 'There is no available delivery method for your order'))
errors.append(
(_('Sorry, we are unable to ship your order'),
_('No shipping method is available for your current order and shipping address. '
'Please contact us for more information.')))
return errors
def _get_website_data(self, cr, uid, order, context=None):
+11
View File
@@ -0,0 +1,11 @@
France, 07/30/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,
Anthony Ruhier <anthony.ruhier@gmail.com> https://github.com/anthony25
+1 -1
View File
@@ -623,7 +623,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">idea.category/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
+1 -4
View File
@@ -287,7 +287,6 @@ class ir_translation(osv.osv):
def _set_ids(self, cr, uid, name, tt, lang, ids, value, src=None):
self._get_ids.clear_cache(self)
self.__get_source.clear_cache(self)
original_module = self.pool[name.split(',')[0]]._original_module
cr.execute('update ir_translation '
'set value=%s '
' , src=%s '
@@ -295,10 +294,9 @@ class ir_translation(osv.osv):
'where lang=%s '
'and type=%s '
'and name=%s '
'and module=%s '
'and res_id IN %s '
'returning res_id',
(value,src,'translated',lang,tt,name,original_module,tuple(ids),))
(value,src,'translated',lang,tt,name,tuple(ids),))
existing_ids = [x[0] for x in cr.fetchall()]
@@ -308,7 +306,6 @@ class ir_translation(osv.osv):
'type':tt,
'name':name,
'res_id':id,
'module':original_module,
'value':value,
'src':src,
'state':'translated'
+1 -1
View File
@@ -709,7 +709,7 @@ def parse_m2m(commands):
for command in commands:
if isinstance(command, (tuple, list)):
if command[0] in (1, 4):
ids.append(command[2])
ids.append(command[1])
elif command[0] == 5:
ids = []
elif command[0] == 6:
+11 -11
View File
@@ -49,7 +49,7 @@ class test_integer_field(CreatorCase):
def test_0(self):
self.assertEqual(
self.export(0),
[[False]])
[[u'0']])
def test_basic_value(self):
self.assertEqual(
@@ -72,7 +72,7 @@ class test_float_field(CreatorCase):
def test_0(self):
self.assertEqual(
self.export(0.0),
[[False]])
[[u'0.0']])
def test_epsilon(self):
self.assertEqual(
@@ -100,14 +100,14 @@ class test_decimal_field(CreatorCase):
def test_0(self):
self.assertEqual(
self.export(0.0),
[[False]])
[[u'0.0']])
def test_epsilon(self):
""" epsilon gets sliced to 0 due to precision
"""
self.assertEqual(
self.export(0.000000000027),
[[False]])
[[u'0.0']])
def test_negative(self):
self.assertEqual(
@@ -129,7 +129,7 @@ class test_string_field(CreatorCase):
def test_empty(self):
self.assertEqual(
self.export(""),
[[False]])
[['']])
def test_within_bounds(self):
self.assertEqual(
self.export("foobar"),
@@ -148,7 +148,7 @@ class test_unbound_string_field(CreatorCase):
def test_empty(self):
self.assertEqual(
self.export(""),
[[False]])
[['']])
def test_small(self):
self.assertEqual(
self.export("foobar"),
@@ -170,7 +170,7 @@ class test_text(CreatorCase):
def test_empty(self):
self.assertEqual(
self.export(""),
[[False]])
[['']])
def test_small(self):
self.assertEqual(
self.export("foobar"),
@@ -190,7 +190,7 @@ class test_date(CreatorCase):
def test_empty(self):
self.assertEqual(
self.export(False),
[[False]])
[['']])
def test_basic(self):
self.assertEqual(
self.export('2011-11-07'),
@@ -202,7 +202,7 @@ class test_datetime(CreatorCase):
def test_empty(self):
self.assertEqual(
self.export(False),
[[False]])
[['']])
def test_basic(self):
self.assertEqual(
self.export('2011-11-07 21:05:48'),
@@ -265,7 +265,7 @@ class test_selection_function(CreatorCase):
def test_empty(self):
self.assertEqual(
self.export(False),
[[None]])
[['']])
def test_value(self):
# FIXME: selection functions export the *value* itself
@@ -278,7 +278,7 @@ class test_selection_function(CreatorCase):
# fucking hell
self.assertEqual(
self.export(0),
[[None]])
[['']])
class test_m2o(CreatorCase):
model_name = 'export.many2one'
+23 -18
View File
@@ -781,9 +781,9 @@ class Field(object):
""" convert ``value`` from the cache to a valid value for export. The
parameter ``env`` is given for managing translations.
"""
if env.context.get('export_raw_data'):
return value
return bool(value) and ustr(value)
if not value:
return ''
return value if env.context.get('export_raw_data') else ustr(value)
def convert_to_display_name(self, value, record=None):
""" convert ``value`` from the cache to a suitable display name. """
@@ -1035,6 +1035,11 @@ class Integer(Field):
# special case, when an integer field is used as inverse for a one2many
records._cache[self] = value.id or 0
def convert_to_export(self, value, env):
if value or value == 0:
return value if env.context.get('export_raw_data') else ustr(value)
return ''
class Float(Field):
""" The precision digits are given by the attribute
@@ -1082,6 +1087,11 @@ class Float(Field):
digits = self.digits
return float_round(value, precision_digits=digits[1]) if digits else value
def convert_to_export(self, value, env):
if value or value == 0.0:
return value if env.context.get('export_raw_data') else ustr(value)
return ''
class _String(Field):
""" Abstract class for string fields. """
@@ -1093,11 +1103,6 @@ class _String(Field):
_related_translate = property(attrgetter('translate'))
_description_translate = property(attrgetter('translate'))
def convert_to_export(self, value, env):
if env.context.get('export_raw_data'):
return value if value else None
return bool(value) and ustr(value)
class Char(_String):
""" Basic string field, can be length-limited, usually displayed as a
@@ -1216,9 +1221,9 @@ class Date(Field):
return self.to_string(value)
def convert_to_export(self, value, env):
if value and env.context.get('export_raw_data'):
return self.from_string(value) or None
return bool(value) and ustr(value)
if not value:
return ''
return self.from_string(value) if env.context.get('export_raw_data') else ustr(value)
class Datetime(Field):
@@ -1285,9 +1290,9 @@ class Datetime(Field):
return self.to_string(value)
def convert_to_export(self, value, env):
if value and env.context.get('export_raw_data'):
return self.from_string(value) or None
return bool(value) and ustr(value)
if not value:
return ''
return self.from_string(value) if env.context.get('export_raw_data') else ustr(value)
def convert_to_display_name(self, value, record=None):
assert record, 'Record expected'
@@ -1395,7 +1400,7 @@ class Selection(Field):
def convert_to_export(self, value, env):
if not isinstance(self.selection, list):
# FIXME: this reproduces an existing buggy behavior!
return value or None
return value if value else ''
for item in self._description_selection(env):
if item[0] == value:
return item[1]
@@ -1432,7 +1437,7 @@ class Reference(Selection):
return "%s,%s" % (value._name, value.id) if value else False
def convert_to_export(self, value, env):
return value.name_get()[0][1] if value else None
return value.name_get()[0][1] if value else ''
def convert_to_display_name(self, value, record=None):
return ustr(value and value.display_name)
@@ -1572,7 +1577,7 @@ class Many2one(_Relational):
return value.id
def convert_to_export(self, value, env):
return value.name_get()[0][1] if value else None
return value.name_get()[0][1] if value else ''
def convert_to_display_name(self, value, record=None):
return ustr(value.display_name)
@@ -1678,7 +1683,7 @@ class _RelationalMulti(_Relational):
return result
def convert_to_export(self, value, env):
return ','.join(name for id, name in value.name_get()) if value else None
return ','.join(name for id, name in value.name_get()) if value else ''
def convert_to_display_name(self, value, record=None):
raise NotImplementedError()
+2 -2
View File
@@ -76,7 +76,7 @@ _logger = logging.getLogger(__name__)
_schema = logging.getLogger(__name__ + '.schema')
_unlink = logging.getLogger(__name__ + '.unlink')
regex_order = re.compile('^( *([a-z0-9:_]+|"[a-z0-9:_]+")( *desc| *asc)?( *, *|))+$', re.I)
regex_order = re.compile('^(\s*([a-z0-9:_]+|"[a-z0-9:_]+")(\s+(desc|asc))?\s*(,|$))+(?<!,)$', re.I)
regex_object_name = re.compile(r'^[a-z0-9_.]+$')
onchange_v7 = re.compile(r"^(\w+)\((.*)\)$")
@@ -3244,7 +3244,7 @@ class BaseModel(object):
result = self.read(list(fnames), load='_classic_write')
# check the cache, and update it if necessary
if not self._cache.contains(field):
if field not in self._cache:
for values in result:
record = self.browse(values.pop('id'))
record._cache.update(record._convert_to_cache(values, validate=False))
-17
View File
@@ -351,23 +351,6 @@ class HttpCase(TransactionCase):
openerp.tools.misc.dumpstacks()
t0 = t1
def phantom_jsfile(self, jsfile, timeout=60, **kw):
options = {
'timeout' : timeout,
'port': PORT,
'db': get_db_name(),
'session_id': self.session_id,
}
options.update(kw)
phantomtest = os.path.join(os.path.dirname(__file__), 'phantomtest.js')
# phantom.args[0] == phantomtest path
# phantom.args[1] == options
cmd = [
'phantomjs',
jsfile, phantomtest, json.dumps(options)
]
self.phantom_run(cmd, timeout)
def phantom_js(self, url_path, code, ready="window", login=None, timeout=60, **kw):
""" Test js code running in the browser
- optionnally log as 'login'