[MERGE] forward port of branch saas-6 up to e941c0f
@@ -685,8 +685,7 @@ Paulius Sladkevičius <komsas@gmail.com> Paulius Sladkevičius <paulius@hacbee.c
|
||||
Paulius Sladkevičius <komsas@gmail.com> Paulius Sladkevičius <paulius@hbee.eu>
|
||||
Paulius Sladkevičius <komsas@gmail.com> Paulius Sladkevičius <paulius@inovera.lt>
|
||||
Paulius Sladkevičius <komsas@gmail.com> paulius <>
|
||||
Pedro Manuel Baeza <pedro.baeza@serviciosbaeza.com>
|
||||
Pedro Manuel Baeza <pedro.baeza@serviciosbaeza.com> <pedro.baeza@gmail.com>
|
||||
Pedro Manuel Baeza <pedro.baeza@gmail.com> <pedro.baeza@serviciosbaeza.com>
|
||||
Peter Langenberg <pl@agaplan.eu>
|
||||
Peter Langenberg <pl@agaplan.eu> Peter Langenberg <>
|
||||
Philu <philubert@gmail.com> philubert@gmail.com <>
|
||||
|
||||
@@ -262,7 +262,6 @@ class account_invoice(models.Model):
|
||||
store=True, readonly=True, compute='_compute_amount')
|
||||
amount_total = fields.Float(string='Total', digits=dp.get_precision('Account'),
|
||||
store=True, readonly=True, compute='_compute_amount')
|
||||
|
||||
currency_id = fields.Many2one('res.currency', string='Currency',
|
||||
required=True, readonly=True, states={'draft': [('readonly', False)]},
|
||||
default=_default_currency, track_visibility='always')
|
||||
@@ -800,7 +799,7 @@ class account_invoice(models.Model):
|
||||
# create the analytical lines, one move line per invoice line
|
||||
iml = inv._get_analytic_lines()
|
||||
# check if taxes are all computed
|
||||
compute_taxes = account_invoice_tax.compute(inv)
|
||||
compute_taxes = account_invoice_tax.compute(inv.with_context(lang=inv.partner_id.lang))
|
||||
inv.check_tax_lines(compute_taxes)
|
||||
|
||||
# I disabled the check_total feature
|
||||
@@ -831,7 +830,7 @@ class account_invoice(models.Model):
|
||||
# create one move line for the total and possibly adjust the other lines amount
|
||||
total, total_currency, iml = inv.with_context(ctx).compute_invoice_totals(company_currency, ref, iml)
|
||||
|
||||
name = inv.name or inv.supplier_invoice_number or '/'
|
||||
name = inv.supplier_invoice_number or inv.name or '/'
|
||||
totlines = []
|
||||
if inv.payment_term:
|
||||
totlines = inv.with_context(ctx).payment_term.compute(total, date_invoice)[0]
|
||||
@@ -1131,7 +1130,7 @@ class account_invoice(models.Model):
|
||||
else:
|
||||
ref = self.number
|
||||
partner = self.partner_id._find_accounting_partner(self.partner_id)
|
||||
name = name or self.invoice_line.name or self.number
|
||||
name = name or self.invoice_line[0].name or self.number
|
||||
# Pay attention to the sign for both debit/credit AND amount_currency
|
||||
l1 = {
|
||||
'name': name,
|
||||
@@ -1216,13 +1215,19 @@ class account_invoice_line(models.Model):
|
||||
|
||||
@api.one
|
||||
@api.depends('price_unit', 'discount', 'invoice_line_tax_id', 'quantity',
|
||||
'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id')
|
||||
'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id', 'invoice_id.company_id')
|
||||
def _compute_price(self):
|
||||
price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
|
||||
taxes = self.invoice_line_tax_id.compute_all(price, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id)
|
||||
self.price_subtotal = taxes['total']
|
||||
self.price_subtotal = price_subtotal_signed = taxes['total']
|
||||
if self.invoice_id.currency_id != self.invoice_id.company_id.currency_id:
|
||||
price_subtotal_signed = self.invoice_id.currency_id.with_context(date=self.invoice_id.date_invoice).compute(price_subtotal_signed, self.company_id.currency_id)
|
||||
sign = self.invoice_id.type in ['in_invoice', 'out_refund'] and -1 or 1
|
||||
self.price_subtotal_signed = price_subtotal_signed * sign
|
||||
|
||||
if self.invoice_id:
|
||||
self.price_subtotal = self.invoice_id.currency_id.round(self.price_subtotal)
|
||||
self.price_subtotal_signed = self.invoice_id.currency_id.round(self.price_subtotal_signed)
|
||||
|
||||
@api.model
|
||||
def _default_price_unit(self):
|
||||
@@ -1273,6 +1278,9 @@ class account_invoice_line(models.Model):
|
||||
default=_default_price_unit)
|
||||
price_subtotal = fields.Float(string='Amount', digits= dp.get_precision('Account'),
|
||||
store=True, readonly=True, compute='_compute_price')
|
||||
price_subtotal_signed = fields.Float(string='Amount Signed', digits=0,
|
||||
store=True, readonly=True, compute='_compute_price',
|
||||
help="Total amount in the currency of the company, negative for credit notes.")
|
||||
quantity = fields.Float(string='Quantity', digits= dp.get_precision('Product Unit of Measure'),
|
||||
required=True, default=1)
|
||||
discount = fields.Float(string='Discount (%)', digits= dp.get_precision('Discount'),
|
||||
@@ -1304,7 +1312,7 @@ class account_invoice_line(models.Model):
|
||||
@api.multi
|
||||
def product_id_change(self, product, uom_id, qty=0, name='', type='out_invoice',
|
||||
partner_id=False, fposition_id=False, price_unit=False, currency_id=False,
|
||||
company_id=None):
|
||||
company_id=None, date_invoice=None):
|
||||
context = self._context
|
||||
company_id = company_id if company_id is not None else context.get('company_id', False)
|
||||
self = self.with_context(company_id=company_id, force_company=company_id)
|
||||
@@ -1362,7 +1370,7 @@ class account_invoice_line(models.Model):
|
||||
if company.currency_id != currency:
|
||||
if type in ('in_invoice', 'in_refund'):
|
||||
values['price_unit'] = product.standard_price
|
||||
values['price_unit'] = values['price_unit'] * currency.rate
|
||||
values['price_unit'] = values['price_unit'] * currency.with_context(dict(context or {}, date=date_invoice)).rate
|
||||
|
||||
if values['uos_id'] and values['uos_id'] != product.uom_id.id:
|
||||
values['price_unit'] = self.env['product.uom']._compute_price(
|
||||
@@ -1372,14 +1380,14 @@ class account_invoice_line(models.Model):
|
||||
|
||||
@api.multi
|
||||
def uos_id_change(self, product, uom, qty=0, name='', type='out_invoice', partner_id=False,
|
||||
fposition_id=False, price_unit=False, currency_id=False, company_id=None):
|
||||
fposition_id=False, price_unit=False, currency_id=False, company_id=None, date_invoice=None):
|
||||
context = self._context
|
||||
company_id = company_id if company_id != None else context.get('company_id', False)
|
||||
self = self.with_context(company_id=company_id)
|
||||
|
||||
result = self.product_id_change(
|
||||
product, uom, qty, name, type, partner_id, fposition_id, price_unit,
|
||||
currency_id, company_id=company_id,
|
||||
currency_id, company_id=company_id, date_invoice=date_invoice
|
||||
)
|
||||
warning = {}
|
||||
if not uom:
|
||||
@@ -1454,7 +1462,7 @@ class account_invoice_line(models.Model):
|
||||
# Set the tax field according to the account and the fiscal position
|
||||
#
|
||||
@api.multi
|
||||
def onchange_account_id(self, product_id, partner_id, inv_type, fposition_id, account_id):
|
||||
def onchange_account_id(self, product_id, partner_id, inv_type, fposition_id, account_id, date_invoice=None):
|
||||
if not account_id:
|
||||
return {}
|
||||
unique_tax_ids = []
|
||||
@@ -1464,7 +1472,7 @@ class account_invoice_line(models.Model):
|
||||
unique_tax_ids = fpos.map_tax(account.tax_ids).ids
|
||||
else:
|
||||
product_change_result = self.product_id_change(product_id, False, type=inv_type,
|
||||
partner_id=partner_id, fposition_id=fposition_id, company_id=account.company_id.id)
|
||||
partner_id=partner_id, fposition_id=fposition_id, company_id=account.company_id.id, date_invoice=date_invoice)
|
||||
if 'invoice_line_tax_id' in product_change_result.get('value', {}):
|
||||
unique_tax_ids = product_change_result['value']['invoice_line_tax_id']
|
||||
return {'value': {'invoice_line_tax_id': unique_tax_ids}}
|
||||
@@ -1521,12 +1529,11 @@ class account_invoice_tax(models.Model):
|
||||
|
||||
@api.multi
|
||||
def amount_change(self, amount, currency_id=False, company_id=False, date_invoice=False):
|
||||
factor = self.factor_tax if self else 1
|
||||
company = self.env['res.company'].browse(company_id)
|
||||
if currency_id and company.currency_id:
|
||||
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 * factor, company.currency_id, round=False)
|
||||
amount = currency.compute(amount, company.currency_id, round=False)
|
||||
return {'value': {'tax_amount': amount}}
|
||||
|
||||
@api.v8
|
||||
|
||||
@@ -60,18 +60,18 @@
|
||||
<group>
|
||||
<group>
|
||||
<field name="product_id"
|
||||
on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, context)" context="{'partner_id': parent.partner_id}"/>
|
||||
on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, parent.date_invoice, context)" context="{'partner_id': parent.partner_id}"/>
|
||||
<label for="quantity"/>
|
||||
<div>
|
||||
<field name="quantity" class="oe_inline"/>
|
||||
<field name="uos_id" class="oe_inline" groups="product.group_uom"
|
||||
on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, context)"/>
|
||||
on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, parent.date_invoice, context)"/>
|
||||
</div>
|
||||
<field name="price_unit"/>
|
||||
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||
</group>
|
||||
<group>
|
||||
<field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '<>', 'view')]" name="account_id" on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)" groups="account.group_account_user"/>
|
||||
<field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '<>', 'view')]" name="account_id" on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id, parent.date_invoice)" groups="account.group_account_user"/>
|
||||
<field name="invoice_line_tax_id" context="{'type':parent.get('type')}" domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]" widget="many2many_tags"/>
|
||||
<field domain="[('type','<>','view'), ('company_id', '=', parent.company_id)], ('state','not in',('close','cancelled'))]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
|
||||
@@ -209,17 +209,17 @@
|
||||
<tree string="Invoice lines" editable="bottom">
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="product_id"
|
||||
on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, context)" context="{'partner_id': parent.partner_id}"/>
|
||||
on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, parent.date_invoice, context)" context="{'partner_id': parent.partner_id}"/>
|
||||
<field name="name"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="account_id" groups="account.group_account_user"
|
||||
domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '=', 'other')]"
|
||||
on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)"/>
|
||||
on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id, parent.date_invoice)"/>
|
||||
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"
|
||||
domain="[('type','!=','view'), ('company_id', '=', parent.company_id), ('state','not in',('close','cancelled'))]"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uos_id" groups="product.group_uom"
|
||||
on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, context)"/>
|
||||
on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, parent.date_invoice, context)"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||
<field name="invoice_line_tax_id" widget="many2many_tags" context="{'type':parent.type}"
|
||||
@@ -266,7 +266,7 @@
|
||||
<group>
|
||||
<field domain="[('partner_id', '=', partner_id)]" name="partner_bank_id" on_change="onchange_partner_bank(partner_bank_id)"/>
|
||||
<field name="user_id" string="Responsible" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'account.group_account_invoice']}"/>
|
||||
<field name="name" invisible="1"/>
|
||||
<field name="name" attrs="{'invisible': [('type', '=', 'in_invoice')]}"/>
|
||||
<field name="payment_term" options="{'no_create': True}" on_change="onchange_payment_term_date_invoice(payment_term, date_invoice)"/>
|
||||
</group>
|
||||
<group>
|
||||
@@ -367,17 +367,17 @@
|
||||
<tree string="Invoice Lines" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="product_id"
|
||||
on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, context)"/>
|
||||
on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, parent.date_invoice, context)"/>
|
||||
<field name="name"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="account_id" groups="account.group_account_user"
|
||||
domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '=', 'other')]"
|
||||
on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)"/>
|
||||
on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id, parent.date_invoice)"/>
|
||||
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"
|
||||
domain="[('type','!=','view'), ('company_id', '=', parent.company_id), ('state','not in',('close','cancelled'))]"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uos_id" groups="product.group_uom"
|
||||
on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, context)"/>
|
||||
on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, parent.company_id, parent.date_invoice, context)"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||
<field name="invoice_line_tax_id" widget="many2many_tags" context="{'type':parent.type}"
|
||||
@@ -516,6 +516,19 @@
|
||||
<field name="search_view_id" ref="view_account_invoice_filter"/>
|
||||
</record>
|
||||
|
||||
<record id="action_invoice_refund_out_tree_tree" model="ir.actions.act_window.view">
|
||||
<field eval="1" name="sequence"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="act_window_id" ref="action_invoice_refund_out_tree"/>
|
||||
</record>
|
||||
|
||||
<record id="action_invoice_refund_out_tree_form" model="ir.actions.act_window.view">
|
||||
<field eval="2" name="sequence"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="invoice_form"/>
|
||||
<field name="act_window_id" ref="action_invoice_refund_out_tree"/>
|
||||
</record>
|
||||
|
||||
<record id="action_invoice_tree_pending_invoice" model="ir.actions.act_window">
|
||||
<field name="name">Pending Invoice</field>
|
||||
<field name="res_model">account.invoice</field>
|
||||
|
||||
@@ -813,7 +813,7 @@ class account_move_line(osv.osv):
|
||||
# For already reconciled lines, don't use amount_residual(_currency)
|
||||
if line.account_id.type == 'liquidity':
|
||||
amount = abs(debit - credit)
|
||||
amount_currency = line.amount_currency
|
||||
amount_currency = abs(line.amount_currency)
|
||||
|
||||
# Get right debit / credit:
|
||||
target_currency = target_currency or company_currency
|
||||
@@ -851,7 +851,12 @@ class account_move_line(osv.osv):
|
||||
ret.append(ret_line)
|
||||
return ret
|
||||
|
||||
def list_partners_to_reconcile(self, cr, uid, context=None):
|
||||
|
||||
def list_partners_to_reconcile(self, cr, uid, context=None, filter_domain=False):
|
||||
line_ids = []
|
||||
if filter_domain:
|
||||
line_ids = self.search(cr, uid, filter_domain, context=context)
|
||||
where_clause = filter_domain and "AND l.id = ANY(%s)" or ""
|
||||
cr.execute(
|
||||
"""SELECT partner_id FROM (
|
||||
SELECT l.partner_id, p.last_reconciliation_date, SUM(l.debit) AS debit, SUM(l.credit) AS credit, MAX(l.create_date) AS max_date
|
||||
@@ -861,10 +866,12 @@ class account_move_line(osv.osv):
|
||||
WHERE a.reconcile IS TRUE
|
||||
AND l.reconcile_id IS NULL
|
||||
AND l.state <> 'draft'
|
||||
%s
|
||||
GROUP BY l.partner_id, p.last_reconciliation_date
|
||||
) AS s
|
||||
WHERE debit > 0 AND credit > 0 AND (last_reconciliation_date IS NULL OR max_date > last_reconciliation_date)
|
||||
ORDER BY last_reconciliation_date""")
|
||||
ORDER BY last_reconciliation_date"""
|
||||
% where_clause, (line_ids,))
|
||||
ids = [x[0] for x in cr.fetchall()]
|
||||
if not ids:
|
||||
return []
|
||||
|
||||
@@ -251,13 +251,14 @@ class res_partner(osv.osv):
|
||||
def _journal_item_count(self, cr, uid, ids, field_name, arg, context=None):
|
||||
MoveLine = self.pool('account.move.line')
|
||||
AnalyticAccount = self.pool('account.analytic.account')
|
||||
return {
|
||||
partner_id: {
|
||||
'journal_item_count': MoveLine.search_count(cr, uid, [('partner_id', '=', partner_id)], context=context),
|
||||
'contracts_count': AnalyticAccount.search_count(cr,uid, [('partner_id', '=', partner_id)], context=context)
|
||||
}
|
||||
for partner_id in ids
|
||||
}
|
||||
results = {}
|
||||
for partner_id in ids:
|
||||
results[partner_id] = {}
|
||||
if 'contracts_count' in field_name:
|
||||
results[partner_id]['contracts_count'] = AnalyticAccount.search_count(cr, uid, [('partner_id', '=', partner_id)], context=context)
|
||||
if 'journal_item_count' in field_name:
|
||||
results[partner_id]['journal_item_count'] = MoveLine.search_count(cr, uid, [('partner_id', 'child_of', partner_id)], context=context)
|
||||
return results
|
||||
|
||||
def has_something_to_reconcile(self, cr, uid, partner_id, context=None):
|
||||
'''
|
||||
|
||||
@@ -28,21 +28,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_template_search_view" model="ir.ui.view">
|
||||
<field name="name">product.template.search</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="mode">primary</field>
|
||||
<field name="inherit_id" ref="product.product_template_search_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="product_variant_ids" position="after">
|
||||
<field name="categ_id"/>
|
||||
</field>
|
||||
<xpath expr="//group[@string='Group by...']" position="inside">
|
||||
<filter string='Category' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'categ_id'}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_category_property_form" model="ir.ui.view">
|
||||
<field name="name">product.category.property.form.inherit</field>
|
||||
<field name="model">product.category</field>
|
||||
|
||||
@@ -24,13 +24,14 @@ from openerp.osv import osv
|
||||
from openerp.tools.translate import _
|
||||
from openerp.report import report_sxw
|
||||
from common_report_header import common_report_header
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
class third_party_ledger(report_sxw.rml_parse, common_report_header):
|
||||
|
||||
def __init__(self, cr, uid, name, context=None):
|
||||
super(third_party_ledger, self).__init__(cr, uid, name, context=context)
|
||||
self.init_bal_sum = 0.0
|
||||
self.amount_currency = {}
|
||||
self.localcontext.update({
|
||||
'time': time,
|
||||
'lines': self.lines,
|
||||
@@ -50,6 +51,7 @@ class third_party_ledger(report_sxw.rml_parse, common_report_header):
|
||||
'display_initial_balance':self._display_initial_balance,
|
||||
'display_currency':self._display_currency,
|
||||
'get_target_move': self._get_target_move,
|
||||
'amount_currency': self.amount_currency,
|
||||
})
|
||||
|
||||
def _get_filter(self, data):
|
||||
@@ -70,7 +72,10 @@ class third_party_ledger(report_sxw.rml_parse, common_report_header):
|
||||
if data['form']['filter'] == 'unreconciled':
|
||||
self.reconcil = False
|
||||
self.result_selection = data['form'].get('result_selection', 'customer')
|
||||
self.amount_currency = data['form'].get('amount_currency', False)
|
||||
if data['form'].get('amount_currency'):
|
||||
self.amount_currency['currency'] = True
|
||||
else:
|
||||
self.amount_currency.pop('currency', False)
|
||||
self.target_move = data['form'].get('target_move', 'all')
|
||||
PARTNER_REQUEST = ''
|
||||
move_state = ['draft','posted']
|
||||
@@ -96,6 +101,7 @@ class third_party_ledger(report_sxw.rml_parse, common_report_header):
|
||||
if (data['model'] == 'res.partner') and ids:
|
||||
PARTNER_REQUEST = "AND l.partner_id IN %s"
|
||||
params += [tuple(ids)]
|
||||
reconcile = "" if self.reconcil else "AND l.reconcile_id IS NULL "
|
||||
self.cr.execute(
|
||||
"SELECT DISTINCT l.partner_id " \
|
||||
"FROM account_move_line AS l, account_account AS account, " \
|
||||
@@ -104,12 +110,12 @@ class third_party_ledger(report_sxw.rml_parse, common_report_header):
|
||||
"AND l.account_id = account.id " \
|
||||
"AND am.id = l.move_id " \
|
||||
"AND am.state IN %s"
|
||||
# "AND " + self.query +" " \
|
||||
"AND " + self.query +" " \
|
||||
"AND l.account_id IN %s " \
|
||||
" " + PARTNER_REQUEST + " " \
|
||||
"AND account.active ", params)
|
||||
"AND account.active " + reconcile + " ", params)
|
||||
self.partner_ids = [res['partner_id'] for res in self.cr.dictfetchall()]
|
||||
objects = obj_partner.browse(self.cr, self.uid, self.partner_ids)
|
||||
objects = obj_partner.browse(self.cr, SUPERUSER_ID, self.partner_ids)
|
||||
objects = sorted(objects, key=lambda x: (x.ref, x.name))
|
||||
return super(third_party_ledger, self).set_context(objects, data, self.partner_ids, report_type)
|
||||
|
||||
|
||||
@@ -93,7 +93,8 @@
|
||||
<label for="date_start" string="Date Range"/>
|
||||
<field name="date_start"
|
||||
on_change="onchange_start_date(date_start)"
|
||||
class="oe_inline"/> -
|
||||
class="oe_inline"
|
||||
help="The fiscal year is created when installing a Chart of Account."/> -
|
||||
<field name="date_stop" class="oe_inline"/>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
<span t-esc="formatLang(sum_balance_account(childrenaccount), digits=get_digits(dp='Account'), currency_obj=res_company.currency_id)"/>
|
||||
</td>
|
||||
<td class="text-right" t-if="data['form']['amount_currency']">
|
||||
<span t-esc="formatLang(sum_currency_amount_account(childrenaccount), digits=get_digits(dp='Account'), currency_obj=res_company.currency_id) if sum_currency_amount_account(childrenaccount) > 0.00 else ''"/>
|
||||
<span t-esc="formatLang(sum_currency_amount_account(childrenaccount), digits=get_digits(dp='Account')) if sum_currency_amount_account(childrenaccount) > 0.00 else ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr t-foreach="lines(childrenaccount)" t-as="line">
|
||||
@@ -117,7 +117,7 @@
|
||||
<span t-esc="formatLang(line['progress'], digits=get_digits(dp='Account'), currency_obj=res_company.currency_id)"/>
|
||||
</td>
|
||||
<td class="text-right" t-if="data['form']['amount_currency']">
|
||||
<span t-esc="formatLang(line['amount_currency'], currency_obj=res_company.currency_id) if line['amount_currency'] > 0.00 else ''"/>
|
||||
<span t-esc="formatLang(line['amount_currency'], digits=get_digits(dp='Account')) if line['amount_currency'] > 0.00 else ''"/>
|
||||
<span t-esc="line['currency_code'] if line['amount_currency'] > 0.00 else ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
<span t-esc="formatLang(line['progress'], currency_obj=res_company.currency_id)"/>
|
||||
</td>
|
||||
<td t-if="amount_currency">
|
||||
<span t-esc="formatLang(line['amount_currency'])"/>
|
||||
<span t-esc="formatLang(line['amount_currency'])"/>&nbsp;<span t-esc="line['currency_code']"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
|
||||
@@ -63,6 +63,8 @@ class account_fiscalyear_close(osv.osv_memory):
|
||||
raise UserError(_('The entries to reconcile should belong to the same company.'))
|
||||
r_id = self.pool.get('account.move.reconcile').create(cr, uid, {'type': 'auto', 'opening_reconciliation': True})
|
||||
cr.execute('update account_move_line set reconcile_id = %s where id in %s',(r_id, tuple(ids),))
|
||||
# reconcile_ref deptends from reconcile_id but was not recomputed
|
||||
obj_acc_move_line._store_set_values(cr, uid, ids, ['reconcile_ref'], context=context)
|
||||
obj_acc_move_line.invalidate_cache(cr, uid, ['reconcile_id'], ids, context=context)
|
||||
return r_id
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ class account_open_closed_fiscalyear(osv.osv_memory):
|
||||
if not period_journal:
|
||||
raise UserError(_("You have to set the 'End of Year Entries Journal' for this Fiscal Year which is set after generating opening entries from 'Generate Opening Entries'."))
|
||||
|
||||
if period_journal.period_id.state == 'done':
|
||||
raise UserError(_("You can not cancel closing entries if the 'End of Year Entries Journal' period is closed."))
|
||||
|
||||
ids_move = move_obj.search(cr, uid, [('journal_id','=',period_journal.journal_id.id),('period_id','=',period_journal.period_id.id)])
|
||||
if ids_move:
|
||||
cr.execute('delete from account_move where id IN %s', (tuple(ids_move),))
|
||||
|
||||
@@ -79,5 +79,7 @@ class account_statement_from_invoice_lines(osv.osv_memory):
|
||||
'statement_id': statement_id,
|
||||
'ref': line.ref,
|
||||
'date': statement.date,
|
||||
'amount_currency': line.amount_currency,
|
||||
'currency_id': line.currency_id.id,
|
||||
}, context=context)
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Import Entries">
|
||||
<separator string="Payable and Receivables"/>
|
||||
<field height="300" width="700" name="line_ids" domain="[('account_id.type','in',['receivable','payable']), ('reconcile_id','=',False), ('state', '=', 'valid')]"/>
|
||||
<field height="300" width="700"
|
||||
name="line_ids"
|
||||
domain="[('account_id.type','in',['receivable','payable']), ('reconcile_id','=',False), ('state', '=', 'valid')]"
|
||||
context="{'currency':True}" />
|
||||
<footer>
|
||||
<button string="Ok" name="populate_statement" type="object" class="oe_highlight"/>
|
||||
or
|
||||
|
||||
@@ -76,8 +76,8 @@ class account_invoice_line(osv.osv):
|
||||
_inherit = "account.invoice.line"
|
||||
_description = "Invoice Line"
|
||||
|
||||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, company_id=None, context=None):
|
||||
res_prod = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit, currency_id=currency_id, company_id=company_id, context=context)
|
||||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, company_id=None, date_invoice=None, context=None):
|
||||
res_prod = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit, currency_id=currency_id, company_id=company_id, date_invoice=date_invoice, context=context)
|
||||
rec = self.pool.get('account.analytic.default').account_get(cr, uid, product, partner_id, uid, time.strftime('%Y-%m-%d'), company_id=company_id, context=context)
|
||||
if rec:
|
||||
res_prod['value'].update({'account_analytic_id': rec.analytic_id.id})
|
||||
|
||||
@@ -303,8 +303,8 @@ class account_invoice_line(osv.osv):
|
||||
res ['analytics_id'] = line.analytics_id and line.analytics_id.id or False
|
||||
return res
|
||||
|
||||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, company_id=None, context=None):
|
||||
res_prod = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit, currency_id, company_id=company_id, context=context)
|
||||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, company_id=None, date_invoice=None, context=None):
|
||||
res_prod = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit, currency_id, company_id=company_id, date_invoice=date_invoice, context=context)
|
||||
rec = self.pool.get('account.analytic.default').account_get(cr, uid, product, partner_id, uid, time.strftime('%Y-%m-%d'), context=context)
|
||||
if rec and rec.analytics_id:
|
||||
res_prod['value'].update({'analytics_id': rec.analytics_id.id})
|
||||
|
||||
@@ -43,9 +43,9 @@ class account_invoice_line(osv.osv):
|
||||
return res
|
||||
|
||||
|
||||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, company_id=None, context=None):
|
||||
def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, company_id=None, date_invoice=None, context=None):
|
||||
fiscal_pool = self.pool.get('account.fiscal.position')
|
||||
res = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit, currency_id, company_id, context)
|
||||
res = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit, currency_id, company_id, date_invoice, context)
|
||||
if not product:
|
||||
return res
|
||||
if type in ('in_invoice','in_refund'):
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import account_bank_statement_import
|
||||
|
||||
import wizard
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
'description' : """Generic Wizard to Import Bank Statements. Includes the import of files in .OFX format""",
|
||||
'data' : [
|
||||
'account_bank_statement_import_view.xml',
|
||||
'account_import_tip_data.xml'
|
||||
'account_import_tip_data.xml',
|
||||
'wizard/journal_creation.xml',
|
||||
],
|
||||
'demo': [
|
||||
'demo/fiscalyear_period.xml',
|
||||
|
||||
@@ -36,21 +36,47 @@ class account_bank_statement_import(osv.TransientModel):
|
||||
context = dict(context or {})
|
||||
#set the active_id in the context, so that any extension module could
|
||||
#reuse the fields chosen in the wizard if needed (see .QIF for example)
|
||||
context.update({'active_id': ids[0]})
|
||||
ctx = dict(context)
|
||||
ctx['active_id'] = ids[0]
|
||||
|
||||
data_file = self.browse(cr, uid, ids[0], context=context).data_file
|
||||
data_file = self.browse(cr, uid, ids[0], context=ctx).data_file
|
||||
|
||||
# The appropriate implementation module returns the required data
|
||||
currency_code, account_number, stmts_vals = self._parse_file(cr, uid, base64.b64decode(data_file), context=context)
|
||||
currency_code, account_number, stmts_vals = self._parse_file(cr, uid, base64.b64decode(data_file), context=ctx)
|
||||
# Check raw data
|
||||
self._check_parsed_data(cr, uid, stmts_vals, context=context)
|
||||
self._check_parsed_data(cr, uid, stmts_vals, context=ctx)
|
||||
# Try to find the bank account and currency in odoo
|
||||
currency_id, bank_account_id = self._find_additional_data(cr, uid, currency_code, account_number, context=context)
|
||||
# Find or create the bank journal
|
||||
journal_id = self._get_journal(cr, uid, currency_id, bank_account_id, account_number, context=context)
|
||||
# Create the bank account if not already existing
|
||||
if not bank_account_id and account_number:
|
||||
self._create_bank_account(cr, uid, account_number, journal_id=journal_id, partner_id=uid, context=context)
|
||||
currency_id, bank_account_id = self._find_additional_data(cr, uid, currency_code, account_number, context=ctx)
|
||||
# Try to find the journal
|
||||
journal_id = self._get_journal(cr, uid, currency_id, bank_account_id, account_number, context=ctx)
|
||||
# If no journal found, ask the user about creating one
|
||||
if not journal_id:
|
||||
return self._journal_creation_wizard(cr, uid, currency_id, account_number, bank_account_id, context=ctx)
|
||||
# Or directly finish the import
|
||||
return self._finalize_import(cr, uid, bank_account_id, account_number, journal_id, stmts_vals, context=ctx)
|
||||
|
||||
def _journal_creation_wizard(self, cr, uid, currency_id, account_number, bank_account_id, context=None):
|
||||
""" Calls a wizard that allows the user to accept/refuse journal creation """
|
||||
return {
|
||||
'name': _('Journal Creation'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'account.bank.statement.import.journal.creation',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'statement_import_transient_id': context['active_id'],
|
||||
'default_currency_id': currency_id,
|
||||
'default_account_number': account_number,
|
||||
'bank_account_id': bank_account_id,
|
||||
'default_name': _('Bank') + ' ' + account_number,
|
||||
}
|
||||
}
|
||||
|
||||
def _finalize_import(self, cr, uid, bank_account_id, account_number, journal_id, stmts_vals, context=None):
|
||||
""" This part is separated from import_file so it can be called via the joutnal creation wizard
|
||||
No CUD can happen before this method is called, so not calling it is like aborting the import.
|
||||
"""
|
||||
# Prepare statement data to be used for bank statements creation
|
||||
stmts_vals = self._complete_stmts_vals(cr, uid, stmts_vals, journal_id, account_number, context=context)
|
||||
# Create the bank statements
|
||||
@@ -152,31 +178,12 @@ class account_bank_statement_import(osv.TransientModel):
|
||||
if currency_id and currency_id != journal_currency_id:
|
||||
raise UserError(_('The currency of the bank statement is not the same as the currency of the journal !'))
|
||||
|
||||
# If there is no journal, create one (and its account)
|
||||
if not journal_id and account_number:
|
||||
journal_id = self._create_journal(cr, uid, currency_id, account_number, context=context)
|
||||
if bank_account_id:
|
||||
bank_pool.write(cr, uid, [bank_account_id], {'journal_id': journal_id}, context=context)
|
||||
|
||||
# If we couldn't find/create a journal, everything is lost
|
||||
if not journal_id:
|
||||
# If we couldn't find a journal and can't create one, everything is lost
|
||||
if not journal_id and not account_number:
|
||||
raise UserError(_('Cannot find in which journal import this statement. Please manually select a journal.'))
|
||||
|
||||
return journal_id
|
||||
|
||||
def _create_journal(self, cr, uid, currency_id, account_number, context=None):
|
||||
""" Create a journal and its account """
|
||||
wmca_pool = self.pool.get('wizard.multi.charts.accounts')
|
||||
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
|
||||
|
||||
vals_account = {'currency_id': currency_id, 'acc_name': account_number, 'account_type': 'bank', 'currency_id': currency_id}
|
||||
vals_account = wmca_pool._prepare_bank_account(cr, uid, company, vals_account, context=context)
|
||||
account_id = self.pool.get('account.account').create(cr, uid, vals_account, context=context)
|
||||
|
||||
vals_journal = {'currency_id': currency_id, 'acc_name': _('Bank') + ' ' + account_number, 'account_type': 'bank'}
|
||||
vals_journal = wmca_pool._prepare_bank_journal(cr, uid, company, vals_journal, account_id, context=context)
|
||||
return self.pool.get('account.journal').create(cr, uid, vals_journal, context=context)
|
||||
|
||||
def _create_bank_account(self, cr, uid, account_number, journal_id=False, partner_id=False, context=None):
|
||||
try:
|
||||
type_model, type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'bank_normal')
|
||||
@@ -244,7 +251,7 @@ class account_bank_statement_import(osv.TransientModel):
|
||||
else:
|
||||
ignored_statement_lines_import_ids.append(line_vals['unique_import_id'])
|
||||
if len(filtered_st_lines) > 0:
|
||||
# Remove values that won't be used to create records
|
||||
# Remove values that won't be used to create records
|
||||
st_vals.pop('transactions', None)
|
||||
for line_vals in filtered_st_lines:
|
||||
line_vals.pop('account_number', None)
|
||||
@@ -269,4 +276,3 @@ class account_bank_statement_import(osv.TransientModel):
|
||||
}]
|
||||
|
||||
return statement_ids, notifications
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import journal_creation
|
||||
@@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class account_bank_statement_import_journal_creation(osv.TransientModel):
|
||||
_name = 'account.bank.statement.import.journal.creation'
|
||||
_description = 'Import Bank Statement Journal Creation Wizard'
|
||||
_columns = {
|
||||
'name': fields.char('Journal Name', required=True),
|
||||
'currency_id': fields.many2one('res.currency', 'Currency', readonly=True),
|
||||
'account_number': fields.char('Account Number', readonly=True),
|
||||
}
|
||||
|
||||
def create_journal(self, cr, uid, ids, context=None):
|
||||
bank_account_id = context['bank_account_id']
|
||||
|
||||
wmca_pool = self.pool.get('wizard.multi.charts.accounts')
|
||||
import_wiz_obj = self.pool['account.bank.statement.import']
|
||||
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
|
||||
wiz = self.browse(cr, uid, ids, context=context)[0]
|
||||
currency_id = wiz.currency_id.id
|
||||
account_number = wiz.account_number
|
||||
|
||||
# Create the account.account
|
||||
vals_account = {'currency_id': currency_id, 'acc_name': account_number, 'account_type': 'bank', 'currency_id': currency_id}
|
||||
vals_account = wmca_pool._prepare_bank_account(cr, uid, company, vals_account, context=context)
|
||||
account_id = self.pool.get('account.account').create(cr, uid, vals_account, context=context)
|
||||
|
||||
# Create the account.journal
|
||||
name = wiz.name
|
||||
vals_journal = {'currency_id': currency_id, 'acc_name': name, 'account_type': 'bank'}
|
||||
vals_journal = wmca_pool._prepare_bank_journal(cr, uid, company, vals_journal, account_id, context=context)
|
||||
journal_id = self.pool.get('account.journal').create(cr, uid, vals_journal, context=context)
|
||||
if bank_account_id:
|
||||
self.pool.get('res.partner.bank').write(cr, uid, [bank_account_id], {'journal_id': journal_id}, context=context)
|
||||
# Create the bank account if not already existing
|
||||
elif not bank_account_id and account_number:
|
||||
import_wiz_obj._create_bank_account(cr, uid, account_number, journal_id=journal_id, partner_id=uid, context=context)
|
||||
# Finish the statement import
|
||||
statement_import_transient = import_wiz_obj.browse(cr, uid, context['statement_import_transient_id'], context=context)
|
||||
return statement_import_transient.import_file()
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" ?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="account_bank_statement_import_journal_creation_view" model="ir.ui.view">
|
||||
<field name="name">Journal Creation</field>
|
||||
<field name="model">account.bank.statement.import.journal.creation</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Journal Creation">
|
||||
<p>The account of the statement you are importing is not yet recorded in Odoo. In order to proceed with the import, you need to create a bank account and journal using this form.</p>
|
||||
<p>Just click OK to create the account/journal and finish the import. If this was a mistake, hit cancel to abort the import.</p>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="currency_id"/>
|
||||
<field name="account_number"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="create_journal" string="OK" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -60,7 +60,9 @@ class account_bank_statement_import(osv.TransientModel):
|
||||
vals_bank_statement = {
|
||||
'name': ofx.account.routing_number,
|
||||
'transactions': transactions,
|
||||
'balance_start': ofx.account.statement.balance,
|
||||
'balance_end_real': float(ofx.account.statement.balance) + total_amt,
|
||||
# WARNING: the provided ledger balance is not necessarily the ending balance of the statement
|
||||
# see https://github.com/odoo/odoo/issues/3003
|
||||
'balance_start': float(ofx.account.statement.balance) - total_amt,
|
||||
'balance_end_real': ofx.account.statement.balance,
|
||||
}
|
||||
return ofx.account.statement.currency, ofx.account.number, [vals_bank_statement]
|
||||
|
||||
@@ -17,13 +17,20 @@ class TestOfxFile(TransactionCase):
|
||||
#the Python library isn't installed on the server, the OFX import is unavailable and the test cannot be run
|
||||
return True
|
||||
cr, uid = self.cr, self.uid
|
||||
creation_wiz_obj = self.registry('account.bank.statement.import.journal.creation')
|
||||
ofx_file_path = get_module_resource('account_bank_statement_import_ofx', 'test_ofx_file', 'test_ofx.ofx')
|
||||
ofx_file = open(ofx_file_path, 'rb').read().encode('base64')
|
||||
bank_statement_id = self.statement_import_model.create(cr, uid, dict(
|
||||
data_file=ofx_file,
|
||||
))
|
||||
self.statement_import_model.import_file(cr, uid, [bank_statement_id])
|
||||
statement_id = self.bank_statement_model.search(cr, uid, [('name', '=', '000000123')])[0]
|
||||
res = self.statement_import_model.import_file(cr, uid, [bank_statement_id])
|
||||
self.assertEquals(res['res_model'], 'account.bank.statement.import.journal.creation', 'boom')
|
||||
ctx = res.get('context', {}).copy()
|
||||
wiz_id = creation_wiz_obj.create(cr, uid, {}, context=ctx)
|
||||
wiz = creation_wiz_obj.browse(cr, uid, [wiz_id], context=ctx)
|
||||
wiz.create_journal()
|
||||
|
||||
statement_id = self.bank_statement_model.search(cr, uid, [('name', '=', '000000123')])
|
||||
bank_st_record = self.bank_statement_model.browse(cr, uid, statement_id)
|
||||
self.assertEquals(bank_st_record.balance_start, 2156.56)
|
||||
self.assertEquals(bank_st_record.balance_end_real, 1796.56)
|
||||
self.assertEquals(bank_st_record.balance_start, 2516.56)
|
||||
self.assertEquals(bank_st_record.balance_end_real, 2156.56)
|
||||
|
||||
@@ -1227,11 +1227,6 @@ class account_voucher(osv.osv):
|
||||
'account_tax_id': voucher.tax_id.id,
|
||||
})
|
||||
|
||||
if move_line.get('account_tax_id', False):
|
||||
tax_data = tax_obj.browse(cr, uid, [move_line['account_tax_id']], context=context)[0]
|
||||
if not (tax_data.base_code_id and tax_data.tax_code_id):
|
||||
raise UserError(_("You have to configure account base code and account tax code on the '%s' tax!") % (tax_data.name))
|
||||
|
||||
# compute the amount in foreign currency
|
||||
foreign_currency_diff = 0.0
|
||||
amount_currency = False
|
||||
|
||||
@@ -5,6 +5,8 @@ from passlib.context import CryptContext
|
||||
import openerp
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
openerp.addons.base.res.res_users.USER_PRIVATE_FIELDS.append('password_crypt')
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
default_crypt_context = CryptContext(
|
||||
|
||||
@@ -136,7 +136,11 @@ class ir_import(orm.TransientModel):
|
||||
StringIO(record.file),
|
||||
quotechar=str(options['quoting']),
|
||||
delimiter=str(options['separator']))
|
||||
csv_nonempty = itertools.ifilter(None, csv_iterator)
|
||||
|
||||
def nonempty(row):
|
||||
return any(x for x in row if x.strip())
|
||||
|
||||
csv_nonempty = itertools.ifilter(nonempty, csv_iterator)
|
||||
# TODO: guess encoding with chardet? Or https://github.com/aadsm/jschardet
|
||||
encoding = options.get('encoding', 'utf-8')
|
||||
return itertools.imap(
|
||||
|
||||
@@ -322,6 +322,28 @@ class test_convert_import_data(TransactionCase):
|
||||
('', '6'),
|
||||
])
|
||||
|
||||
def test_empty_rows(self):
|
||||
Import = self.registry('base_import.import')
|
||||
id = Import.create(self.cr, self.uid, {
|
||||
'res_model': 'base_import.tests.models.preview',
|
||||
'file': 'name,Some Value\n'
|
||||
'foo,1\n'
|
||||
'\n'
|
||||
'bar,2\n'
|
||||
' \n'
|
||||
'\t \n'
|
||||
})
|
||||
record = Import.browse(self.cr, self.uid, id)
|
||||
data, fields = Import._convert_import_data(
|
||||
record, ['name', 'somevalue'],
|
||||
{'quoting': '"', 'separator': ',', 'headers': True,})
|
||||
|
||||
self.assertItemsEqual(fields, ['name', 'somevalue'])
|
||||
self.assertItemsEqual(data, [
|
||||
('foo', '1'),
|
||||
('bar', '2'),
|
||||
])
|
||||
|
||||
def test_nofield(self):
|
||||
Import = self.registry('base_import.import')
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ _ref_vat = {
|
||||
'se': 'SE123456789701',
|
||||
'si': 'SI12345679',
|
||||
'sk': 'SK0012345675',
|
||||
'tr': 'TR1234567890 (VERGINO) veya TR12345678901 (TCKIMLIKNO)' # Levent Karakas @ Eska Yazilim A.S.
|
||||
}
|
||||
|
||||
class res_partner(osv.osv):
|
||||
@@ -316,3 +317,45 @@ class res_partner(osv.osv):
|
||||
return int(vat[10]) == dig_check
|
||||
else:
|
||||
return False
|
||||
|
||||
# VAT validation in Turkey, contributed by # Levent Karakas @ Eska Yazilim A.S.
|
||||
def check_vat_tr(self, vat):
|
||||
|
||||
if not (10 <= len(vat) <= 11):
|
||||
return False
|
||||
try:
|
||||
int(vat)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
# check vat number (vergi no)
|
||||
if len(vat) == 10:
|
||||
sum = 0
|
||||
check = 0
|
||||
for f in range(0,9):
|
||||
c1 = (int(vat[f]) + (9-f)) % 10
|
||||
c2 = ( c1 * (2 ** (9-f)) ) % 9
|
||||
if (c1 != 0) and (c2 == 0): c2 = 9
|
||||
sum += c2
|
||||
if sum % 10 == 0:
|
||||
check = 0
|
||||
else:
|
||||
check = 10 - (sum % 10)
|
||||
return int(vat[9]) == check
|
||||
|
||||
# check personal id (tc kimlik no)
|
||||
if len(vat) == 11:
|
||||
c1a = 0
|
||||
c1b = 0
|
||||
c2 = 0
|
||||
for f in range(0,9,2):
|
||||
c1a += int(vat[f])
|
||||
for f in range(1,9,2):
|
||||
c1b += int(vat[f])
|
||||
c1 = ( (7 * c1a) - c1b) % 10
|
||||
for f in range(0,10):
|
||||
c2 += int(vat[f])
|
||||
c2 = c2 % 10
|
||||
return int(vat[9]) == c1 and int(vat[10]) == c2
|
||||
|
||||
return False
|
||||
|
||||
@@ -101,11 +101,11 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.start, 'dayname')}</div>
|
||||
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.start, 'dayname', tz=object.partner_id.tz)}</div>
|
||||
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #E1E2F8;width: 130px;">
|
||||
${object.event_id.get_interval(object.event_id.start,'day')}
|
||||
${object.event_id.get_interval(object.event_id.start,'day', tz=object.partner_id.tz)}
|
||||
</div>
|
||||
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.start, 'month')}</div>
|
||||
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.start, 'month', tz=object.partner_id.tz)}</div>
|
||||
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -226,11 +226,11 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.start, 'dayname')}</div>
|
||||
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.start, 'dayname', tz=object.partner_id.tz)}</div>
|
||||
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #E1E2F8;width: 130px;">
|
||||
${object.event_id.get_interval(object.event_id.start,'day')}
|
||||
${object.event_id.get_interval(object.event_id.start,'day', tz=object.partner_id.tz)}
|
||||
</div>
|
||||
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.start, 'month')}</div>
|
||||
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.start, 'month', tz=object.partner_id.tz)}</div>
|
||||
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -350,11 +350,11 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.start, 'dayname')}</div>
|
||||
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.event_id.get_interval(object.event_id.start, 'dayname', tz=object.partner_id.tz)}</div>
|
||||
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #E1E2F8;width: 130px;">
|
||||
${object.event_id.get_interval(object.event_id.start,'day')}
|
||||
${object.event_id.get_interval(object.event_id.start,'day', tz=object.partner_id.tz)}
|
||||
</div>
|
||||
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.start, 'month')}</div>
|
||||
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.event_id.get_interval(object.event_id.start, 'month', tz=object.partner_id.tz)}</div>
|
||||
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${not object.event_id.allday and object.event_id.get_interval(object.event_id.start, 'time', tz=object.partner_id.tz) or ''}</div>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -150,7 +150,7 @@ class crm_lead(format_address, osv.osv):
|
||||
|
||||
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||
if context and context.get('opportunity_id'):
|
||||
action = self._get_formview_action(cr, user, context['opportunity_id'], context=context)
|
||||
action = self.get_formview_action(cr, user, context['opportunity_id'], context=context)
|
||||
if action.get('views') and any(view_id for view_id in action['views'] if view_id[1] == view_type):
|
||||
view_id = next(view_id[0] for view_id in action['views'] if view_id[1] == view_type)
|
||||
res = super(crm_lead, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
|
||||
@@ -876,6 +876,7 @@ class crm_lead(format_address, osv.osv):
|
||||
(tree_view or False, 'tree'), (False, 'kanban'),
|
||||
(False, 'calendar'), (False, 'graph')],
|
||||
'type': 'ir.actions.act_window',
|
||||
'context': {'default_type': 'opportunity'}
|
||||
}
|
||||
|
||||
def redirect_lead_view(self, cr, uid, lead_id, context=None):
|
||||
@@ -982,11 +983,11 @@ class crm_lead(format_address, osv.osv):
|
||||
return 'crm.mt_lead_stage'
|
||||
return super(crm_lead, self)._track_subtype(cr, uid, ids, init_values, context=context)
|
||||
|
||||
def message_get_reply_to(self, cr, uid, ids, context=None):
|
||||
def message_get_reply_to(self, cr, uid, ids, default=None, context=None):
|
||||
""" Override to get the reply_to of the parent project. """
|
||||
leads = self.browse(cr, SUPERUSER_ID, ids, context=context)
|
||||
team_ids = set([lead.team_id.id for lead in leads if lead.team_id])
|
||||
aliases = self.pool['crm.team'].message_get_reply_to(cr, uid, list(team_ids), context=context)
|
||||
aliases = self.pool['crm.team'].message_get_reply_to(cr, uid, list(team_ids), default=default, context=context)
|
||||
return dict((lead.id, aliases.get(lead.team_id and lead.team_id.id or 0, False)) for lead in leads)
|
||||
|
||||
def get_formview_id(self, cr, uid, id, context=None):
|
||||
@@ -1076,7 +1077,10 @@ class crm_lead(format_address, osv.osv):
|
||||
duration = _('unknown')
|
||||
else:
|
||||
duration = str(duration)
|
||||
message = _("Meeting scheduled at '%s'<br> Subject: %s <br> Duration: %s hour(s)") % (meeting_date, meeting_subject, duration)
|
||||
meet_date = datetime.strptime(meeting_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
meeting_usertime = fields.datetime.context_timestamp(cr, uid, meet_date, context=context).strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
html_time = "<time datetime='%s+00:00'>%s</time>" % (meeting_date, meeting_usertime)
|
||||
message = _("Meeting scheduled at '%s'<br> Subject: %s <br> Duration: %s hour(s)") % (html_time, meeting_subject, duration)
|
||||
return self.message_post(cr, uid, ids, body=message, context=context)
|
||||
|
||||
def onchange_state(self, cr, uid, ids, state_id, context=None):
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<field name="context">{
|
||||
'default_type':'lead',
|
||||
'stage_type':'lead',
|
||||
'empty_list_help_model': 'crm.team',
|
||||
'needaction_menu_ref': 'crm.menu_crm_opportunities',
|
||||
'search_default_to_process':1,
|
||||
}
|
||||
|
||||
@@ -35,8 +35,9 @@ class stock_picking(osv.osv):
|
||||
total_weight = total_weight_net = 0.00
|
||||
|
||||
for move in picking.move_lines:
|
||||
total_weight += move.weight
|
||||
total_weight_net += move.weight_net
|
||||
if move.state != 'cancel':
|
||||
total_weight += move.weight
|
||||
total_weight_net += move.weight_net
|
||||
|
||||
res[picking.id] = {
|
||||
'weight': total_weight,
|
||||
@@ -58,12 +59,12 @@ class stock_picking(osv.osv):
|
||||
'weight': fields.function(_cal_weight, type='float', string='Weight', digits_compute= dp.get_precision('Stock Weight'), multi='_cal_weight',
|
||||
store={
|
||||
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 40),
|
||||
'stock.move': (_get_picking_line, ['picking_id', 'product_id','product_uom_qty','product_uom'], 40),
|
||||
'stock.move': (_get_picking_line, ['state', 'picking_id', 'product_id','product_uom_qty','product_uom'], 40),
|
||||
}),
|
||||
'weight_net': fields.function(_cal_weight, type='float', string='Net Weight', digits_compute= dp.get_precision('Stock Weight'), multi='_cal_weight',
|
||||
store={
|
||||
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 40),
|
||||
'stock.move': (_get_picking_line, ['picking_id', 'product_id','product_uom_qty','product_uom'], 40),
|
||||
'stock.move': (_get_picking_line, ['state', 'picking_id', 'product_id','product_uom_qty','product_uom'], 40),
|
||||
}),
|
||||
'carrier_tracking_ref': fields.char('Carrier Tracking Ref', copy=False),
|
||||
'number_of_packages': fields.integer('Number of Packages', copy=False),
|
||||
|
||||
@@ -110,6 +110,8 @@ class EventMailRegistration(models.Model):
|
||||
@api.depends('registration_id', 'scheduler_id.interval_unit', 'scheduler_id.interval_type')
|
||||
def _compute_scheduled_date(self):
|
||||
if self.registration_id:
|
||||
self.scheduled_date = datetime.strptime(self.registration_id.date_open, tools.DEFAULT_SERVER_DATETIME_FORMAT) + _INTERVALS[self.scheduler_id.interval_unit](self.scheduler_id.interval_nbr)
|
||||
date_open = self.registration_id.date_open
|
||||
date_open_datetime = date_open and datetime.strptime(date_open, tools.DEFAULT_SERVER_DATETIME_FORMAT) or fields.datetime.now()
|
||||
self.scheduled_date = date_open_datetime + _INTERVALS[self.scheduler_id.interval_unit](self.scheduler_id.interval_nbr)
|
||||
else:
|
||||
self.scheduled_date = False
|
||||
|
||||
@@ -37,7 +37,10 @@ MAX_VISIBILITY_RANKING = 3
|
||||
def start_end_date_for_period(period, default_start_date=False, default_end_date=False):
|
||||
"""Return the start and end date for a goal period based on today
|
||||
|
||||
:return: (start_date, end_date), datetime.date objects, False if the period is
|
||||
:param str default_start_date: string date in DEFAULT_SERVER_DATE_FORMAT format
|
||||
:param str default_end_date: string date in DEFAULT_SERVER_DATE_FORMAT format
|
||||
|
||||
:return: (start_date, end_date), dates in string format, False if the period is
|
||||
not defined or unknown"""
|
||||
today = date.today()
|
||||
if period == 'daily':
|
||||
@@ -58,11 +61,9 @@ def start_end_date_for_period(period, default_start_date=False, default_end_date
|
||||
start_date = default_start_date # for manual goal, start each time
|
||||
end_date = default_end_date
|
||||
|
||||
if start_date and end_date:
|
||||
return (datetime.strftime(start_date, DF), datetime.strftime(end_date, DF))
|
||||
else:
|
||||
return (start_date, end_date)
|
||||
|
||||
return (datetime.strftime(start_date, DF), datetime.strftime(end_date, DF))
|
||||
|
||||
class gamification_challenge(osv.Model):
|
||||
"""Gamification challenge
|
||||
|
||||
@@ -16,37 +16,36 @@
|
||||
<div attrs="{'invisible':[('google_cal_sync','=',False)]}">
|
||||
<br/><h2>To setup the signin process with Google, first you have to perform the following steps</h2>
|
||||
<ul>
|
||||
<li> Connect on your google account and go to <a href='https://cloud.google.com/console' target='_blank'>https://cloud.google.com/console</a> </li>
|
||||
<li> Connect on your google account and go to <a href='https://console.developers.google.com/' target='_blank'>https://console.developers.google.com/</a> </li>
|
||||
<li>
|
||||
Click on <b>"Create Project"</b> and enter a project name and change your id if you want. Don't forget to accept the Terms of Services
|
||||
Click on <b>'Create Project'</b> and enter a project name and change your id if you want. Don't forget to accept the Terms of Services
|
||||
<br/><br/><img src='/google_calendar/static/src/img/setup_01.png' class='calendar_img_tuto'/>
|
||||
<br/><img src='/google_calendar/static/src/img/setup_02.png' class='calendar_img_tuto'/>
|
||||
</li>
|
||||
<li> In the menu on left side, select the sub menu APIs (from menu APIs and auth) and activate <b>"Calendar API"</b> by clicking on button "OFF".
|
||||
<br/> You will need to accept again the "Google APIs Terms of services" and "Calendar API Terms of service"
|
||||
<br/> When it's done, check that the button of <b>"Calendar API"</b> is well in green and with text "ON"
|
||||
<li> In the menu on left side, select the sub menu APIs (from menu APIs and auth) and click on <b>'Calendar API'</b>.
|
||||
<br/> Activate the Calendar API by clicking on the blue button <b>'Enable API'</b>.
|
||||
<br/> When it's done, the Calendar API overview will be available
|
||||
<br/><br/> <img src='/google_calendar/static/src/img/setup_03.png' class='calendar_img_tuto'/>
|
||||
<br/> <img src='/google_calendar/static/src/img/setup_04.png' class='calendar_img_tuto'/>
|
||||
<br/> <img src='/google_calendar/static/src/img/setup_05.png' class='calendar_img_tuto'/>
|
||||
</li>
|
||||
<li>
|
||||
In the menu on left side, select the sub menu <b>'Credentials'</b> (from menu APIs and auth) and click on button <b>'Create New Client ID'</b>
|
||||
In the menu on left side, select the sub menu <b>'Credentials'</b> (from menu APIs and auth) and click on button <b>'Create new Client ID'</b>
|
||||
<br/><br/> <img src='/google_calendar/static/src/img/setup_06.png' class='calendar_img_tuto'/>
|
||||
</li>
|
||||
<li> Fill in the Name of application and check that the platform is well on <b>'Web Application'</b>.<br/>
|
||||
<br/> You should now configure the allowed pages on which you will be redirected. To do it, you need to complete the field <b>"Redirect RI"</b>
|
||||
and set as value (your own domain followed by <i>"/google_account/authentication"</i>) :
|
||||
<li> Check that the Application type is set on <b>'Web Application'</b>, then click on <b>'Configure consent screen'</b>.
|
||||
<br/> Specify an email address and a product name, then save.
|
||||
<br/><br/> <img src='/google_calendar/static/src/img/setup_07.png' class='calendar_img_tuto'/>
|
||||
<br/><img src='/google_calendar/static/src/img/setup_09.png' class='calendar_img_tuto'/>
|
||||
<br/> You should now configure the allowed pages on which you will be redirected. To do it, you need to complete the field <b>"Authorized redirect URI"</b>
|
||||
and set as value (your own domain followed by <i>'/google_account/authentication'</i>):
|
||||
<br/>==> <b><field name="server_uri" readonly="1" style='display:inline'/></b>
|
||||
<br/> You can now click on <b>'Create Client ID'</b>
|
||||
<br/><br/> <img src='/google_calendar/static/src/img/setup_07.png' class='calendar_img_tuto'/>
|
||||
<br/><br/> <img src='/google_calendar/static/src/img/setup_10.png' class='calendar_img_tuto'/>
|
||||
</li>
|
||||
<li>Once done, you will have the both informations (<b>Client ID</b> and <b>Client Secret</b>) that you need to insert in the 2 fields below !
|
||||
<li>Once done, you will have the both informations (<b>Client ID</b> and <b>Client Secret</b>) that you need to insert in the 2 fields below!
|
||||
<br/><br/> <img src='/google_calendar/static/src/img/setup_08.png' class='calendar_img_tuto'/>
|
||||
</li>
|
||||
<li>
|
||||
To finish, you just need to create a "consent screen" by clicking on <b>"Consent Screen"</b> in the left menu.
|
||||
<br/><br/> <img src='/google_calendar/static/src/img/setup_09.png' class='calendar_img_tuto'/>
|
||||
</li>
|
||||
<a href="#" class="oe_link">Return at Top</a>
|
||||
|
||||
</ul>
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 50 KiB |
@@ -427,11 +427,11 @@ class hr_applicant(osv.Model):
|
||||
return 'hr_recruitment.mt_applicant_stage_changed'
|
||||
return super(hr_applicant, self)._track_subtype(cr, uid, ids, init_values, context=context)
|
||||
|
||||
def message_get_reply_to(self, cr, uid, ids, context=None):
|
||||
def message_get_reply_to(self, cr, uid, ids, default=None, context=None):
|
||||
""" Override to get the reply_to of the parent project. """
|
||||
applicants = self.browse(cr, SUPERUSER_ID, ids, context=context)
|
||||
job_ids = set([applicant.job_id.id for applicant in applicants if applicant.job_id])
|
||||
aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(job_ids), context=context)
|
||||
aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(job_ids), default=default, context=context)
|
||||
return dict((applicant.id, aliases.get(applicant.job_id and applicant.job_id.id or 0, False)) for applicant in applicants)
|
||||
|
||||
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<field name="view_mode">kanban,tree,form,pivot,graph,calendar</field>
|
||||
<field name="view_id" eval="False"/>
|
||||
<field name="search_view_id" ref="view_crm_case_jobs_filter"/>
|
||||
<field name="context">{'empty_list_help_model': 'hr.job'}</field>
|
||||
<field name="context">{}</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
Odoo helps you track applicants in the recruitment
|
||||
|
||||
@@ -318,7 +318,7 @@
|
||||
<field name="name">Applications</field>
|
||||
<field name="res_model">hr.applicant</field>
|
||||
<field name="view_mode">kanban,tree,form,graph,calendar</field>
|
||||
<field name="context">{'search_default_job_id': [active_id], 'default_job_id': active_id, 'empty_list_help_model': 'hr.job'}</field>
|
||||
<field name="context">{'search_default_job_id': [active_id], 'default_job_id': active_id}</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
Odoo helps you track applicants in the recruitment
|
||||
|
||||
@@ -164,7 +164,7 @@ class account_analytic_line(osv.osv):
|
||||
|
||||
date_due = False
|
||||
if partner.property_payment_term:
|
||||
pterm_list= account_payment_term_obj.compute(cr, uid,
|
||||
pterm_list = account_payment_term_obj.compute(cr, uid,
|
||||
partner.property_payment_term.id, value=1,
|
||||
date_ref=time.strftime('%Y-%m-%d'))
|
||||
if pterm_list:
|
||||
@@ -250,9 +250,9 @@ class account_analytic_line(osv.osv):
|
||||
if data.get('name', False):
|
||||
details.append(line['name'])
|
||||
if details:
|
||||
note.append(u' - '.join(map(lambda x: unicode(x) or '',details)))
|
||||
note.append(u' - '.join(map(lambda x: unicode(x) or '', details)))
|
||||
if note:
|
||||
curr_invoice_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note)))
|
||||
curr_invoice_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '', note)))
|
||||
return curr_invoice_line
|
||||
|
||||
def invoice_cost_create(self, cr, uid, ids, data=None, context=None):
|
||||
@@ -279,8 +279,7 @@ class account_analytic_line(osv.osv):
|
||||
currency_id = line.account_id.pricelist_id.currency_id.id
|
||||
if line.account_id.pricelist_id and line.account_id.pricelist_id.currency_id:
|
||||
if line.account_id.pricelist_id.currency_id.id != currency_id and group_by_partner:
|
||||
raise UserError(_('Error!'),
|
||||
_('You cannot group invoices having different currencies on different analytic accounts for the same partner.'))
|
||||
raise UserError(_('You cannot group invoices having different currencies on different analytic accounts for the same partner.'))
|
||||
|
||||
if group_by_partner:
|
||||
key = (line.account_id.partner_id.id,
|
||||
@@ -298,13 +297,13 @@ class account_analytic_line(osv.osv):
|
||||
# don't really care, what's important is the analytic lines that
|
||||
# will be used to create the invoice lines
|
||||
|
||||
partner = analytic_lines[0].account_id.partner_id # will be the same for every line
|
||||
partner = analytic_lines[0].account_id.partner_id # will be the same for every line
|
||||
|
||||
curr_invoice = self._prepare_cost_invoice(cr, uid, partner, company_id, currency_id, analytic_lines, group_by_partner, context=context)
|
||||
invoice_context = dict(context,
|
||||
lang=partner.lang,
|
||||
force_company=company_id, # set force_company in context so the correct product properties are selected (eg. income account)
|
||||
company_id=company_id) # set company_id in context, so the correct default journal will be selected
|
||||
force_company=company_id, # set force_company in context so the correct product properties are selected (eg. income account)
|
||||
company_id=company_id) # set company_id in context, so the correct default journal will be selected
|
||||
last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=invoice_context)
|
||||
invoices.append(last_invoice)
|
||||
|
||||
@@ -339,7 +338,6 @@ class account_analytic_line(osv.osv):
|
||||
return invoices
|
||||
|
||||
|
||||
|
||||
class hr_analytic_timesheet(osv.osv):
|
||||
_inherit = "hr.analytic.timesheet"
|
||||
def on_change_account_id(self, cr, uid, ids, account_id, user_id=False):
|
||||
|
||||
@@ -41,20 +41,39 @@ class hr_timesheet_report(osv.osv):
|
||||
}
|
||||
|
||||
def _select(self):
|
||||
return super(hr_timesheet_report, self)._select() + """,
|
||||
return """
|
||||
WITH
|
||||
totals AS (
|
||||
SELECT
|
||||
d.sheet_id,
|
||||
d.name as date,
|
||||
sum(total_difference) / coalesce(sum(j.count),1) as total_diff,
|
||||
sum(total_timesheet) / coalesce(sum(j.count),1) as total_timesheet,
|
||||
sum(total_attendance) / coalesce(sum(j.count),1) as total_attendance
|
||||
FROM hr_timesheet_sheet_sheet_day d left join (
|
||||
SELECT
|
||||
h.sheet_id,
|
||||
a.date,
|
||||
count(*)
|
||||
FROM account_analytic_line a inner join hr_analytic_timesheet h ON (h.line_id=a.id)
|
||||
GROUP BY h.sheet_id, a.date
|
||||
) j ON (d.sheet_id = j.sheet_id AND d.name = j.date)
|
||||
GROUP BY d.sheet_id, d.name
|
||||
)
|
||||
""" + super(hr_timesheet_report, self)._select() + """,
|
||||
htss.name,
|
||||
htss.date_from,
|
||||
htss.date_to,
|
||||
count(*) as nbr,
|
||||
sum(day.total_difference) as total_diff,
|
||||
sum(day.total_timesheet) as total_timesheet,
|
||||
sum(day.total_attendance) as total_attendance,
|
||||
sum(t.total_diff) as total_diff,
|
||||
sum(t.total_timesheet) as total_timesheet,
|
||||
sum(t.total_attendance) as total_attendance,
|
||||
aal.to_invoice,
|
||||
htss.department_id,
|
||||
htss.state"""
|
||||
|
||||
def _from(self):
|
||||
return super(hr_timesheet_report, self)._from() + "left join hr_timesheet_sheet_sheet as htss ON (hat.sheet_id=htss.id) left join hr_timesheet_sheet_sheet_day AS day ON (htss.id = day.sheet_id)"
|
||||
return super(hr_timesheet_report, self)._from() + "left join hr_timesheet_sheet_sheet as htss ON (hat.sheet_id=htss.id) join totals as t on (t.sheet_id = hat.sheet_id and t.date = aal.date)"
|
||||
|
||||
def _group_by(self):
|
||||
return super(hr_timesheet_report, self)._group_by() + """,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<record id="message_rule_1" model="ir.rule">
|
||||
<field name="name">Can only read messages that you sent or messages sent to you</field>
|
||||
<field name="model_id" ref="model_im_chat_message"/>
|
||||
<field name="groups" eval="[(6,0,[ref('base.group_user')])]"/>
|
||||
<field name="groups" eval="[(4, ref('base.group_user')), (4, ref('base.group_portal'))]"/>
|
||||
<field name="domain_force">[('to_id.user_ids', 'in', [user.id])]</field>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
@@ -15,7 +15,7 @@
|
||||
<record id="users_rule_1" model="ir.rule">
|
||||
<field name="name">Can only modify your session</field>
|
||||
<field name="model_id" ref="model_im_chat_session"/>
|
||||
<field name="groups" eval="[(6,0,[ref('base.group_user')])]"/>
|
||||
<field name="groups" eval="[(4, ref('base.group_user')), (4, ref('base.group_portal'))]"/>
|
||||
<field name="domain_force">[('user_ids', 'in', [user.id])]</field>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
@@ -26,7 +26,7 @@
|
||||
<record id="session_relation_rule_1" model="ir.rule">
|
||||
<field name="name">Can only modify your own session relations</field>
|
||||
<field name="model_id" ref="model_im_chat_conversation_state"/>
|
||||
<field name="groups" eval="[(6,0,[ref('base.group_user')])]"/>
|
||||
<field name="groups" eval="[(4, ref('base.group_user')), (4, ref('base.group_portal'))]"/>
|
||||
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
|
||||
@@ -4,3 +4,8 @@ access_im_chat_session,im_chat.session,model_im_chat_session,base.group_user,1,1
|
||||
access_im_chat_conversation_state,im_chat.conversation_state,model_im_chat_conversation_state,base.group_user,1,1,1,0
|
||||
access_im_chat_presence,im_chat.presence,model_im_chat_presence,base.group_user,1,1,1,1
|
||||
access_im_chat_shortcode,im_chat.shortcode,model_im_chat_shortcode,base.group_user,1,1,1,1
|
||||
access_im_chat_message_portal,im_chat.message,model_im_chat_message,base.group_portal,1,0,1,0
|
||||
access_im_chat_session_portal,im_chat.session,model_im_chat_session,base.group_portal,1,1,1,0
|
||||
access_im_chat_conversation_state_portal,im_chat.conversation_state,model_im_chat_conversation_state,base.group_portal,1,1,1,0
|
||||
access_im_chat_presence_portal,im_chat.presence,model_im_chat_presence,base.group_portal,1,1,1,1
|
||||
access_im_chat_shortcode_portal,im_chat.shortcode,model_im_chat_shortcode,base.group_portal,1,0,0,0
|
||||
|
@@ -166,7 +166,7 @@
|
||||
}
|
||||
|
||||
/* Smiley size */
|
||||
.oe_im_chatview .oe_im_chatview_message_bubble img{
|
||||
.oe_im_chatview .oe_im_chatview_bubble_item img{
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,16 +50,19 @@ class LivechatController(http.Controller):
|
||||
|
||||
@http.route('/im_livechat/get_session', type="json", auth="none")
|
||||
def get_session(self, channel_id, anonymous_name, **kwargs):
|
||||
cr, uid, context, db = request.cr, request.uid or openerp.SUPERUSER_ID, request.context, request.db
|
||||
cr, uid, context, db = request.cr, request.session.uid, request.context, request.db
|
||||
reg = openerp.modules.registry.RegistryManager.get(db)
|
||||
# if geoip, add the country name to the anonymous name
|
||||
if request.session.geoip:
|
||||
anonymous_name = anonymous_name + " ("+request.session.geoip.get('country_name', "")+")"
|
||||
# if the user is identifiy (eg: portal user on the frontend), don't use the anonymous name. The user will be added to session.
|
||||
if request.session.uid:
|
||||
anonymous_name = False
|
||||
return reg.get("im_livechat.channel").get_channel_session(cr, uid, channel_id, anonymous_name, context=context)
|
||||
|
||||
@http.route('/im_livechat/available', type='json', auth="none")
|
||||
def available(self, db, channel):
|
||||
cr, uid, context, db = request.cr, request.uid or openerp.SUPERUSER_ID, request.context, request.db
|
||||
cr, uid, context, db = request.cr, request.session.uid or openerp.SUPERUSER_ID, request.context, request.db
|
||||
reg = openerp.modules.registry.RegistryManager.get(db)
|
||||
with reg.cursor() as cr:
|
||||
return len(reg.get('im_livechat.channel').get_available_users(cr, uid, channel)) > 0
|
||||
|
||||
@@ -26,6 +26,7 @@ import re
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
from openerp import api, models, tools
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
|
||||
class im_livechat_channel(osv.Model):
|
||||
@@ -155,7 +156,7 @@ class im_livechat_channel(osv.Model):
|
||||
|
||||
def get_available_users(self, cr, uid, channel_id, context=None):
|
||||
""" get available user of a given channel """
|
||||
channel = self.browse(cr, uid, channel_id, context=context)
|
||||
channel = self.browse(cr, SUPERUSER_ID, channel_id, context=context)
|
||||
users = []
|
||||
for user_id in channel.user_ids:
|
||||
if (user_id.im_status == 'online'):
|
||||
@@ -165,14 +166,18 @@ class im_livechat_channel(osv.Model):
|
||||
def get_channel_session(self, cr, uid, channel_id, anonymous_name, context=None):
|
||||
""" return a session given a channel : create on with a registered user, or return false otherwise """
|
||||
# get the avalable user of the channel
|
||||
users = self.get_available_users(cr, uid, channel_id, context=context)
|
||||
users = self.get_available_users(cr, SUPERUSER_ID, channel_id, context=context)
|
||||
if len(users) == 0:
|
||||
return False
|
||||
user_id = random.choice(users).id
|
||||
# user to add to the session
|
||||
user_to_add = [(4, user_id)]
|
||||
if uid:
|
||||
user_to_add.append((4, uid))
|
||||
# create the session, and add the link with the given channel
|
||||
Session = self.pool["im_chat.session"]
|
||||
newid = Session.create(cr, uid, {'user_ids': [(4, user_id)], 'channel_id': channel_id, 'anonymous_name' : anonymous_name}, context=context)
|
||||
return Session.session_info(cr, uid, [newid], context=context)
|
||||
newid = Session.create(cr, SUPERUSER_ID, {'user_ids': user_to_add, 'channel_id': channel_id, 'anonymous_name' : anonymous_name}, context=context)
|
||||
return Session.session_info(cr, SUPERUSER_ID, [newid], context=context)
|
||||
|
||||
def test_channel(self, cr, uid, channel, context=None):
|
||||
if not channel:
|
||||
@@ -227,14 +232,22 @@ class im_livechat_channel_rule(osv.Model):
|
||||
|
||||
def match_rule(self, cr, uid, channel_id, url, country_id=False, context=None):
|
||||
""" determine if a rule of the given channel match with the given url """
|
||||
domain = [('channel_id', '=', channel_id)]
|
||||
def _match(rule_ids):
|
||||
for rule in self.browse(cr, uid, rule_ids, context=context):
|
||||
if re.search(rule.regex_url, url):
|
||||
return rule
|
||||
return False
|
||||
# first, search the country specific rules (the first match is returned)
|
||||
if country_id: # don't include the country in the research if geoIP is not installed
|
||||
domain.append(('country_ids', 'in', country_id))
|
||||
rule_ids = self.search(cr, uid, domain, context=context)
|
||||
for rule in self.browse(cr, uid, rule_ids, context=context):
|
||||
if re.search(rule.regex_url, url):
|
||||
domain = [('country_ids', 'in', [country_id]), ('channel_id', '=', channel_id)]
|
||||
rule_ids = self.search(cr, uid, domain, context=context)
|
||||
rule = _match(rule_ids)
|
||||
if rule:
|
||||
return rule
|
||||
return False
|
||||
# second, fallback on the rules without country
|
||||
domain = [('country_ids', '=', False), ('channel_id', '=', channel_id)]
|
||||
rule_ids = self.search(cr, uid, domain, context=context)
|
||||
return _match(rule_ids)
|
||||
|
||||
|
||||
class im_chat_session(osv.Model):
|
||||
|
||||
@@ -65,6 +65,7 @@ im_chat_common.Conversation.include({
|
||||
}else{
|
||||
this._super.apply(this, arguments);
|
||||
utils.set_cookie(COOKIE_NAME, "", -1);
|
||||
utils.set_cookie('im_livechat_auto_popup', JSON.stringify(false), 60*60);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -126,7 +127,8 @@ var LiveSupport = Widget.extend({
|
||||
build_button: function(channel, options, session, rule){
|
||||
var button = new ChatButton(null, channel, options, session);
|
||||
button.appendTo($("body"));
|
||||
if (rule.action === 'auto_popup'){
|
||||
var auto_popup_cookie = utils.get_cookie('im_livechat_auto_popup') ? JSON.parse(utils.get_cookie('im_livechat_auto_popup')) : true;
|
||||
if (rule.action === 'auto_popup' && auto_popup_cookie){
|
||||
setTimeout(function() {
|
||||
button.click();
|
||||
}, rule.auto_popup_timer*1000);
|
||||
|
||||
@@ -76,9 +76,9 @@
|
||||
<!-- css style -->
|
||||
<link t-att-href="'%s/web/css/im_livechat.external_lib' % (url)" rel="stylesheet"/>
|
||||
<!-- js of all the required lib (internal and external) -->
|
||||
<script t-att-src="'%s/web/js/im_livechat.external_lib' % (url)" type="text/javascript" />
|
||||
<script t-att-src="'%s/web/js/im_livechat.external_lib' % (url)" type="text/javascript" async="async" />
|
||||
<!-- the loader -->
|
||||
<script t-att-src="'%s/im_livechat/loader/%s/%i' % (url, dbname, channel)" type="text/javascript" />
|
||||
<script t-att-src="'%s/im_livechat/loader/%s/%i' % (url, dbname, channel)" type="text/javascript" async="async" />
|
||||
</template>
|
||||
|
||||
<!-- Template rendering all the scripts required to execute the Livechat from a page containing Odoo -->
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<record id="view_partner_inv_comm_type_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.inv_comm_type.form.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="inherit_id" ref="account.view_partner_property_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="last_reconciliation_date" position="after">
|
||||
<field name="out_inv_comm_type"/>
|
||||
|
||||
@@ -213,7 +213,7 @@ class mail_notification(osv.Model):
|
||||
for chunk in chunks:
|
||||
mail_values = {
|
||||
'mail_message_id': message.id,
|
||||
'auto_delete': True,
|
||||
'auto_delete': (context or {}).get('mail_auto_delete', True),
|
||||
'body_html': body_html,
|
||||
'recipient_ids': [(4, id) for id in chunk],
|
||||
'references': references,
|
||||
|
||||
@@ -601,7 +601,8 @@ class mail_message(osv.Model):
|
||||
|
||||
def _find_allowed_model_wise(self, cr, uid, doc_model, doc_dict, context=None):
|
||||
doc_ids = doc_dict.keys()
|
||||
allowed_doc_ids = self.pool[doc_model].search(cr, uid, [('id', 'in', doc_ids)], context=context)
|
||||
ctx = dict(context or {}, active_test=False)
|
||||
allowed_doc_ids = self.pool[doc_model].search(cr, uid, [('id', 'in', doc_ids)], context=ctx)
|
||||
return set([message_id for allowed_doc_id in allowed_doc_ids for message_id in doc_dict[allowed_doc_id]])
|
||||
|
||||
def _find_allowed_doc_ids(self, cr, uid, model_ids, context=None):
|
||||
|
||||
@@ -113,22 +113,29 @@ class mail_thread(osv.AbstractModel):
|
||||
if alias_ids and len(alias_ids) == 1:
|
||||
alias = alias_obj.browse(cr, uid, alias_ids[0], context=context)
|
||||
|
||||
if alias:
|
||||
alias_email = alias.name_get()[0][1]
|
||||
return _("""<p class='oe_view_nocontent_create'>
|
||||
Click here to add new %(document)s or send an email to: <a href='mailto:%(email)s'>%(email)s</a>
|
||||
</p>
|
||||
%(static_help)s"""
|
||||
) % {
|
||||
'document': document_name,
|
||||
'email': alias_email,
|
||||
'static_help': help or ''
|
||||
}
|
||||
add_arrow = not help or help.find("oe_view_nocontent_create") == -1
|
||||
|
||||
if document_name != 'document' and help and help.find("oe_view_nocontent_create") == -1:
|
||||
return _("<p class='oe_view_nocontent_create'>Click here to add new %(document)s</p>%(static_help)s") % {
|
||||
'document': document_name,
|
||||
'static_help': help or '',
|
||||
if alias:
|
||||
email_link = "<a href='mailto:%(email)s'>%(email)s</a>" % {'email': alias.name_get()[0][1]}
|
||||
if add_arrow:
|
||||
return _("""<p class='oe_view_nocontent_create'>
|
||||
Click here to add new %(document)s or send an email to: %(email)s.
|
||||
</p>
|
||||
%(static_help)s"""
|
||||
) % {
|
||||
'document': document_name, 'email': email_link, 'static_help': help or ''
|
||||
}
|
||||
|
||||
return _("""%(static_help)s
|
||||
<p>
|
||||
You could also add a new %(document)s by sending an email to: %(email)s.
|
||||
</p>""") % {
|
||||
'document': document_name, 'email': email_link, 'static_help': help or ''
|
||||
}
|
||||
|
||||
if add_arrow:
|
||||
return _("<p class='oe_view_nocontent_create'>Click here to add new %(document)s</p>%(static_help)s") % {
|
||||
'document': document_name, 'static_help': help or ''
|
||||
}
|
||||
|
||||
return help
|
||||
@@ -370,7 +377,7 @@ class mail_thread(osv.AbstractModel):
|
||||
# auto_subscribe: take values and defaults into account
|
||||
create_values = dict(values)
|
||||
for key, val in context.iteritems():
|
||||
if key.startswith('default_'):
|
||||
if key.startswith('default_') and key[8:] not in create_values:
|
||||
create_values[key[8:]] = val
|
||||
self.message_auto_subscribe(cr, uid, [thread_id], create_values.keys(), context=context, values=create_values)
|
||||
|
||||
@@ -579,6 +586,7 @@ class mail_thread(osv.AbstractModel):
|
||||
ir_attachment_obj.unlink(cr, uid, attach_ids, context=context)
|
||||
return True
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def check_mail_message_access(self, cr, uid, mids, operation, model_obj=None, context=None):
|
||||
""" mail.message check permission rules for related document. This method is
|
||||
meant to be inherited in order to implement addons-specific behavior.
|
||||
@@ -937,6 +945,16 @@ class mail_thread(osv.AbstractModel):
|
||||
in_reply_to = decode_header(message, 'In-Reply-To')
|
||||
thread_references = references or in_reply_to
|
||||
|
||||
# 0. First check if this is a bounce message or not.
|
||||
# See http://datatracker.ietf.org/doc/rfc3462/?include_text=1
|
||||
# As all MTA does not respect this RFC (googlemail is one of them),
|
||||
# we also need to verify if the message come from "mailer-daemon"
|
||||
localpart = (tools.email_split(email_from) or [''])[0].split('@', 1)[0].lower()
|
||||
if message.get_content_type() == 'multipart/report' or localpart == 'mailer-daemon':
|
||||
_logger.info("Not routing bounce email from %s to %s with Message-Id %s",
|
||||
email_from, email_to, message_id)
|
||||
return []
|
||||
|
||||
# 1. message is a reply to an existing message (exact match of message_id)
|
||||
ref_match = thread_references and tools.reference_re.search(thread_references)
|
||||
msg_references = mail_header_msgid_re.findall(thread_references)
|
||||
|
||||
@@ -207,6 +207,7 @@ class mrp_bom(osv.osv):
|
||||
}
|
||||
_order = "sequence"
|
||||
|
||||
|
||||
def _bom_find(self, cr, uid, product_tmpl_id=None, product_id=None, properties=None, context=None):
|
||||
""" Finds BoM for particular product and product uom.
|
||||
@param product_tmpl_id: Selected product.
|
||||
@@ -1007,6 +1008,11 @@ class mrp_production(osv.osv):
|
||||
stock_mov_obj.action_done(cr, uid, [extra_move_id], context=context)
|
||||
|
||||
self.message_post(cr, uid, production_id, body=_("%s produced") % self._description, context=context)
|
||||
|
||||
# Remove remaining products to consume if no more products to produce
|
||||
if not production.move_created_ids and production.move_lines:
|
||||
stock_mov_obj.action_cancel(cr, uid, [x.id for x in production.move_lines], context=context)
|
||||
|
||||
self.signal_workflow(cr, uid, [production_id], 'button_produce_done')
|
||||
return True
|
||||
|
||||
|
||||
@@ -797,7 +797,7 @@
|
||||
<button name="action_compute" states="draft"
|
||||
string="Compute Data" type="object"
|
||||
icon="terp-stock_format-scientific"/>
|
||||
<field name="workcenter_lines">
|
||||
<field name="workcenter_lines" options="{'reload_on_button': true}">
|
||||
<form string="Production Work Centers">
|
||||
<group col="4">
|
||||
<field colspan="4" name="name"/>
|
||||
|
||||
@@ -305,7 +305,7 @@ class stock_warehouse(osv.osv):
|
||||
def _handle_renaming(self, cr, uid, warehouse, name, code, context=None):
|
||||
res = super(stock_warehouse, self)._handle_renaming(cr, uid, warehouse, name, code, context=context)
|
||||
pull_obj = self.pool.get('procurement.rule')
|
||||
#change the manufacture pull rule name
|
||||
#change the manufacture procurement rule name
|
||||
if warehouse.manufacture_pull_id:
|
||||
pull_obj.write(cr, uid, warehouse.manufacture_pull_id.id, {'name': warehouse.manufacture_pull_id.name.replace(warehouse.name, name, 1)}, context=context)
|
||||
return res
|
||||
|
||||
@@ -220,7 +220,7 @@ class mrp_production(osv.osv):
|
||||
workcenter_line.signal_workflow('button_done')
|
||||
return super(mrp_production,self).action_production_end(cr, uid, ids, context=context)
|
||||
|
||||
def action_in_production(self, cr, uid, ids):
|
||||
def action_in_production(self, cr, uid, ids, context=None):
|
||||
""" Changes state to In Production and writes starting date.
|
||||
@return: True
|
||||
"""
|
||||
@@ -228,7 +228,7 @@ class mrp_production(osv.osv):
|
||||
for prod in self.browse(cr, uid, ids):
|
||||
if prod.workcenter_lines:
|
||||
workcenter_pool.signal_workflow(cr, uid, [prod.workcenter_lines[0].id], 'button_start_working')
|
||||
return super(mrp_production,self).action_in_production(cr, uid, ids)
|
||||
return super(mrp_production,self).action_in_production(cr, uid, ids, context=context)
|
||||
|
||||
def action_cancel(self, cr, uid, ids, context=None):
|
||||
""" Cancels work order if production order is canceled.
|
||||
|
||||
@@ -167,6 +167,7 @@
|
||||
<field name="product_uom_qty" string="Quantity"/>
|
||||
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="tax_id" domain="[('parent_id', '=', False), ('type_tax_use', '=', 'purchase')]" widget="many2many_tags"/>
|
||||
<field name="to_invoice"/>
|
||||
<field name="price_subtotal"/>
|
||||
</tree>
|
||||
|
||||
@@ -154,7 +154,7 @@ class PaymentAcquirerOgone(osv.Model):
|
||||
'OWNERZIP': partner_values['zip'],
|
||||
'OWNERADDRESS': partner_values['address'],
|
||||
'OWNERTOWN': partner_values['city'],
|
||||
'OWNERCTY': partner_values['country'] and partner_values['country'].name or '',
|
||||
'OWNERCTY': partner_values['country'] and partner_values['country'].code or '',
|
||||
'OWNERTELNO': partner_values['phone'],
|
||||
'ACCEPTURL': '%s' % urlparse.urljoin(base_url, OgoneController._accept_url),
|
||||
'DECLINEURL': '%s' % urlparse.urljoin(base_url, OgoneController._decline_url),
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
import sets
|
||||
|
||||
@@ -534,13 +535,13 @@ class pos_session(osv.osv):
|
||||
}
|
||||
|
||||
def _confirm_orders(self, cr, uid, ids, context=None):
|
||||
account_move_obj = self.pool.get('account.move')
|
||||
pos_order_obj = self.pool.get('pos.order')
|
||||
for session in self.browse(cr, uid, ids, context=context):
|
||||
local_context = dict(context or {}, force_company=session.config_id.journal_id.company_id.id)
|
||||
company_id = session.config_id.journal_id.company_id.id
|
||||
local_context = dict(context or {}, force_company=company_id)
|
||||
order_ids = [order.id for order in session.order_ids if order.state == 'paid']
|
||||
|
||||
move_id = account_move_obj.create(cr, uid, {'ref' : session.name, 'journal_id' : session.config_id.journal_id.id, }, context=local_context)
|
||||
move_id = pos_order_obj._create_account_move(cr, uid, session.start_at, session.name, session.config_id.journal_id.id, company_id, context=context)
|
||||
|
||||
pos_order_obj._create_account_move_line(cr, uid, order_ids, session, move_id, context=local_context)
|
||||
|
||||
@@ -998,6 +999,7 @@ class pos_order(osv.osv):
|
||||
'invoice_id': inv_id,
|
||||
'product_id': line.product_id.id,
|
||||
'quantity': line.qty,
|
||||
'account_analytic_id': self._prepare_analytic_account(cr, uid, line, context=context),
|
||||
}
|
||||
inv_name = product_obj.name_get(cr, uid, [line.product_id.id], context=context)[0][1]
|
||||
inv_line.update(inv_line_ref.product_id_change(cr, uid, [],
|
||||
@@ -1040,6 +1042,14 @@ class pos_order(osv.osv):
|
||||
'''This method is designed to be inherited in a custom module'''
|
||||
return False
|
||||
|
||||
def _create_account_move(self, cr, uid, dt, ref, journal_id, company_id, context=None):
|
||||
local_context = dict(context or {}, company_id=company_id)
|
||||
start_at_datetime = datetime.strptime(dt, tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
date_tz_user = fields.datetime.context_timestamp(cr, uid, start_at_datetime, context=context)
|
||||
date_tz_user = date_tz_user.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
|
||||
period_id = self.pool['account.period'].find(cr, uid, dt=date_tz_user, context=local_context)
|
||||
return self.pool['account.move'].create(cr, uid, {'ref': ref, 'journal_id': journal_id, 'period_id': period_id[0]}, context=context)
|
||||
|
||||
def _create_account_move_line(self, cr, uid, ids, session=None, move_id=None, context=None):
|
||||
# Tricky, via the workflow, we only have one id in the ids variable
|
||||
"""Create a account move line of order grouped by products or not."""
|
||||
@@ -1063,7 +1073,7 @@ class pos_order(osv.osv):
|
||||
tax_amount = line.price_subtotal * tax['base_sign']
|
||||
else:
|
||||
tax_code_id = tax['ref_base_code_id']
|
||||
tax_amount = line.price_subtotal * tax['ref_base_sign']
|
||||
tax_amount = -line.price_subtotal * tax['ref_base_sign']
|
||||
|
||||
return (tax_code_id, tax_amount,)
|
||||
|
||||
@@ -1085,16 +1095,14 @@ class pos_order(osv.osv):
|
||||
|
||||
if move_id is None:
|
||||
# Create an entry for the sale
|
||||
move_id = account_move_obj.create(cr, uid, {
|
||||
'ref' : order.name,
|
||||
'journal_id': order.sale_journal.id,
|
||||
}, context=context)
|
||||
move_id = self._create_account_move(cr, uid, order.session_id.start_at, order.name, order.sale_journal.id, order.company_id.id, context=context)
|
||||
|
||||
move = account_move_obj.browse(cr, uid, move_id, context=context)
|
||||
|
||||
def insert_data(data_type, values):
|
||||
# if have_to_group_by:
|
||||
|
||||
sale_journal_id = order.sale_journal.id
|
||||
period = account_period_obj.find(cr, uid, context=dict(context or {}, company_id=current_company.id))[0]
|
||||
|
||||
# 'quantity': line.qty,
|
||||
# 'product_id': line.product_id.id,
|
||||
@@ -1103,7 +1111,7 @@ class pos_order(osv.osv):
|
||||
'ref': order.name,
|
||||
'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False,
|
||||
'journal_id' : sale_journal_id,
|
||||
'period_id' : period,
|
||||
'period_id': move.period_id.id,
|
||||
'move_id' : move_id,
|
||||
'company_id': current_company.id,
|
||||
})
|
||||
@@ -1155,7 +1163,10 @@ class pos_order(osv.osv):
|
||||
|
||||
for tax in computed_taxes:
|
||||
tax_amount += cur_obj.round(cr, uid, cur, tax['amount'])
|
||||
group_key = (tax['tax_code_id'], tax['base_code_id'], tax['account_collected_id'], tax['id'])
|
||||
if tax_amount < 0:
|
||||
group_key = (tax['ref_tax_code_id'], tax['base_code_id'], tax['account_collected_id'], tax['id'])
|
||||
else:
|
||||
group_key = (tax['tax_code_id'], tax['base_code_id'], tax['account_collected_id'], tax['id'])
|
||||
|
||||
group_tax.setdefault(group_key, 0)
|
||||
group_tax[group_key] += cur_obj.round(cr, uid, cur, tax['amount'])
|
||||
@@ -1233,7 +1244,7 @@ class pos_order(osv.osv):
|
||||
'credit': ((tax_amount>0) and tax_amount) or 0.0,
|
||||
'debit': ((tax_amount<0) and -tax_amount) or 0.0,
|
||||
'tax_code_id': key[tax_code_pos],
|
||||
'tax_amount': tax_amount,
|
||||
'tax_amount': abs(tax_amount),
|
||||
'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False
|
||||
})
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ class pos_lines(report_sxw.rml_parse):
|
||||
def __taxes__(self, obj):
|
||||
self.cr.execute ( " Select acct.name from pos_order as po " \
|
||||
" LEFT JOIN pos_order_line as pol ON po.id = pol.order_id " \
|
||||
" LEFT JOIN product_taxes_rel as ptr ON pol.product_id = ptr.prod_id " \
|
||||
" LEFT JOIN product_product as pp ON pol.product_id = pp.id"
|
||||
" LEFT JOIN product_taxes_rel as ptr ON pp.product_tmpl_id = ptr.prod_id " \
|
||||
" LEFT JOIN account_tax as acct ON acct.id = ptr.tax_id " \
|
||||
" WHERE pol.id = %s", (obj.id,))
|
||||
res=self.cr.fetchone()[0]
|
||||
|
||||
@@ -12,3 +12,13 @@ class res_users(osv.osv):
|
||||
'pos_security_pin': fields.char('Security PIN',size=32, help='A Security PIN used to protect sensible functionality in the Point of Sale'),
|
||||
'pos_config' : fields.many2one('pos.config', 'Default Point of Sale', domain=[('state', '=', 'active')]),
|
||||
}
|
||||
|
||||
def _check_pin(self, cr, uid, ids, context=None):
|
||||
for user in self.browse(cr, uid, ids, context=context):
|
||||
if user.pos_security_pin and not user.pos_security_pin.isdigit():
|
||||
return False
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_pin, "Security PIN can only contain digits",['pos_security_pin']),
|
||||
]
|
||||
|
||||
@@ -226,7 +226,7 @@ var Gui = core.Class.extend({
|
||||
var self = this;
|
||||
var ret = new $.Deferred();
|
||||
if (password) {
|
||||
this.gui.show_popup('password',{
|
||||
this.show_popup('password',{
|
||||
'title': _t('Password ?'),
|
||||
confirm: function(pw) {
|
||||
if (pw !== password) {
|
||||
|
||||
@@ -194,15 +194,20 @@ exports.PosModel = Backbone.Model.extend({
|
||||
},
|
||||
},{
|
||||
model: 'account.tax',
|
||||
fields: ['name','amount', 'price_include', 'include_base_amount', 'type'],
|
||||
fields: ['name','amount', 'price_include', 'include_base_amount', 'type', 'child_ids', 'child_depend', 'include_base_amount'],
|
||||
domain: null,
|
||||
loaded: function(self,taxes){
|
||||
self.taxes = taxes;
|
||||
loaded: function(self, taxes){
|
||||
self.taxes = taxes;
|
||||
self.taxes_by_id = {};
|
||||
|
||||
for (var i = 0; i < taxes.length; i++) {
|
||||
self.taxes_by_id[taxes[i].id] = taxes[i];
|
||||
}
|
||||
_.each(taxes, function(tax){
|
||||
self.taxes_by_id[tax.id] = tax;
|
||||
});
|
||||
_.each(self.taxes_by_id, function(tax) {
|
||||
tax.child_taxes = {};
|
||||
_.each(tax.child_ids, function(child_tax_id) {
|
||||
tax.child_taxes[child_tax_id] = self.taxes_by_id[child_tax_id];
|
||||
});
|
||||
});
|
||||
},
|
||||
},{
|
||||
model: 'pos.session',
|
||||
@@ -1107,51 +1112,84 @@ exports.Orderline = Backbone.Model.extend({
|
||||
}
|
||||
return taxes;
|
||||
},
|
||||
get_all_prices: function(){
|
||||
compute_all: function(taxes, price_unit) {
|
||||
var self = this;
|
||||
var res = [];
|
||||
var currency_rounding = this.pos.currency.rounding;
|
||||
var base = this.get_base_price();
|
||||
var totalTax = base;
|
||||
var totalNoTax = base;
|
||||
|
||||
var product = this.get_product();
|
||||
var taxes = this.get_applicable_taxes();
|
||||
var taxtotal = 0;
|
||||
var taxdetail = {};
|
||||
_.each(taxes, function(tax) {
|
||||
var tmp;
|
||||
var base = price_unit;
|
||||
_(taxes).each(function(tax) {
|
||||
if (tax.price_include) {
|
||||
if (tax.type === "percent") {
|
||||
tmp = base - round_pr(base / (1 + tax.amount),currency_rounding);
|
||||
tmp = round_pr(base - round_pr(base / (1 + tax.amount),currency_rounding),currency_rounding);
|
||||
data = {amount:tmp, price_include:true, id: tax.id};
|
||||
res.push(data);
|
||||
} else if (tax.type === "fixed") {
|
||||
tmp = round_pr(tax.amount * self.get_quantity(),currency_rounding);
|
||||
data = {amount:tmp, price_include:true, id: tax.id};
|
||||
res.push(data);
|
||||
} else {
|
||||
throw "This type of tax is not supported by the point of sale: " + tax.type;
|
||||
}
|
||||
tmp = round_pr(tmp,currency_rounding);
|
||||
taxtotal += tmp;
|
||||
totalNoTax -= tmp;
|
||||
taxdetail[tax.id] = tmp;
|
||||
} else {
|
||||
if (tax.type === "percent") {
|
||||
tmp = tax.amount * base;
|
||||
tmp = round_pr(tax.amount * base, currency_rounding);
|
||||
data = {amount:tmp, price_include:false, id: tax.id};
|
||||
res.push(data);
|
||||
} else if (tax.type === "fixed") {
|
||||
tmp = tax.amount * self.get_quantity();
|
||||
tmp = round_pr(tax.amount * self.get_quantity(), currency_rounding);
|
||||
data = {amount:tmp, price_include:false, id: tax.id};
|
||||
res.push(data);
|
||||
} else {
|
||||
throw "This type of tax is not supported by the point of sale: " + tax.type;
|
||||
}
|
||||
|
||||
tmp = round_pr(tmp,currency_rounding);
|
||||
|
||||
if (tax.include_base_amount) {
|
||||
base += tmp;
|
||||
var base_amount = data.amount;
|
||||
var child_amount = 0.0;
|
||||
if (tax.child_depend) {
|
||||
res.pop(); // do not use parent tax
|
||||
child_tax = self.compute_all(tax.child_taxes, base_amount);
|
||||
res.push(child_tax);
|
||||
_(child_tax).each(function(child) {
|
||||
child_amount += child.amount;
|
||||
});
|
||||
}
|
||||
if (tax.include_base_amount) {
|
||||
base += base_amount + child_amount;
|
||||
}
|
||||
|
||||
taxtotal += tmp;
|
||||
totalTax += tmp;
|
||||
taxdetail[tax.id] = tmp;
|
||||
}
|
||||
});
|
||||
return res;
|
||||
},
|
||||
get_all_prices: function(){
|
||||
var base = round_pr(this.get_quantity() * this.get_unit_price() * (1.0 - (this.get_discount() / 100.0)), this.pos.currency.rounding);
|
||||
var totalTax = base;
|
||||
var totalNoTax = base;
|
||||
var taxtotal = 0;
|
||||
|
||||
var product = this.get_product();
|
||||
var taxes_ids = product.taxes_id;
|
||||
var taxes = this.pos.taxes;
|
||||
var taxdetail = {};
|
||||
var product_taxes = [];
|
||||
|
||||
_(taxes_ids).each(function(el){
|
||||
product_taxes.push(_.detect(taxes, function(t){
|
||||
return t.id === el;
|
||||
}));
|
||||
});
|
||||
|
||||
var all_taxes = _(this.compute_all(product_taxes, base)).flatten();
|
||||
|
||||
_(all_taxes).each(function(tax) {
|
||||
if (tax.price_include) {
|
||||
totalNoTax -= tax.amount;
|
||||
} else {
|
||||
totalTax += tax.amount;
|
||||
}
|
||||
taxtotal += tax.amount;
|
||||
taxdetail[tax.id] = tax.amount;
|
||||
});
|
||||
|
||||
return {
|
||||
"priceWithTax": totalTax,
|
||||
"priceWithoutTax": totalNoTax,
|
||||
@@ -1882,4 +1920,4 @@ exports.NumpadState = Backbone.Model.extend({
|
||||
// };
|
||||
return exports;
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -142,20 +142,13 @@ var SelectionPopupWidget = PopupWidget.extend({
|
||||
|
||||
this.list = options.list || [];
|
||||
this.renderElement();
|
||||
|
||||
this.$('.selection-item').click(function(){
|
||||
if (options.confirm) {
|
||||
var item = self.list[parseInt($(this).data('item-index'))];
|
||||
item = item ? item.item : item;
|
||||
options.confirm.call(self,item);
|
||||
}
|
||||
});
|
||||
},
|
||||
click_item : function(event) {
|
||||
this.gui.close_popup();
|
||||
if (this.options.confirm) {
|
||||
var item = this.list[parseInt($(event.target).data('item-index'))];
|
||||
item = item ? item.item : item;
|
||||
this.options.confirm.call(self,item);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -194,7 +187,7 @@ var NumberPopupWidget = PopupWidget.extend({
|
||||
options = options || {};
|
||||
this._super(options);
|
||||
|
||||
this.inputbuffer = '' + options.value || '';
|
||||
this.inputbuffer = '' + (options.value || '');
|
||||
this.renderElement();
|
||||
this.firstinput = true;
|
||||
},
|
||||
|
||||
@@ -1129,7 +1129,7 @@ var ClientListScreenWidget = ScreenWidget.extend({
|
||||
|
||||
fields.id = partner.id || false;
|
||||
fields.country_id = fields.country_id || false;
|
||||
fields.barcode = fields.barcode ? this.pos.barcode_reader.sanitize_ean(fields.barcode) : false;
|
||||
fields.barcode = fields.barcode || '';
|
||||
|
||||
new Model('res.partner').call('create_from_ui',[fields]).then(function(partner_id){
|
||||
self.saved_client_details(partner_id);
|
||||
@@ -1316,7 +1316,6 @@ gui.define_screen({name:'clientlist', widget: ClientListScreenWidget});
|
||||
|
||||
var ReceiptScreenWidget = ScreenWidget.extend({
|
||||
template: 'ReceiptScreenWidget',
|
||||
|
||||
show: function(){
|
||||
this._super();
|
||||
var self = this;
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
<field name="model">procurement.rule</field>
|
||||
<field eval="10" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Pull Rules">
|
||||
<tree string="Procurement Rules">
|
||||
<field name="name"/>
|
||||
<field name="action"/>
|
||||
<field name='company_id' groups="base.group_multi_company"/>
|
||||
@@ -221,7 +221,7 @@
|
||||
<field name="model">procurement.rule</field>
|
||||
<field eval="10" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Pull Rule">
|
||||
<form string="Procurement Rule">
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name"/></h1>
|
||||
|
||||
@@ -246,8 +246,25 @@ class product_category(osv.osv):
|
||||
context = {}
|
||||
if name:
|
||||
# Be sure name_search is symetric to name_get
|
||||
name = name.split(' / ')[-1]
|
||||
ids = self.search(cr, uid, [('name', operator, name)] + args, limit=limit, context=context)
|
||||
categories = name.split(' / ')
|
||||
parents = list(categories)
|
||||
child = parents.pop()
|
||||
domain = [('name', operator, child)]
|
||||
if parents:
|
||||
names_ids = self.name_search(cr, uid, ' / '.join(parents), args=args, operator='ilike', context=context, limit=limit)
|
||||
category_ids = [name_id[0] for name_id in names_ids]
|
||||
if operator in expression.NEGATIVE_TERM_OPERATORS:
|
||||
category_ids = self.search(cr, uid, [('id', 'not in', category_ids)])
|
||||
domain = expression.OR([[('parent_id', 'in', category_ids)], domain])
|
||||
else:
|
||||
domain = expression.AND([[('parent_id', 'in', category_ids)], domain])
|
||||
for i in range(1, len(categories)):
|
||||
domain = [[('name', operator, ' / '.join(categories[-1 - i:]))], domain]
|
||||
if operator in expression.NEGATIVE_TERM_OPERATORS:
|
||||
domain = expression.AND(domain)
|
||||
else:
|
||||
domain = expression.OR(domain)
|
||||
ids = self.search(cr, uid, expression.AND([domain, args]), limit=limit, context=context)
|
||||
else:
|
||||
ids = self.search(cr, uid, args, limit=limit, context=context)
|
||||
return self.name_get(cr, uid, ids, context)
|
||||
@@ -808,7 +825,7 @@ class product_template(osv.osv):
|
||||
# re-apply product.template order + name_get
|
||||
return super(product_template, self).name_search(
|
||||
cr, user, '', args=[('id', 'in', template_ids)],
|
||||
operator=operator, context=context, limit=limit)
|
||||
operator='ilike', context=context, limit=limit)
|
||||
|
||||
class product_product(osv.osv):
|
||||
_name = "product.product"
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<field name="category_id" ref="product_uom_categ_unit"/>
|
||||
<field name="name">Unit(s)</field>
|
||||
<field name="factor" eval="1.0"/>
|
||||
<field name="rounding" eval="1.0"/>
|
||||
<field name="rounding" eval="0.001"/>
|
||||
</record>
|
||||
<record id="product_uom_dozen" model="product.uom">
|
||||
<field name="category_id" ref="product.product_uom_categ_unit"/>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<separator/>
|
||||
<filter string="Can be Sold" name="filter_to_sell" icon="terp-accessories-archiver-minus" domain="[('sale_ok','=',1)]"/>
|
||||
<field name="categ_id" filter_domain="[('categ_id', 'child_of', self)]"/>
|
||||
<field string="Product Variant" name="product_variant_ids" filter_domain="['|', ('product_variant_ids.name','ilike',self), ('product_variant_ids.attribute_value_ids.name','ilike',self)]"/>
|
||||
<field string="Product Variant" name="product_variant_ids" filter_domain="['|', ('product_variant_ids.name','ilike',self), ('product_variant_ids.attribute_value_ids.attribute_id.name','ilike',self)]"/>
|
||||
<field name="company_id"/>
|
||||
<field name="pricelist_id" widget="selection" context="{'pricelist': self}" filter_domain="[]" groups="product.group_sale_pricelist"/> <!-- Keep widget=selection on this field to pass numeric `self` value, which is not the case for regular m2o widgets! -->
|
||||
<group expand='0' string='Group by...'>
|
||||
@@ -620,23 +620,20 @@
|
||||
<field name="name"/>
|
||||
<field name="category_id"/>
|
||||
<field name="uom_type" on_change="onchange_type(uom_type)"/>
|
||||
<label for="factor"/>
|
||||
<div>
|
||||
<field name="factor"
|
||||
digits="[42,5]"
|
||||
attrs="{'invisible':[('uom_type','!=','smaller')],
|
||||
'readonly':[('uom_type','=','bigger')]}"/>
|
||||
<field name="factor_inv"
|
||||
digits="[42,5]"
|
||||
attrs="{'invisible':[('uom_type','!=','bigger')],
|
||||
'readonly':[('uom_type','!=','bigger')]}"/>
|
||||
<p attrs="{'invisible':[('uom_type','!=','smaller')]}" class="oe_grey">
|
||||
e.g: 1 * (reference unit) = ratio * (this unit)
|
||||
</p>
|
||||
<p attrs="{'invisible':[('uom_type','!=','bigger')]}" class="oe_grey">
|
||||
e.g: 1 * (this unit) = ratio * (reference unit)
|
||||
</p>
|
||||
</div>
|
||||
<field name="factor"
|
||||
digits="[42,5]"
|
||||
attrs="{'invisible':[('uom_type','!=','smaller')],
|
||||
'readonly':[('uom_type','=','bigger')]}"/>
|
||||
<field name="factor_inv"
|
||||
digits="[42,5]"
|
||||
attrs="{'invisible':[('uom_type','!=','bigger')],
|
||||
'readonly':[('uom_type','!=','bigger')]}"/>
|
||||
<p attrs="{'invisible':[('uom_type','!=','smaller')]}" class="oe_grey" colspan="2">
|
||||
e.g: 1 * (reference unit) = ratio * (this unit)
|
||||
</p>
|
||||
<p attrs="{'invisible':[('uom_type','!=','bigger')]}" class="oe_grey" colspan="2">
|
||||
e.g: 1 * (this unit) = ratio * (reference unit)
|
||||
</p>
|
||||
</group>
|
||||
<group>
|
||||
<field name="active"/>
|
||||
|
||||
@@ -707,7 +707,6 @@ class task(osv.osv):
|
||||
res[task.id]['progress'] = 0.0
|
||||
if (task.remaining_hours + hours.get(task.id, 0.0)):
|
||||
res[task.id]['progress'] = round(min(100.0 * hours.get(task.id, 0.0) / res[task.id]['total_hours'], 99.99),2)
|
||||
# TDE CHECK: if task.state in ('done','cancelled'):
|
||||
if task.stage_id and task.stage_id.fold:
|
||||
res[task.id]['progress'] = 100.0
|
||||
return res
|
||||
@@ -889,18 +888,23 @@ class task(osv.osv):
|
||||
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||
users_obj = self.pool.get('res.users')
|
||||
if context is None: context = {}
|
||||
# read uom as admin to avoid access rights issues, e.g. for portal/share users,
|
||||
# this should be safe (no context passed to avoid side-effects)
|
||||
obj_tm = users_obj.browse(cr, SUPERUSER_ID, uid, context=context).company_id.project_time_mode_id
|
||||
tm = obj_tm and obj_tm.name or 'Hours'
|
||||
|
||||
res = super(task, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu=submenu)
|
||||
|
||||
if tm in ['Hours','Hour']:
|
||||
# read uom as admin to avoid access rights issues, e.g. for portal/share users,
|
||||
# this should be safe (no context passed to avoid side-effects)
|
||||
obj_tm = users_obj.browse(cr, SUPERUSER_ID, uid, context=context).company_id.project_time_mode_id
|
||||
try:
|
||||
# using get_object to get translation value
|
||||
uom_hour = self.pool['ir.model.data'].get_object(cr, uid, 'product', 'product_uom_hour', context=context)
|
||||
except ValueError:
|
||||
uom_hour = False
|
||||
if not obj_tm or not uom_hour or obj_tm.id == uom_hour.id:
|
||||
return res
|
||||
|
||||
eview = etree.fromstring(res['arch'])
|
||||
|
||||
# if the project_time_mode_id is not in hours (so in days), display it as a float field
|
||||
def _check_rec(eview):
|
||||
if eview.attrib.get('widget','') == 'float_time':
|
||||
eview.set('widget','float')
|
||||
@@ -912,9 +916,13 @@ class task(osv.osv):
|
||||
|
||||
res['arch'] = etree.tostring(eview)
|
||||
|
||||
# replace reference of 'Hours' to 'Day(s)'
|
||||
for f in res['fields']:
|
||||
# TODO this NOT work in different language than english
|
||||
# the field 'Initially Planned Hours' should be replaced by 'Initially Planned Days'
|
||||
# but string 'Initially Planned Days' is not available in translation
|
||||
if 'Hours' in res['fields'][f]['string']:
|
||||
res['fields'][f]['string'] = res['fields'][f]['string'].replace('Hours',tm)
|
||||
res['fields'][f]['string'] = res['fields'][f]['string'].replace('Hours', obj_tm.name)
|
||||
return res
|
||||
|
||||
def get_empty_list_help(self, cr, uid, help, context=None):
|
||||
@@ -1008,7 +1016,7 @@ class task(osv.osv):
|
||||
def set_remaining_time(self, cr, uid, ids, remaining_time=1.0, context=None):
|
||||
for task in self.browse(cr, uid, ids, context=context):
|
||||
if (task.stage_id and task.stage_id.sequence <= 1) or (task.planned_hours == 0.0):
|
||||
self.write(cr, uid, [task.id], {'planned_hours': remaining_time}, context=context)
|
||||
self.write(cr, uid, [task.id], {'planned_hours': remaining_time + task.effective_hours}, context=context)
|
||||
self.write(cr, uid, ids, {'remaining_hours': remaining_time}, context=context)
|
||||
return True
|
||||
|
||||
@@ -1136,11 +1144,11 @@ class task(osv.osv):
|
||||
return 'project.mt_task_stage'
|
||||
return super(task, self)._track_subtype(cr, uid, ids, init_values, context=context)
|
||||
|
||||
def message_get_reply_to(self, cr, uid, ids, context=None):
|
||||
def message_get_reply_to(self, cr, uid, ids, default=None, context=None):
|
||||
""" Override to get the reply_to of the parent project. """
|
||||
tasks = self.browse(cr, SUPERUSER_ID, ids, context=context)
|
||||
project_ids = set([task.project_id.id for task in tasks if task.project_id])
|
||||
aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(project_ids), context=context)
|
||||
aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(project_ids), default=default, context=context)
|
||||
return dict((task.id, aliases.get(task.project_id and task.project_id.id or 0, False)) for task in tasks)
|
||||
|
||||
def message_new(self, cr, uid, msg, custom_values=None, context=None):
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Tasks Analysis" type="pivot">
|
||||
<field name="project_id" type="row"/>
|
||||
<field name="user_id" type="col"/>
|
||||
<field name="nbr" type="measure"/>
|
||||
</graph>
|
||||
</field>
|
||||
|
||||
@@ -47,7 +47,8 @@ class project_configuration(osv.osv_memory):
|
||||
'module_rating_project': fields.boolean('Allow customers to rate provided services',
|
||||
help="This allows customers to give rating on provided services"),
|
||||
'time_unit': fields.many2one('product.uom', 'Working time unit', required=True,
|
||||
help="""This will set the unit of measure used in projects and tasks."""),
|
||||
help='This will set the unit of measure used in projects and tasks.\n'
|
||||
'Changing the unit will only impact new entries.'),
|
||||
'module_project_issue_sheet': fields.boolean("Invoice working time on issues",
|
||||
help='Provides timesheet support for the issues/bugs management in project.\n'
|
||||
'-This installs the module project_issue_sheet.'),
|
||||
|
||||
@@ -423,11 +423,11 @@ class project_issue(osv.Model):
|
||||
return 'project_issue.mt_issue_stage'
|
||||
return super(project_issue, self)._track_subtype(cr, uid, ids, init_values, context=context)
|
||||
|
||||
def message_get_reply_to(self, cr, uid, ids, context=None):
|
||||
def message_get_reply_to(self, cr, uid, ids, default=None, context=None):
|
||||
""" Override to get the reply_to of the parent project. """
|
||||
issues = self.browse(cr, SUPERUSER_ID, ids, context=context)
|
||||
project_ids = set([issue.project_id.id for issue in issues if issue.project_id])
|
||||
aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(project_ids), context=context)
|
||||
aliases = self.pool['project.project'].message_get_reply_to(cr, uid, list(project_ids), default=default, context=context)
|
||||
return dict((issue.id, aliases.get(issue.project_id and issue.project_id.id or 0, False)) for issue in issues)
|
||||
|
||||
def message_get_suggested_recipients(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -86,13 +86,13 @@ class project_work(osv.osv):
|
||||
raise UserError(_('Please define product and product category property account on the related employee.\nFill in the HR Settings tab of the employee form.'))
|
||||
|
||||
if not emp.journal_id:
|
||||
raise UserError(_('Please define journal on the related employee.\nFill in the timesheet tab of the employee form.'))
|
||||
raise UserError(_('Please define journal on the related employee.\nFill in the HR Settings tab of the employee form.'))
|
||||
|
||||
acc_id = emp.product_id.property_account_expense.id
|
||||
if not acc_id:
|
||||
acc_id = emp.product_id.categ_id.property_account_expense_categ.id
|
||||
if not acc_id:
|
||||
raise UserError(_('Please define product and product category property account on the related employee.\nFill in the timesheet tab of the employee form.'))
|
||||
raise UserError(_('Please define product and product category property account on the related employee.\nFill in the HR Settings of the employee form.'))
|
||||
|
||||
res['product_id'] = emp.product_id.id
|
||||
res['journal_id'] = emp.journal_id.id
|
||||
|
||||
@@ -843,7 +843,7 @@ class purchase_order(osv.osv):
|
||||
picking_vals = {
|
||||
'picking_type_id': order.picking_type_id.id,
|
||||
'partner_id': order.partner_id.id,
|
||||
'date': max([l.date_planned for l in order.order_line]),
|
||||
'date': order.date_order,
|
||||
'origin': order.name
|
||||
}
|
||||
picking_id = self.pool.get('stock.picking').create(cr, uid, picking_vals, context=context)
|
||||
@@ -870,7 +870,7 @@ class purchase_order(osv.osv):
|
||||
Orders will only be merged if:
|
||||
* Purchase Orders are in draft
|
||||
* Purchase Orders belong to the same partner
|
||||
* Purchase Orders are have same stock location, same pricelist
|
||||
* Purchase Orders are have same stock location, same pricelist, same currency
|
||||
Lines will only be merged if:
|
||||
* Order lines are exactly the same except for the quantity and unit
|
||||
|
||||
@@ -906,12 +906,13 @@ class purchase_order(osv.osv):
|
||||
# Compute what the new orders should contain
|
||||
new_orders = {}
|
||||
|
||||
order_lines_to_move = []
|
||||
order_lines_to_move = {}
|
||||
for porder in [order for order in self.browse(cr, uid, ids, context=context) if order.state == 'draft']:
|
||||
order_key = make_key(porder, ('partner_id', 'location_id', 'pricelist_id'))
|
||||
order_key = make_key(porder, ('partner_id', 'location_id', 'pricelist_id', 'currency_id'))
|
||||
new_order = new_orders.setdefault(order_key, ({}, []))
|
||||
new_order[1].append(porder.id)
|
||||
order_infos = new_order[0]
|
||||
order_lines_to_move.setdefault(order_key, [])
|
||||
|
||||
if not order_infos:
|
||||
order_infos.update({
|
||||
@@ -922,6 +923,7 @@ class purchase_order(osv.osv):
|
||||
'picking_type_id': porder.picking_type_id.id,
|
||||
'location_id': porder.location_id.id,
|
||||
'pricelist_id': porder.pricelist_id.id,
|
||||
'currency_id': porder.currency_id.id,
|
||||
'state': 'draft',
|
||||
'order_line': {},
|
||||
'notes': '%s' % (porder.notes or '',),
|
||||
@@ -935,8 +937,7 @@ class purchase_order(osv.osv):
|
||||
if porder.origin:
|
||||
order_infos['origin'] = (order_infos['origin'] or '') + ' ' + porder.origin
|
||||
|
||||
for order_line in porder.order_line:
|
||||
order_lines_to_move += [order_line.id]
|
||||
order_lines_to_move[order_key] += [order_line.id for order_line in porder.order_line]
|
||||
|
||||
allorders = []
|
||||
orders_info = {}
|
||||
@@ -950,7 +951,7 @@ class purchase_order(osv.osv):
|
||||
for key, value in order_data['order_line'].iteritems():
|
||||
del value['uom_factor']
|
||||
value.update(dict(key))
|
||||
order_data['order_line'] = [(6, 0, order_lines_to_move)]
|
||||
order_data['order_line'] = [(6, 0, order_lines_to_move[order_key])]
|
||||
|
||||
# create the new order
|
||||
context.update({'mail_create_nolog': True})
|
||||
|
||||
@@ -32,6 +32,13 @@ class stock_move(osv.osv):
|
||||
readonly=True),
|
||||
}
|
||||
|
||||
def get_price_unit(self, cr, uid, move, context=None):
|
||||
""" Returns the unit price to store on the quant """
|
||||
if move.purchase_line_id:
|
||||
return move.price_unit
|
||||
|
||||
return super(stock_move, self).get_price_unit(cr, uid, move, context=context)
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
@@ -113,11 +120,26 @@ class stock_move(osv.osv):
|
||||
res['price_unit'] = purchase_line.price_unit
|
||||
return res
|
||||
|
||||
def _get_moves_taxes(self, cr, uid, moves, context=None):
|
||||
is_extra_move, extra_move_tax = super(stock_move, self)._get_moves_taxes(cr, uid, moves, context=context)
|
||||
for move in moves:
|
||||
if move.purchase_line_id:
|
||||
is_extra_move[move.id] = False
|
||||
extra_move_tax[move.picking_id, move.product_id] = [(6, 0, [x.id for x in move.purchase_line_id.taxes_id])]
|
||||
return (is_extra_move, extra_move_tax)
|
||||
|
||||
|
||||
def attribute_price(self, cr, uid, move, context=None):
|
||||
"""
|
||||
Attribute price to move, important in inter-company moves or receipts with only one partner
|
||||
"""
|
||||
# The method attribute_price of the parent class sets the price to the standard product
|
||||
# price if move.price_unit is zero. We don't want this behavior in the case of a purchase
|
||||
# order since we can purchase goods which are free of charge (e.g. 5 units offered if 100
|
||||
# are purchased).
|
||||
if move.purchase_line_id:
|
||||
return
|
||||
|
||||
code = self.get_code_from_locs(cr, uid, move, context=context)
|
||||
if not move.purchase_line_id and code == 'incoming' and not move.price_unit:
|
||||
partner = move.picking_id and move.picking_id.partner_id or False
|
||||
@@ -260,7 +282,7 @@ class stock_warehouse(osv.osv):
|
||||
def _handle_renaming(self, cr, uid, warehouse, name, code, context=None):
|
||||
res = super(stock_warehouse, self)._handle_renaming(cr, uid, warehouse, name, code, context=context)
|
||||
pull_obj = self.pool.get('procurement.rule')
|
||||
#change the buy pull rule name
|
||||
#change the buy procurement rule name
|
||||
if warehouse.buy_pull_id:
|
||||
pull_obj.write(cr, uid, warehouse.buy_pull_id.id, {'name': warehouse.buy_pull_id.name.replace(warehouse.name, name, 1)}, context=context)
|
||||
return res
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Set a product as using average price.
|
||||
-
|
||||
!record {model: product.product, id: product_variable_icecream}:
|
||||
default_code: AVG
|
||||
default_code: VARAVG
|
||||
name: Variable Ice Cream
|
||||
type: product
|
||||
categ_id: product.product_category_1
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Set a product as using fifo price
|
||||
-
|
||||
!record {model: product.product, id: product_fiforet_icecream}:
|
||||
default_code: FIFO
|
||||
default_code: FIFORET
|
||||
name: FIFO Ice Cream
|
||||
type: product
|
||||
standard_price: 0.0
|
||||
|
||||
@@ -13,7 +13,7 @@ Please note that:
|
||||
Orders will only be merged if:
|
||||
* Purchase Orders are in draft
|
||||
* Purchase Orders belong to the same supplier
|
||||
* Purchase Orders are have same stock location, same pricelist
|
||||
* Purchase Orders are have same stock location, same pricelist, same currency
|
||||
Lines will only be merged if:
|
||||
* Order lines are exactly the same except for the product,quantity and unit
|
||||
" colspan="4"/>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<field name="name">rating.rating.graph</field>
|
||||
<field name="model">rating.rating</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Rating Average" type="pivot">
|
||||
<graph string="Rating Average" type="bar">
|
||||
<field name="res_name" type="row"/>
|
||||
<field name="rating" type="measure"/>
|
||||
</graph>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="style">
|
||||
<template id="style" name="Report Style">
|
||||
body {
|
||||
color: #000 !important;
|
||||
word-wrap: break-word;
|
||||
|
||||
@@ -45,6 +45,7 @@ class sale_report(osv.osv):
|
||||
'nbr': fields.integer('# of Lines', readonly=True), # TDE FIXME master: rename into nbr_lines
|
||||
'state': fields.selection([
|
||||
('draft', 'Quotation'),
|
||||
('sent', 'Quotation Sent'),
|
||||
('waiting_date', 'Waiting Schedule'),
|
||||
('manual', 'Manual In Progress'),
|
||||
('progress', 'In Progress'),
|
||||
|
||||
@@ -29,7 +29,7 @@ class res_partner(osv.osv):
|
||||
# The current user may not have access rights for sale orders
|
||||
try:
|
||||
for partner in self.browse(cr, uid, ids, context):
|
||||
res[partner.id] = len(partner.sale_order_ids)
|
||||
res[partner.id] = len(partner.sale_order_ids) + len(partner.mapped('child_ids.sale_order_ids'))
|
||||
except:
|
||||
pass
|
||||
return res
|
||||
|
||||
@@ -300,7 +300,8 @@ class sale_order(osv.osv):
|
||||
part = self.pool.get('res.partner').browse(cr, uid, part, context=context)
|
||||
addr = self.pool.get('res.partner').address_get(cr, uid, [part.id], ['delivery', 'invoice', 'contact'])
|
||||
pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
|
||||
payment_term = part.property_payment_term and part.property_payment_term.id or False
|
||||
invoice_part = self.pool.get('res.partner').browse(cr, uid, addr['invoice'], context=context)
|
||||
payment_term = invoice_part.property_payment_term and invoice_part.property_payment_term.id or False
|
||||
dedicated_salesman = part.user_id and part.user_id.id or uid
|
||||
val = {
|
||||
'partner_invoice_id': addr['invoice'],
|
||||
@@ -363,14 +364,14 @@ class sale_order(osv.osv):
|
||||
'origin': order.name,
|
||||
'type': 'out_invoice',
|
||||
'reference': order.client_order_ref or order.name,
|
||||
'account_id': order.partner_id.property_account_receivable.id,
|
||||
'account_id': order.partner_invoice_id.property_account_receivable.id,
|
||||
'partner_id': order.partner_invoice_id.id,
|
||||
'journal_id': journal_ids[0],
|
||||
'invoice_line': [(6, 0, lines)],
|
||||
'currency_id': order.pricelist_id.currency_id.id,
|
||||
'comment': order.note,
|
||||
'payment_term': order.payment_term and order.payment_term.id or False,
|
||||
'fiscal_position': order.fiscal_position.id or order.partner_id.property_account_position.id,
|
||||
'fiscal_position': order.fiscal_position.id or order.partner_invoice_id.property_account_position.id,
|
||||
'date_invoice': context.get('date_invoice', False),
|
||||
'company_id': order.company_id.id,
|
||||
'user_id': order.user_id and order.user_id.id or False,
|
||||
@@ -749,7 +750,7 @@ class sale_order(osv.osv):
|
||||
order_line.append([4, line_id])
|
||||
else:
|
||||
order_line.append(line)
|
||||
return {'value': {'order_line': order_line}}
|
||||
return {'value': {'order_line': order_line, 'amount_untaxed': False, 'amount_tax': False, 'amount_total': False}}
|
||||
|
||||
def test_procurements_done(self, cr, uid, ids, context=None):
|
||||
for sale in self.browse(cr, uid, ids, context=context):
|
||||
|
||||
@@ -40,7 +40,7 @@ def grouplines(self, ordered_lines, sortkey):
|
||||
|
||||
class SaleLayoutCategory(osv.Model):
|
||||
_name = 'sale_layout.category'
|
||||
_order = 'sequence'
|
||||
_order = 'sequence, id'
|
||||
_columns = {
|
||||
'name': fields.char('Name', required=True),
|
||||
'sequence': fields.integer('Sequence', required=True),
|
||||
@@ -121,7 +121,7 @@ class SaleOrderLine(osv.Model):
|
||||
'categ_sequence': 0
|
||||
}
|
||||
|
||||
_order = 'order_id, categ_sequence, sequence, id'
|
||||
_order = 'order_id, categ_sequence, sale_layout_cat_id, sequence, id'
|
||||
|
||||
def _prepare_order_line_invoice_line(self, cr, uid, line, account_id=False, context=None):
|
||||
"""Save the layout when converting to an invoice line."""
|
||||
|
||||
@@ -30,6 +30,7 @@ class sale_report(osv.osv):
|
||||
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse',readonly=True),
|
||||
'state': fields.selection([
|
||||
('draft', 'Quotation'),
|
||||
('sent', 'Quotation Sent'),
|
||||
('waiting_date', 'Waiting Schedule'),
|
||||
('manual', 'Manual In Progress'),
|
||||
('progress', 'In Progress'),
|
||||
|
||||
@@ -414,6 +414,14 @@ class stock_move(osv.osv):
|
||||
res['price_unit'] = res['price_unit'] / uos_coeff
|
||||
return res
|
||||
|
||||
def _get_moves_taxes(self, cr, uid, moves, context=None):
|
||||
is_extra_move, extra_move_tax = super(stock_move, self)._get_moves_taxes(cr, uid, moves, context=context)
|
||||
for move in moves:
|
||||
if move.procurement_id and move.procurement_id.sale_line_id:
|
||||
is_extra_move[move.id] = False
|
||||
extra_move_tax[move.picking_id, move.product_id] = [(6, 0, [x.id for x in move.procurement_id.sale_line_id.tax_id])]
|
||||
return (is_extra_move, extra_move_tax)
|
||||
|
||||
|
||||
class stock_location_route(osv.osv):
|
||||
_inherit = "stock.location.route"
|
||||
|
||||
@@ -88,6 +88,7 @@ Dashboard / Reports for Warehouse Management will include:
|
||||
'views/report_lot_barcode.xml',
|
||||
'views/report_location_barcode.xml',
|
||||
'views/report_stockpicking.xml',
|
||||
'views/report_stockpicking_operations.xml',
|
||||
'views/report_stockinventory.xml',
|
||||
'views/stock.xml',
|
||||
'report/report_stock_forecast.xml',
|
||||
|
||||
@@ -138,20 +138,21 @@ class product_product(osv.osv):
|
||||
domain_move_in += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_products
|
||||
domain_move_out += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_products
|
||||
domain_quant += domain_products
|
||||
if context.get('lot_id') or context.get('owner_id') or context.get('package_id'):
|
||||
if context.get('lot_id'):
|
||||
domain_quant.append(('lot_id', '=', context['lot_id']))
|
||||
if context.get('owner_id'):
|
||||
domain_quant.append(('owner_id', '=', context['owner_id']))
|
||||
if context.get('package_id'):
|
||||
domain_quant.append(('package_id', '=', context['package_id']))
|
||||
moves_in = []
|
||||
moves_out = []
|
||||
else:
|
||||
domain_move_in += domain_move_in_loc
|
||||
domain_move_out += domain_move_out_loc
|
||||
moves_in = self.pool.get('stock.move').read_group(cr, uid, domain_move_in, ['product_id', 'product_qty'], ['product_id'], context=context)
|
||||
moves_out = self.pool.get('stock.move').read_group(cr, uid, domain_move_out, ['product_id', 'product_qty'], ['product_id'], context=context)
|
||||
|
||||
if context.get('lot_id'):
|
||||
domain_quant.append(('lot_id', '=', context['lot_id']))
|
||||
if context.get('owner_id'):
|
||||
domain_quant.append(('owner_id', '=', context['owner_id']))
|
||||
owner_domain = ('restrict_partner_id', '=', context['owner_id'])
|
||||
domain_move_in.append(owner_domain)
|
||||
domain_move_out.append(owner_domain)
|
||||
if context.get('package_id'):
|
||||
domain_quant.append(('package_id', '=', context['package_id']))
|
||||
|
||||
domain_move_in += domain_move_in_loc
|
||||
domain_move_out += domain_move_out_loc
|
||||
moves_in = self.pool.get('stock.move').read_group(cr, uid, domain_move_in, ['product_id', 'product_qty'], ['product_id'], context=context)
|
||||
moves_out = self.pool.get('stock.move').read_group(cr, uid, domain_move_out, ['product_id', 'product_qty'], ['product_id'], context=context)
|
||||
|
||||
domain_quant += domain_quant_loc
|
||||
quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context)
|
||||
|
||||