--- /dev/null
+# Config file .coveragerc
+# adapt the include for your project
+
+[report]
+include =
+ */OCA/OCB/addons/*
+ */OCA/OCB/openerp/*
+
+omit =
+ */tests/*
+ *__init__.py
+
+# Regexes for lines to exclude from consideration
+exclude_lines =
+ # Have to re-enable the standard pragma
+ pragma: no cover
+
+ # Don't complain about null context checking
+ if context is None:
--- /dev/null
+language: python
+
+python:
+ - "2.7"
+
+env:
+ - DATABASE="openerp_test"
+
+virtualenv:
+ system_site_packages: true
+
+install:
+ - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
+ - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
+ - sudo apt-get install python-lxml
+ - pip install QUnitSuite flake8 coveralls
+ - pip install -r ${HOME}/maintainer-quality-tools/travis/requirements.txt
+
+script:
+# - travis_run_flake8
+ - createdb ${DATABASE}
+ - ./openerp-server -d ${DATABASE} --addons-path=./openerp/addons,./addons --stop-after-init --init=all
+ - coverage run ./openerp-server -d ${DATABASE} --addons-path=./openerp/addons,./addons --stop-after-init --init=all --test-enable --log-level=test | tee stdout.log
+ - if grep -v mail stdout.log | egrep -q "(At least one test failed when loading the modules.|ERROR ${DATABASE})"; then exit 1; fi
+
+after_success:
+ coveralls
--- /dev/null
+[![Build Status](https://travis-ci.org/OCA/OCB.png?branch=7.0)](https://travis-ci.org/OCA/OCB)
+[![Coverage Status](https://coveralls.io/repos/OCA/OCB/badge.png?branch=7.0)](https://coveralls.io/r/OCA/OCB?branch=master)
+
+[Original readme](README)
'entry_posted': fields.boolean('Skip \'Draft\' State for Manual Entries', help='Check this box if you don\'t want new journal entries to pass through the \'draft\' state and instead goes directly to the \'posted state\' without any manual validation. \nNote that journal entries that are automatically created by the system are always skipping that state.'),
'company_id': fields.many2one('res.company', 'Company', required=True, select=1, help="Company related to this journal"),
'allow_date':fields.boolean('Check Date in Period', help= 'If set to True then do not accept the entry if the entry date is not into the period dates'),
-
+ 'active': fields.boolean('Active', select=True),
'profit_account_id' : fields.many2one('account.account', 'Profit Account'),
'loss_account_id' : fields.many2one('account.account', 'Loss Account'),
'internal_account_id' : fields.many2one('account.account', 'Internal Transfers Account', select=1),
'with_last_closing_balance' : False,
'user_id': lambda self, cr, uid, context: uid,
'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
+ 'active': True,
}
_sql_constraints = [
('code_company_uniq', 'unique (code, company_id)', 'The code of the journal must be unique per company !'),
_description = "Account Entry"
_order = 'id desc'
+ def account_assert_balanced(self, cr, uid, context=None):
+ cr.execute("""\
+ SELECT move_id
+ FROM account_move_line
+ WHERE state = 'valid'
+ GROUP BY move_id
+ HAVING abs(sum(debit) - sum(credit)) > 0.00001
+ """)
+ assert len(cr.fetchall()) == 0, \
+ "For all Journal Items, the state is valid implies that the sum " \
+ "of credits equals the sum of debits"
+ return True
+
def account_move_prepare(self, cr, uid, journal_id, date=False, ref='', company_id=False, context=None):
'''
Prepares and returns a dictionary of values, ready to be passed to create() based on the parameters received.
'child_ids': fields.one2many('account.tax.code.template', 'parent_id', 'Child Codes'),
'sign': fields.float('Sign For Parent', required=True),
'notprintable':fields.boolean("Not Printable in Invoice", help="Check this box if you don't want any tax related to this tax Code to appear on invoices."),
+ 'sequence': fields.integer(
+ 'Sequence', help=(
+ "Determine the display order in the report 'Accounting "
+ "\ Reporting \ Generic Reporting \ Taxes \ Taxes Report'"),
+ ),
}
_defaults = {
'parent_id': tax_code_template.parent_id and ((tax_code_template.parent_id.id in tax_code_template_ref) and tax_code_template_ref[tax_code_template.parent_id.id]) or False,
'company_id': company_id,
'sign': tax_code_template.sign,
+ 'sequence': tax_code_template.sequence,
}
#check if this tax code already exists
rec_list = obj_tax_code.search(cr, uid, [('name', '=', vals['name']),('code', '=', vals['code']),('company_id', '=', vals['company_id'])], context=context)
_columns = {
'product_uom_id': fields.many2one('product.uom', 'Unit of Measure'),
'product_id': fields.many2one('product.product', 'Product'),
- 'general_account_id': fields.many2one('account.account', 'General Account', required=True, ondelete='restrict'),
+ 'general_account_id': fields.many2one('account.account', 'General Account', ondelete='restrict'),
'move_id': fields.many2one('account.move.line', 'Move Line', ondelete='cascade', select=True),
'journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal', required=True, ondelete='restrict', select=True),
'code': fields.char('Code', size=8),
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
- <assert model="account.move" search="[]" string="For all Journal Items, the state is valid implies that the sum of credits equals the sum of debits">
- <test expr="not len(line_id) or line_id[0].state != 'valid' or (sum([l.debit - l.credit for l in line_id]) <= 0.00001)"/>
- </assert>
+ <function name="account_assert_balanced" model="account.move"/>
</data>
</openerp>
acc_id = p.property_account_payable.id
partner_payment_term = p.property_supplier_payment_term and p.property_supplier_payment_term.id or False
fiscal_position = p.property_account_position and p.property_account_position.id or False
- if p.bank_ids:
- bank_id = p.bank_ids[0].id
+ if p.commercial_partner_id.bank_ids:
+ bank_id = p.commercial_partner_id.bank_ids[0].id
result = {'value': {
'account_id': acc_id,
line = self.finalize_invoice_move_lines(cr, uid, inv, line)
move = {
- 'ref': inv.reference and inv.reference or inv.name,
+ 'ref': inv.reference or inv.supplier_invoice_number or inv.name,
'line_id': line,
'journal_id': journal_id,
'date': inv.date_invoice,
(ref, move_id))
return True
+ def action_proforma(self, cr, uid, ids, context=None):
+ """
+ Check if all taxes are present with the correct base amount
+ on creating a proforma invoice. This leaves room for manual
+ corrections of the tax amount.
+ """
+ if not ids:
+ return True
+ if isinstance(ids, (int, long)):
+ ids = [ids]
+ ait_obj = self.pool.get('account.invoice.tax')
+ for inv in self.browse(cr, uid, ids, context=context):
+ compute_taxes = ait_obj.compute(cr, uid, inv.id, context=context)
+ self.check_tax_lines(cr, uid, inv, compute_taxes, ait_obj)
+ return self.write(
+ cr, uid, ids, {'state': 'proforma2'}, context=context)
+
def action_cancel(self, cr, uid, ids, context=None):
if context is None:
context = {}
'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True),
'name': fields.char('Tax Description', size=64, required=True),
'account_id': fields.many2one('account.account', 'Tax Account', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]),
- 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
+ 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account', readonly=True),
'base': fields.float('Base', digits_compute=dp.get_precision('Account')),
'amount': fields.float('Amount', digits_compute=dp.get_precision('Account')),
'manual': fields.boolean('Manual'),
<field name="amount_untaxed" widget="monetary" options="{'currency_field': 'currency_id'}"/>
<div>
<label for="amount_tax"/>
- <button name="button_reset_taxes" states="draft,proforma2"
+ <button name="button_reset_taxes" states="draft"
string="(update)" class="oe_link oe_edit_only"
type="object" help="Recompute taxes and total"/>
</div>
<field name="sent" invisible="1"/>
<notebook colspan="4">
<page string="Invoice Lines">
- <field name="invoice_line" nolabel="1" widget="one2many_list" context="{'type': type}">
+ <field name="invoice_line" nolabel="1" widget="one2many_list" context="{'type': type, 'company_id': company_id}">
<tree string="Invoice Lines" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="product_id"
<field name="amount_untaxed" widget="monetary" options="{'currency_field': 'currency_id'}"/>
<div>
<label for="amount_tax"/>
- <button name="button_reset_taxes" states="draft,proforma2"
+ <button name="button_reset_taxes" states="draft"
string="(update)" class="oe_link oe_edit_only"
type="object" help="Recompute taxes and total"/>
</div>
<record id="act_proforma2" model="workflow.activity">
<field name="wkf_id" ref="wkf"/>
<field name="name">proforma2</field>
- <field name="action">write({'state':'proforma2'})</field>
+ <field name="action">action_proforma()</field>
<field name="kind">function</field>
</record>
}
_order = "date desc, id desc"
_sql_constraints = [
- ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in accounting entry !'),
- ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in accounting entry !'),
+ ('credit_debit1', 'CHECK ((credit * debit) = 0::numeric)',
+ 'Wrong credit or debit value in accounting entry !'),
+ ('credit_debit2', 'CHECK ((credit + debit) >= 0::numeric)',
+ 'Wrong credit or debit value in accounting entry !'),
]
def _auto_init(self, cr, context=None):
<field name="model">account.journal</field>
<field name="arch" type="xml">
<form string="Account Journal" version="7.0">
+ <sheet>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1>
<field name="default_credit_account_id" attrs="{'required':[('type','in',('cash', 'bank'))]}" domain="[('type','<>','view'),('type','<>','consolidation')]"/>
<field name="currency" groups="base.group_multi_currency"/>
<field name="company_id" groups="base.group_multi_company"/>
+ <field name="active"/>
</group>
</group>
<notebook>
</field>
</page>
</notebook>
+ </sheet>
</form>
</field>
</record>
<h1><field name="name"/></h1>
<group>
<group>
+ <!-- last_closing_balance must be here because it is returned by onchange_journal_id -->
+ <field name="last_closing_balance" invisible="1"/>
<field name="journal_id" domain="[('type', '=', 'bank')]" on_change="onchange_journal_id(journal_id)" widget="selection"/>
<label for="date" string="Date / Period"/>
<div>
Fiscal Periods
-->
+ <record id="period_0" model="account.period">
+ <field eval="'00/'+time.strftime('%Y')" name="code"/>
+ <field eval="'X 00/'+time.strftime('%Y')" name="name"/>
+ <field eval="True" name="special"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear"/>
+ <field eval="time.strftime('%Y')+'-01-01'" name="date_start"/>
+ <field eval="time.strftime('%Y')+'-01-01'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
<record id="period_1" model="account.period">
<field eval="'01/'+time.strftime('%Y')" name="code"/>
<field eval="'X 01/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-01-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-01-31'" name="date_stop"/>
<record id="period_2" model="account.period">
<field eval="'02/'+time.strftime('%Y')" name="code"/>
<field eval="'X 02/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-02-01'" name="date_start"/>
<!-- for the last day of February, we have to compute the day before March 1st -->
<record id="period_3" model="account.period">
<field eval="'03/'+time.strftime('%Y')" name="code"/>
<field eval="'X 03/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-03-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-03-31'" name="date_stop"/>
<record id="period_4" model="account.period">
<field eval="'04/'+time.strftime('%Y')" name="code"/>
<field eval="'X 04/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-04-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-04-30'" name="date_stop"/>
<record id="period_5" model="account.period">
<field eval="'05/'+time.strftime('%Y')" name="code"/>
<field eval="'X 05/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-05-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-05-31'" name="date_stop"/>
<field eval="'06/'+time.strftime('%Y')" name="code"/>
<field eval="'X 06/'+time.strftime('%Y')" name="name"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field eval="time.strftime('%Y')+'-06-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-06-30'" name="date_stop"/>
<field name="company_id" ref="base.main_company"/>
<record id="period_7" model="account.period">
<field eval="'07/'+time.strftime('%Y')" name="code"/>
<field eval="'X 07/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-07-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-07-31'" name="date_stop"/>
<record id="period_8" model="account.period">
<field eval="'08/'+time.strftime('%Y')" name="code"/>
<field eval="'X 08/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-08-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-08-31'" name="date_stop"/>
<record id="period_9" model="account.period">
<field eval="'09/'+time.strftime('%Y')" name="code"/>
<field eval="'X 09/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-09-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-09-30'" name="date_stop"/>
<record id="period_10" model="account.period">
<field eval="'10/'+time.strftime('%Y')" name="code"/>
<field eval="'X 10/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-10-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-10-31'" name="date_stop"/>
<record id="period_11" model="account.period">
<field eval="'11/'+time.strftime('%Y')" name="code"/>
<field eval="'X 11/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-11-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-11-30'" name="date_stop"/>
<record id="period_12" model="account.period">
<field eval="'12/'+time.strftime('%Y')" name="code"/>
<field eval="'X 12/'+time.strftime('%Y')" name="name"/>
- <field eval="True" name="special"/>
+ <field eval="False" name="special"/>
<field name="fiscalyear_id" ref="data_fiscalyear"/>
<field eval="time.strftime('%Y')+'-12-01'" name="date_start"/>
<field eval="time.strftime('%Y')+'-12-31'" name="date_stop"/>
self.ACCOUNT_TYPE = ['payable']
else:
self.ACCOUNT_TYPE = ['payable','receivable']
+ self.partner_ids = ids if data.get('model') == 'res.partner' else False
return super(aged_trial_report, self).set_context(objects, data, ids, report_type=report_type)
def _get_lines(self, form):
move_state = ['draft','posted']
if self.target_move == 'posted':
move_state = ['posted']
+ query_params = [
+ tuple(move_state), tuple(self.ACCOUNT_TYPE),
+ self.date_from, self.date_from]
+ if self.partner_ids:
+ partner_query = ' AND res_partner.id IN %s '
+ query_params.append(tuple(self.partner_ids))
+ else:
+ partner_query = ''
+
self.cr.execute('SELECT DISTINCT res_partner.id AS id,\
res_partner.name AS name \
FROM res_partner,account_move_line AS l, account_account, account_move am\
AND (account_account.type IN %s)\
AND account_account.active\
AND ((reconcile_id IS NULL)\
- OR (reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date > %s )))\
+ OR (reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date >= %s::timestamp + \'1day\'::interval )))\
AND (l.partner_id=res_partner.id)\
AND (l.date <= %s)\
- AND ' + self.query + ' \
- ORDER BY res_partner.name', (tuple(move_state), tuple(self.ACCOUNT_TYPE), self.date_from, self.date_from,))
+ AND ' + self.query + partner_query + ' \
+ ORDER BY res_partner.name', tuple(query_params))
partners = self.cr.dictfetchall()
## mise a 0 du total
for i in range(7):
AND (account_account.type IN %s)\
AND (l.partner_id IN %s)\
AND ((l.reconcile_id IS NULL)\
- OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date > %s )))\
+ OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date >= %s::timestamp + \'1day\'::interval )))\
AND ' + self.query + '\
AND account_account.active\
AND (l.date <= %s)\
AND (COALESCE(l.date_maturity, l.date) < %s)\
AND (l.partner_id IN %s)\
AND ((l.reconcile_id IS NULL)\
- OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date > %s )))\
+ OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date >= %s::timestamp + \'1day\'::interval )))\
AND '+ self.query + '\
AND account_account.active\
AND (l.date <= %s)\
AND (COALESCE(l.date_maturity,l.date) > %s)\
AND (l.partner_id IN %s)\
AND ((l.reconcile_id IS NULL)\
- OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date > %s )))\
+ OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date >= %s::timestamp + \'1day\'::interval )))\
AND '+ self.query + '\
AND account_account.active\
AND (l.date <= %s)\
AND (account_account.type IN %s)
AND (l.partner_id IN %s)
AND ((l.reconcile_id IS NULL)
- OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date > %s )))
+ OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date >= %s::timestamp + \'1day\'::interval )))
AND ''' + self.query + '''
AND account_account.active
AND ''' + dates_query + '''
return res
def _get_lines_with_out_partner(self, form):
+ if self.partner_ids:
+ return []
res = []
move_state = ['draft','posted']
if self.target_move == 'posted':
AND (l.partner_id IS NULL)\
AND (account_account.type IN %s)\
AND ((l.reconcile_id IS NULL) \
- OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date > %s )))\
+ OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date >= %s::timestamp + \'1day\'::interval )))\
AND ' + self.query + '\
AND (l.date <= %s)\
AND account_account.active ',(tuple(move_state), tuple(self.ACCOUNT_TYPE), self.date_from, self.date_from,))
AND (account_account.type IN %s)\
AND (COALESCE(l.date_maturity, l.date) < %s)\
AND ((l.reconcile_id IS NULL)\
- OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date > %s )))\
+ OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date >= %s::timestamp + \'1day\'::interval )))\
AND '+ self.query + '\
AND account_account.active ', (tuple(move_state), tuple(self.ACCOUNT_TYPE), self.date_from, self.date_from))
t = self.cr.fetchall()
AND (account_account.type IN %s)\
AND (COALESCE(l.date_maturity,l.date) > %s)\
AND ((l.reconcile_id IS NULL)\
- OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date > %s )))\
+ OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date >= %s::timestamp + \'1day\'::interval )))\
AND '+ self.query + '\
AND account_account.active ', (tuple(move_state), tuple(self.ACCOUNT_TYPE), self.date_from, self.date_from))
t = self.cr.fetchall()
AND (account_account.type IN %s)\
AND (l.partner_id IS NULL)\
AND ((l.reconcile_id IS NULL)\
- OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date > %s )))\
+ OR (l.reconcile_id IN (SELECT recon.id FROM account_move_reconcile AS recon WHERE recon.create_date >= %s::timestamp + \'1day\'::interval )))\
AND '+ self.query + '\
AND account_account.active\
AND ' + dates_query + '\
'get_start_period': self.get_start_period,
'get_end_period': self.get_end_period,
'get_basedon': self._get_basedon,
+ 'get_target_move': self._get_target_move,
})
def _get_basedon(self, form):
return form['form']['based_on']
- def _get_lines(self, based_on, company_id=False, parent=False, level=0, context=None):
+ def _get_lines(self, based_on, target_move, company_id=False, parent=False, level=0, context=None):
period_list = self.period_ids
res = self._get_codes(based_on, company_id, parent, level, period_list, context=context)
if period_list:
- res = self._add_codes(based_on, res, period_list, context=context)
+ res = self._add_codes(based_on, target_move, res, period_list, context=context)
else:
self.cr.execute ("select id from account_fiscalyear")
fy = self.cr.fetchall()
periods = self.cr.fetchall()
for p in periods:
period_list.append(p[0])
- res = self._add_codes(based_on, res, period_list, context=context)
+ res = self._add_codes(based_on, target_move, res, period_list, context=context)
i = 0
top_result = []
res += self._get_codes(based_on, company_id, code.id, level+1, context=context)
return res
- def _add_codes(self, based_on, account_list=None, period_list=None, context=None):
+ def _add_codes(self, based_on, target_move, account_list=None, period_list=None, context=None):
if account_list is None:
account_list = []
if period_list is None:
period_list = []
+ if context is None:
+ context = {}
res = []
obj_tc = self.pool.get('account.tax.code')
for account in account_list:
ids = obj_tc.search(self.cr, self.uid, [('id','=', account[1].id)], context=context)
sum_tax_add = 0
- for period_ind in period_list:
- for code in obj_tc.browse(self.cr, self.uid, ids, {'period_id':period_ind,'based_on': based_on}):
+ for period_id in period_list:
+ ctx = context.copy()
+ ctx.update(
+ period_id=period_id,
+ based_on=based_on,
+ state=target_move,
+ )
+ for code in obj_tc.browse(self.cr, self.uid, ids, context=ctx):
sum_tax_add = sum_tax_add + code.sum_period
code.sum_period = sum_tax_add
</tr>
</blockTable>
</td>
- <td><para style="terp_default_Centre_8">[[ get_basedon(data) or '' ]]</para></td>
+ <td>
+ <para style="terp_default_Centre_8">[[ get_basedon(data) or '' ]]</para>
+ <para style="terp_default_Centre_8">[[ get_target_move(data) or '' ]]</para>
+ </td>
</tr>
</blockTable>
<para style="P2"><font color="white"> </font></para>
<td><para style="P12a">Tax Amount</para></td>
</tr>
<tr>
- <td><para style="P5"><font>[[ repeatIn(get_lines(data['form']['based_on'], data['form']['company_id']), 'o') ]]</font><font color="white">[[ (o['level']) ]]</font> <font>[[ (len(o['level'])<5 and setTag('para','para',{'fontName':'Helvetica-Bold'})) or removeParentNode('font') ]]</font><font>[[ o['code'] ]] [[ o['name'] ]] </font></para></td>
+ <td><para style="P5"><font>[[ repeatIn(get_lines(data['form']['based_on'], data['form']['target_move'],data['form']['company_id']), 'o') ]]</font><font color="white">[[ (o['level']) ]]</font> <font>[[ (len(o['level'])<5 and setTag('para','para',{'fontName':'Helvetica-Bold'})) or removeParentNode('font') ]]</font><font>[[ o['code'] ]] [[ o['name'] ]] </font></para></td>
<td><para style="P6"><font>[[ len(o['level'])<5 and setTag('para','para',{'fontName':"Helvetica-Bold"}) or removeParentNode('font')]]</font><font>[[ o['type']=='view' and removeParentNode('font') ]][[ formatLang(o['debit']) ]]</font><font>[[ o['type']<>'view' and removeParentNode('font') ]][[ formatLang(o['debit']) ]]</font></para></td>
<td><para style="P6"><font>[[ len(o['level'])<5 and setTag('para','para',{'fontName':"Helvetica-Bold"}) or removeParentNode('font')]]</font><font>[[ o['type']=='view' and removeParentNode('font') ]][[ formatLang(o['credit']) ]]</font><font>[[ o['type']<>'view' and removeParentNode('font') ]][[ formatLang(o['credit'])]]</font></para></td>
<td><para style="P6"><font>[[ len(o['level'])<5 and setTag('para','para',{'fontName':"Helvetica-Bold"}) or removeParentNode('font')]]</font><font>[[ o['type']=='view' and removeParentNode('font') ]][[ formatLang(o['tax_amount'], currency_obj=company.currency_id) ]]</font><font>[[ o['type']<>'view' and removeParentNode('font') ]][[ formatLang(o['tax_amount'], currency_obj=company.currency_id) ]]</font> </para></td>
context = {}
data = self.pre_print_report(cr, uid, ids, data, context=context)
+ data['ids'] = context.get('active_ids', [])
+ data['model'] = context.get('active_model', 'ir.ui.menu')
data['form'].update(self.read(cr, uid, ids, ['period_length', 'direction_selection'])[0])
period_length = data['form']['period_length']
}
start = stop + relativedelta(days=1)
data['form'].update(res)
- if data.get('form',False):
- data['ids']=[data['form'].get('chart_account_id',False)]
return {
'type': 'ir.actions.report.xml',
'report_name': 'account.aged_trial_balance',
<field name="target">new</field>
</record>
+ <record model="ir.values" id="ir_values_account_aged_partner_balance">
+ <field name="key2" eval="'client_print_multi'"/>
+ <field name="model" eval="'res.partner'"/>
+ <field name="name">Print Aged Partner Balance</field>
+ <field name="value" eval="'ir.actions.act_window,%d'%action_account_aged_balance_view"/>
+ </record>
+
<menuitem icon="STOCK_PRINT"
name="Aged Partner Balance"
action="action_account_aged_balance_view"
return fiscalyears and fiscalyears[0] or False
def _get_all_journal(self, cr, uid, context=None):
- return self.pool.get('account.journal').search(cr, uid ,[])
+ # The 'active_test' context is used, because reports must
+ # return entries created on inactive journals.
+ return self.pool['account.journal'].search(
+ cr, uid, [], context=dict(context or {}, active_test=False)
+ )
_defaults = {
'fiscalyear_id': _get_fiscalyear,
data = {}
data['ids'] = context.get('active_ids', [])
data['model'] = context.get('active_model', 'ir.ui.menu')
- data['form'] = self.read(cr, uid, ids, ['date_from', 'date_to', 'fiscalyear_id', 'journal_ids', 'period_from', 'period_to', 'filter', 'chart_account_id', 'target_move'], context=context)[0]
+ data['form'] = self.read(
+ cr, uid, ids,
+ [
+ 'date_from', 'date_to', 'fiscalyear_id', 'period_from',
+ 'period_to', 'filter', 'chart_account_id', 'target_move'
+ ], context=context)[0]
+ # The 'active_test' context is used, because reports must
+ # return entries created on inactive journals.
+ data['form']['journal_ids'] = self.read(
+ cr, uid, ids, ['journal_ids'], context=dict(context or {}, active_test=False)
+ )[0]['journal_ids']
for field in ['fiscalyear_id', 'chart_account_id', 'period_from', 'period_to']:
if isinstance(data['form'][field], tuple):
data['form'][field] = data['form'][field][0]
<field name="chart_tax_id" widget='selection'/>
<field name="fiscalyear_id"/>
<field name="display_detail"/>
+ <field name="target_move"/>
<!--- <field name="based_on"/>--> <!-- the option based_on 'payment' is probably not fully compliant with what the users understand with that term. So, currently, it's seems better to remove it from the view to avoid further problems -->
</group>
<group>
<field name="pricelist_id"
class="oe_inline"
- attrs="{'required': [('invoice_on_timesheets', '=', True)]}"/>
+ attrs="{'required': ['|',('type','=','template'),('type','=','contract'), ('invoice_on_timesheets', '=', True)]}"/>
<field name="to_invoice"
class="oe_inline"
widget="selection"
- attrs="{'required': [('invoice_on_timesheets', '=', True)]}"/>
+ attrs="{'required': ['|',('type','=','template'),('type','=','contract'), ('invoice_on_timesheets', '=', True)]}"/>
</group>
</group>
</xpath>
'amount': amount,
'asset_id': asset.id,
'sequence': i,
- 'name': str(asset.id) +'/' + str(i),
+ 'name': "%s/%s" %(i, undone_dotation_number),
'remaining_value': residual_amount,
'depreciated_value': (asset.purchase_value - asset.salvage_value) - (residual_amount + amount),
'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
asset_name = line.asset_id.name
reference = line.name
move_vals = {
- 'name': asset_name,
'date': depreciation_date,
- 'ref': reference,
+ 'ref': "%s %s" %(line.asset_id.code or line.asset_id.name, line.name),
'period_id': period_ids and period_ids[0] or False,
'journal_id': line.asset_id.category_id.journal_id.id,
}
result = 0.0
if context is None:
context = {}
+ account_obj = self.pool.get('account.account')
for line in self.browse(cr, uid, ids, context=context):
acc_ids = [x.id for x in line.general_budget_id.account_ids]
if not acc_ids:
raise osv.except_osv(_('Error!'),_("The Budget '%s' has no accounts!") % ustr(line.general_budget_id.name))
+ acc_ids = account_obj._get_children_and_consol(cr, uid, acc_ids, context=context)
date_to = line.date_to
date_from = line.date_from
if line.analytic_account_id.id:
- cr.execute("SELECT SUM(amount) FROM account_analytic_line WHERE account_id=%s AND (date "
+ cr.execute("SELECT SUM(amount) FROM account_analytic_line WHERE account_id in "
+ """(with recursive account_analytic_account_hierarchy(id)
+ as
+ (
+ select id from account_analytic_account
+ where id=%s
+ union all
+ select account_analytic_account.id from
+ account_analytic_account
+ join account_analytic_account_hierarchy
+ on account_analytic_account.parent_id=
+ account_analytic_account_hierarchy.id
+ )"""
+ "select id from account_analytic_account_hierarchy) "
+ "AND (date "
"between to_date(%s,'yyyy-mm-dd') AND to_date(%s,'yyyy-mm-dd')) AND "
"general_account_id=ANY(%s)", (line.analytic_account_id.id, date_from, date_to,acc_ids,))
result = cr.fetchone()[0]
</div>
<group>
<group>
+ <field name="total"/>
<field name="user_id"/>
<field name="mode"/>
</group>
<field name="move_line_id" on_change="onchange_move_line(move_line_id,parent.mode,parent.date_prefered,parent.date_scheduled,currency,company_currency)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_to_pay','>',0)] "/>
<separator colspan="4" string="Transaction Information"/>
<field name="date"/>
- <label for="amount_currency" groups="base.group_multi_currency"/>
- <div groups="base.group_multi_currency">
+ <label for="amount_currency"/>
+ <div>
<field name="amount_currency" on_change="onchange_amount(amount_currency,currency,company_currency)" class="oe_inline"/>
- <field name="currency" nolabel="1" class="oe_inline"/>
+ <field name="currency" nolabel="1" class="oe_inline" groups="base.group_multi_currency"/>
</div>
<field name="partner_id" on_change="onchange_partner(partner_id,parent.mode)"/>
<field domain="[('partner_id','=',partner_id)]" name="bank_id"/>
<field name="bank_id" domain="[('partner_id', '=', partner_id)]"/>
<field name="ml_maturity_date"/>
<field name="date"/>
- <field name="amount_currency" string="Amount" groups="base.group_multi_currency"/>
+ <field name="amount_currency" string="Amount"/>
<field name="currency" groups="base.group_multi_currency"/>
<field name="name"/>
<field name="amount" sum="Total in Company Currency" invisible="1"/>
# payment = self.pool.get('payment.order').browse(cr, uid, context['active_id'], context=context)
# Search for move line to pay:
- domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)]
+ domain = [('reconcile_id', '=', False),
+ ('account_id.type', '=', 'payable'),
+ ('journal_id.type','<>','bank'),
+ ('amount_to_pay', '>', 0)]
domain = domain + ['|', ('date_maturity', '<=', search_due_date), ('date_maturity', '=', False)]
line_ids = line_obj.search(cr, uid, domain, context=context)
context.update({'line_ids': line_ids})
<field name="type" invisible="context.get('default_type', False)"/>
<field name="template_id" on_change="on_change_template(template_id,context)" domain="[('type','=','template')]" attrs="{'invisible': [('type','in',['view', 'normal','template'])]}" context="{'default_type' : 'template'}"/>
<field name="code"/>
- <field name="parent_id" on_change="on_change_parent(parent_id)" attrs="{'invisible': [('type','in',['contract'])]}"/>
+ <field name="parent_id" on_change="on_change_parent(parent_id)"/>
<field name="company_id" on_change="on_change_company(company_id)" widget="selection" groups="base.group_multi_company" attrs="{'required': [('type','<>','view')]}"/>
</group>
</group>
<attribute name="attrs">{'invisible': [('invoice_on_timesheets','=',False),('charge_expenses','=',False)]}</attribute>
</xpath>
<xpath expr="//field[@name='pricelist_id']" position="attributes">
- <attribute name="attrs">{'required': ['|',('invoice_on_timesheets','=',True),('charge_expenses','=',True)]}</attribute>
+ <attribute name="attrs">{'required': ['|',('type','=','template'),('type','=','contract'), '|', ('invoice_on_timesheets','=',True),('charge_expenses','=',True)]}</attribute>
</xpath>
<xpath expr="//field[@name='to_invoice']" position="attributes">
- <attribute name="attrs">{'required': ['|',('invoice_on_timesheets','=',True),('charge_expenses','=',True)]}</attribute>
+ <attribute name="attrs">{'required': ['|',('type','=','template'),('type','=','contract'), '|', ('invoice_on_timesheets','=',True),('charge_expenses','=',True)]}</attribute>
<attribute name="string">Expenses and Timesheet Invoicing Ratio</attribute>
</xpath>
</field>
<field name="priority" eval="18"/>
<field name="inherit_id" ref="hr_timesheet_sheet.hr_timesheet_sheet_form"/>
<field name="arch" type="xml">
- <xpath expr="//field[@name='timesheet_ids']/tree/field[@name='account_id']" position="replace">
- <field name="account_id" domain="[('type','=','normal'),('state', '<>', 'close')]" on_change="on_change_account_id(account_id, user_id, unit_amount)"/>
+ <xpath expr="//field[@name='timesheet_ids']/tree/field[@name='account_id']" position="attributes">
+ <attribute name="on_change">on_change_account_id(account_id, user_id, unit_amount)</attribute>
</xpath>
</field>
</record>
<field name="priority" eval="19"/>
<field name="inherit_id" ref="hr_timesheet_sheet.hr_timesheet_sheet_form"/>
<field name="arch" type="xml">
- <xpath expr="//field[@name='timesheet_ids']/form/field[@name='account_id']" position="replace">
- <field name="account_id" domain="[('type','=','normal'),('state', '<>', 'close')]" on_change="on_change_account_id(account_id, user_id, unit_amount)"/>
+ <xpath expr="//field[@name='timesheet_ids']/form/field[@name='account_id']" position="attributes">
+ <attribute name="on_change">on_change_account_id(account_id, user_id, unit_amount)</attribute>
</xpath>
</field>
</record>
<field name="model">hr.analytic.timesheet</field>
<field name="inherit_id" ref="hr_timesheet.hr_timesheet_line_form"/>
<field name="arch" type="xml">
- <xpath expr="//field[@name='account_id']" position="replace">
- <field name="account_id" domain="[('type','=','normal'),('state', '<>', 'close')]" on_change="on_change_account_id(account_id, user_id, unit_amount)" />
+ <xpath expr="//field[@name='account_id']" position="attributes">
+ <attribute name="on_change">on_change_account_id(account_id, user_id, unit_amount)</attribute>
</xpath>
</field>
</record>
<field name="model">hr.analytic.timesheet</field>
<field name="inherit_id" ref="hr_timesheet.hr_timesheet_line_tree"/>
<field name="arch" type="xml">
- <xpath expr="/tree/field[@name='account_id']" position="replace">
- <field name="account_id" domain="[('type','=','normal'),('state', '<>', 'close')]" on_change="on_change_account_id(account_id, user_id, unit_amount)" />
+ <xpath expr="/tree/field[@name='account_id']" position="attributes">
+ <attribute name="on_change">on_change_account_id(account_id, user_id, unit_amount)</attribute>
</xpath>
</field>
</record>
return (int(real_id), real_date, end.strftime("%Y-%m-%d %H:%M:%S"))
return int(real_id)
- return base_calendar_id and int(base_calendar_id) or base_calendar_id
+ return int(base_calendar_id)
+
+ return base_calendar_id
+
def get_real_ids(ids):
if isinstance(ids, (str, int, long)):
'email': partner.email
}, context=local_context)
if partner.email:
- mail_to = mail_to + " " + partner.email
+ mail_to = (
+ mail_to + ";" + partner.email
+ if mail_to
+ else partner.email
+ )
self.write(cr, uid, [event.id], {
'attendee_ids': [(4, att_id)]
}, context=context)
name="Phone calls"
groups="base.group_sale_salesman"
res_model="crm.phonecall"
+ src_model="crm.lead"
view_mode="tree,calendar,form"
context="{'default_duration': 1.0 ,'default_opportunity_id': active_id}"
+ domain="[('opportunity_id', '=', active_id)]"
view_type="form"/>
<act_window
id="act_crm_opportunity_crm_meeting_new"
name="Meetings"
res_model="crm.meeting"
+ src_model="crm.lead"
view_mode="tree,form,calendar"
context="{'default_duration': 4.0, 'default_opportunity_id': active_id}"
+ domain="[('opportunity_id', '=', active_id)]"
view_type="form"/>
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.phonecall')]"),
'partner_id': fields.many2one('res.partner', 'Partner' , readonly=True),
+ 'opportunity_id': fields.many2one('crm.lead', 'Lead/Opportunity' , readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
'opening_date': fields.date('Opening Date', readonly=True, select=True),
'creation_date': fields.date('Creation Date', readonly=True, select=True),
c.section_id,
c.categ_id,
c.partner_id,
+ c.opportunity_id,
c.duration,
c.company_id,
c.priority,
<field name="user_id" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="partner_id" invisible="1"/>
+ <field name="opportunity_id" invisible="1"/>
<field name="state" invisible="1"/>
<field name="categ_id" invisible="1"/>
<field name="day" invisible="1"/>
<filter string="Salesperson" name="Salesperson" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}" />
<filter string="Sales Team" icon="terp-personal+" domain="[]" context="{'group_by':'section_id'}" />
<filter string="Partner" icon="terp-partner" context="{'group_by':'partner_id'}" />
+ <filter string="Lead/Opportunity" icon="terp-personal+" context="{'group_by':'opportunity_id'}" />
<filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}" />
<filter string="Category" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'categ_id'}" />
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}" />
'demo': ['delivery_demo.xml'],
'test': ['test/delivery_cost.yml',
'test/delivery_chained_pickings.yml',
+ 'test/delivery_tracking_ref_backorder.yml',
],
'installable': True,
'auto_install': False,
</blockTable>
</pto_header>
<para style="terp_default_9">[[repeatIn(objects,'o')]]</para>
+ <para style="terp_default_8">[[ setLang(o.partner_id.lang) ]]</para>
<para style="terp_default_8">
<font color="white"> </font>
</para>
result[line.picking_id.id] = True
return result.keys()
+ def write(self, cr, uid, ids, vals, context=None):
+ if context is None: context = {}
+ if vals.get('backorder_id'):
+ vals.update(carrier_tracking_ref = '')
+ return super(stock_picking, self).write(cr, uid, ids, vals, context=context)
+
_columns = {
'carrier_id':fields.many2one("delivery.carrier","Carrier"),
'volume': fields.float('Volume'),
--- /dev/null
+-
+ In order to partial delivery with tracking number
+-
+ I create an outgoing picking
+-
+ !record {model: stock.picking, id: outgoing_shipment}:
+ type: out
+ carrier_tracking_ref: ABC12345
+ volume: 2
+ number_of_packages: 2
+-
+ !record {model: stock.move, id: outgoing_move}:
+ picking_id: outgoing_shipment
+ product_id: product.product_product_8
+ product_uom: product.product_uom_unit
+ product_qty: 2
+ location_id: stock.stock_location_stock
+ location_dest_id: stock.stock_location_customers
+-
+ I confirm the picking
+-
+ !workflow {model: stock.picking, action: button_confirm, ref: outgoing_shipment}
+-
+ I make a partial delivery
+-
+ !python {model: stock.partial.picking}: |
+ pick_ids = [ref("outgoing_shipment")]
+ partial_id = self.create(cr, uid, {}, context={'active_model': 'stock.picking','active_ids': pick_ids})
+ partial = self.browse(cr, uid, partial_id, context=context)
+ partial.move_ids[0].write({'quantity': 1})
+ self.do_partial(cr, uid, [partial_id])
+-
+ I check backorder shipment after partial delivery.
+-
+ !python {model: stock.picking}: |
+ shipment = self.browse(cr, uid, ref("delivery.outgoing_shipment"))
+ backorder = shipment.backorder_id
+ assert backorder, "Backorder should be created after partial shipment."
+ assert backorder.state == 'done', "Backorder should be close after received"
+ assert backorder.carrier_tracking_ref == 'ABC12345', 'wrong tracking ref on done picking'
+ assert not shipment.carrier_tracking_ref, 'remaining picking must have no tracking ref'
service = netsvc.LocalService(report_service)
(result, format) = service.create(cr, uid, [res_id], {'model': template.model}, ctx)
# TODO in trunk, change return format to binary to match message_post expected format
- result = base64.b64encode(result)
+ if not ctx.get('default_composition_mode', False) == 'mass_mail':
+ result = base64.b64encode(result)
if not report_name:
report_name = report_service
ext = "." + format
total_currency -= i['amount_currency'] or i['price']
return total, total_currency, account_move_lines
+ def action_expense_cancel(self, cr, uid, ids, context=None):
+ ## We will first check the the move is not reconciled
+ for expense in self.browse(cr, uid, ids, context=context):
+ if expense.account_move_id:
+ for move_line in expense.account_move_id.line_id:
+ if move_line.reconcile_id or move_line.reconcile_partial_id:
+ raise osv.except_osv(
+ _('Error!'),
+ _('Please unreconcile payment accounting entries before cancelling this expense'))
+ ### Then we unlink the move line
+ self.pool.get('account.move').unlink(cr, uid, [expense.account_move_id.id], context=context)
+ wf_service = netsvc.LocalService("workflow")
+ wf_service.trg_delete(uid, 'hr.expense.expense', expense.id, cr)
+ wf_service.trg_create(uid, 'hr.expense.expense', expense.id, cr)
+ wf_service.trg_validate(uid, 'hr.expense.expense', expense.id, 'draft', cr)
+
+ return True
+
def action_receipt_create(self, cr, uid, ids, context=None):
'''
<button name="refuse" states="confirm,accepted" string="Refuse" type="workflow" groups="base.group_hr_user" />
<button name="draft" states="confirm,cancelled" string="Set to Draft" type="workflow" groups="base.group_hr_user" />
<button name="done" states="accepted" string="Generate Accounting Entries" type="workflow" groups="account.group_account_invoice" class="oe_highlight"/>
+ <button name="action_expense_cancel" states="done,paid" string="Cancel Expense" type="object" />
<button name="action_view_receipt" states="done" string="Open Accounting Entries" type="object" groups="account.group_account_invoice"/>
<field name="state" widget="statusbar" statusbar_visible="draft,confirm,accepted,done,paid" statusbar_colors='{"confirm":"blue","cancelled":"red"}'/>
</header>
'state': 'draft',
'manager_id': False,
'manager_id2': False,
+ 'number_of_days_temp': 0,
})
wf_service = netsvc.LocalService("workflow")
for id in ids:
_table = 'hr_analytic_timesheet'
_description = "Timesheet Line"
_inherits = {'account.analytic.line': 'line_id'}
- _order = "id desc"
+ _order = "date_aal DESC, account_name ASC"
+
+ def _get_account_analytic_line(self, cr, uid, ids, context=None):
+ ts_line_ids = self.pool.get('hr.analytic.timesheet').search(cr, uid, [('line_id', 'in', ids)], context=context)
+ return ts_line_ids
+
+ def _get_account_analytic_account(self, cr, uid, ids, context=None):
+ ts_line_ids = self.pool.get('hr.analytic.timesheet').search(cr, uid, [('account_id', 'in', ids)], context=context)
+ return ts_line_ids
+
_columns = {
'line_id': fields.many2one('account.analytic.line', 'Analytic Line', ondelete='cascade', required=True),
'partner_id': fields.related('account_id', 'partner_id', type='many2one', string='Partner', relation='res.partner', store=True),
+
+ 'date_aal': fields.related('line_id', 'date', string="Analytic Line Date", type='date',
+ store={
+ 'account.analytic.line': (_get_account_analytic_line, ['date'], 10),
+ 'hr.analytic.timesheet': (lambda self,cr,uid,ids,context=None: ids, None, 10),
+ }),
+ 'account_name': fields.related('account_id', 'name', string="Analytic Account Name", type='char', size=256,
+ store={
+ 'account.analytic.account': (_get_account_analytic_account, ['name'], 10),
+ 'hr.analytic.timesheet': (lambda self,cr,uid,ids,context=None: ids, None, 10),
+ }
+ ),
}
def unlink(self, cr, uid, ids, context=None):
ename = ''
if emp_id:
ename = emp_obj.browse(cr, uid, emp_id[0], context=context).name
+ res = self.on_change_unit_amount(cr, uid, id, vals.get('product_id'), vals.get('unit_amount'), False, False, vals.get('journal_id'), context)
+ if res['value'].get('amount'):
+ vals.update(amount=res['value']['amount'])
if not vals.get('journal_id',False):
raise osv.except_osv(_('Warning!'), _('No \'Analytic Journal\' is defined for employee %s \nDefine an employee for the selected user and assign an \'Analytic Journal\'!')%(ename,))
if not vals.get('account_id',False):
<field name="date" on_change="on_change_date(date)"/>
<field name="user_id" on_change="on_change_user_id(user_id)" required="1" options='{"no_open": True}'/>
<field name="name"/>
- <field domain="[('type','=','normal'),('use_timesheets','=',1)]" name="account_id" context="{'default_use_timesheets': 1, 'default_type': 'contract'}"/>
+ <field domain="[('type','in',['normal','contract']),('state', '<>', 'close'),('use_timesheets','=',1)]" name="account_id" context="{'default_use_timesheets': 1, 'default_type': 'contract'}"/>
<field name="unit_amount" string="Duration" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" sum="Total time" widget="float_time"/>
<field name="product_uom_id" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" invisible="1"/>
<field name="journal_id" invisible="1"/>
</div>
</group>
<group string="Accounting">
- <field domain="[('type','=','normal'),('state', '<>', 'close'),('parent_id','!=',False)]" name="account_id" select="1"/>
+ <field domain="[('type','in',['normal','contract']),('state', '<>', 'close'),('use_timesheets','=',1)]" name="account_id" select="1" context="{'default_use_timesheets': 1, 'default_type': 'contract'}"/>
<field name="amount"/>
<field name="general_account_id"/>
<field name="journal_id"/>
fiscal_pos_obj = self.pool.get('account.fiscal.position')
product_uom_obj = self.pool.get('product.uom')
invoice_line_obj = self.pool.get('account.invoice.line')
+ res_partner_obj = self.pool.get('res.partner')
invoices = []
if context is None:
context = {}
for journal_type, account_ids in journal_types.items():
for account in analytic_account_obj.browse(cr, uid, list(account_ids), context=context):
partner = account.partner_id
+
if (not partner) or not (account.pricelist_id):
raise osv.except_osv(_('Analytic Account Incomplete!'),
_('Contract incomplete. Please fill in the Customer and Pricelist fields.'))
+ context2 = context.copy()
+ context2['lang'] = account.partner_id.lang
+ # set company_id in context, so the correct default journal will be selected
+ # when creating the invoice
+ context2['company_id'] = account.company_id.id
+ # set force_company in context so the correct properties are selected
+ # (eg. income account, receivable account)
+ context2['force_company'] = account.company_id.id
+
+ partner = res_partner_obj.browse(cr, uid, account.partner_id.id, context=context2)
date_due = False
if partner.property_payment_term:
'date_due': date_due,
'fiscal_position': account.partner_id.property_account_position.id
}
- context2 = context.copy()
- context2['lang'] = partner.lang
- # set company_id in context, so the correct default journal will be selected
- context2['force_company'] = curr_invoice['company_id']
- # set force_company in context so the correct product properties are selected (eg. income account)
- context2['company_id'] = curr_invoice['company_id']
last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context2)
invoices.append(last_invoice)
--- /dev/null
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import test_multi_company
+
+checks = [
+ test_multi_company,
+]
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.tests import common
+
+
+class test_multi_company(common.TransactionCase):
+
+ QTY = 5.0
+ PRICE = 75
+
+ def prepare(self):
+ # super(test_multi_company, self).setUp()
+
+ self.company_obj = self.registry('res.company')
+ self.analytic_account_obj = self.registry('account.analytic.account')
+ self.analytic_line_obj = self.registry('account.analytic.line')
+ self.invoice_obj = self.registry('account.invoice')
+ self.product_obj = self.registry('product.product')
+
+ # load main company
+ self.company_a = self.browse_ref('base.main_company')
+ # create an analytic account
+ self.aa_id = self.analytic_account_obj.create(self.cr, self.uid, {
+ 'name': 'Project',
+ 'company_id': self.company_a.id,
+ 'partner_id': self.ref('base.res_partner_2'),
+ 'pricelist_id': self.ref('product.list0'),
+ })
+ # set a known price on product
+ self.product_obj.write(self.cr, self.uid, self.ref('product.product_product_consultant'), {
+ 'list_price': self.PRICE,
+ })
+
+ def create_invoice(self):
+ # create an analytic line to invoice
+ line_id = self.analytic_line_obj.create(self.cr, self.uid, {
+ 'account_id': self.aa_id,
+ 'amount': -1.0,
+ 'general_account_id': self.ref('account.a_expense'),
+ 'journal_id': self.ref('hr_timesheet.analytic_journal'),
+ 'name': 'some work',
+ 'product_id': self.ref('product.product_product_consultant'),
+ 'product_uom_id': self.ref('product.product_uom_hour'),
+ 'to_invoice': self.ref('hr_timesheet_invoice.timesheet_invoice_factor2'), # 50%
+ 'unit_amount': self.QTY,
+ })
+ # XXX too strong coupling with UI?
+ wizard_obj = self.registry('hr.timesheet.invoice.create')
+ wizard_id = wizard_obj.create(self.cr, self.uid, {
+ 'date': True,
+ 'name': True,
+ 'price': True,
+ 'time': True,
+ }, context={'active_ids': [line_id]})
+ act_win = wizard_obj.do_create(self.cr, self.uid, [wizard_id], context={'active_ids': [line_id]})
+ invoice_ids = self.invoice_obj.search(self.cr, self.uid, act_win['domain'])
+ invoices = self.invoice_obj.browse(self.cr, self.uid, invoice_ids)
+ self.assertEquals(1, len(invoices))
+ return invoices[0]
+
+ def test_00(self):
+ """ invoice task work basic test """
+ self.prepare()
+ invoice = self.create_invoice()
+ self.assertEquals(round(self.QTY * self.PRICE * 0.5, 2), invoice.amount_untaxed)
+
+ def test_01(self):
+ """ invoice task work for analytic account of other company """
+ self.prepare()
+ # create a company B with its own account chart
+ self.company_b_id = self.company_obj.create(self.cr, self.uid, {'name': 'Company B'})
+ self.company_b = self.company_obj.browse(self.cr, self.uid, self.company_b_id)
+ mc_wizard = self.registry('wizard.multi.charts.accounts')
+ mc_wizard_id = mc_wizard.create(self.cr, self.uid, {
+ 'company_id': self.company_b_id,
+ 'chart_template_id': self.ref('account.conf_chart0'),
+ 'code_digits': 2,
+ 'sale_tax': self.ref('account.itaxs'),
+ 'purchase_tax': self.ref('account.otaxs'),
+ # 'complete_tax_set': config.complete_tax_set,
+ 'currency_id': self.company_b.currency_id.id,
+ })
+ mc_wizard.execute(self.cr, self.uid, [mc_wizard_id])
+ # set our analytic account on company B
+ self.analytic_account_obj.write(self.cr, self.uid, [self.aa_id], {
+ 'company_id': self.company_b_id,
+ })
+ invoice = self.create_invoice()
+ self.assertEquals(self.company_b_id, invoice.company_id.id, "invoice created for wrong company")
+ self.assertEquals(self.company_b_id, invoice.journal_id.company_id.id, "invoice created with journal of wrong company")
+ self.assertEquals(self.company_b_id, invoice.invoice_line[0].account_id.company_id.id, "invoice line created with account of wrong company")
+ self.assertEquals(self.company_b_id, invoice.account_id.company_id.id, "invoice line created with partner account of wrong company")
+ # self.assertEquals(self.company_b_id, invoice.fiscal_position.company_id.id, "invoice line created with fiscal position of wrong company")
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
def _check_sheet_state(self, cr, uid, ids, context=None):
if context is None:
context = {}
- for timesheet_line in self.browse(cr, uid, ids, context=context):
- if timesheet_line.sheet_id and timesheet_line.sheet_id.state not in ('draft', 'new'):
- return False
+ ts_sheet_obj = self.pool.get('hr_timesheet_sheet.sheet')
+ # When a timesheet_line is created from view tree of
+ # hr.analytic.timesheet sheet_id is not defined at this stage
+ # here we check the state of the timesheet for the given date
+ # Furthermore we don't want a default sheet_id allowing to bypass
+ # the check so we recompute all sheet_ids
+ sheet_ids = self._sheet(cr ,uid, ids, False, False, context=context)
+ for ts_line_id, sheet in sheet_ids.iteritems():
+
+ if sheet:
+ ts_sheet = ts_sheet_obj.browse(cr, uid, sheet[0], context=context)
+ if ts_sheet.state not in ('draft', 'new'):
+ return False
+
return True
_constraints = [
<field context="{'employee_id': employee_id, 'user_id':user_id, 'timesheet_date_from': date_from, 'timesheet_date_to': date_to}" name="timesheet_ids" nolabel="1">
<tree editable="top" string="Timesheet Activities">
<field name="date"/>
- <field domain="[('type','in',['normal', 'contract']), ('state', '<>', 'close'),('use_timesheets','=',1)]" name="account_id" on_change="on_change_account_id(account_id, user_id)" context="{'default_use_timesheets': 1}"/>
+ <field domain="[('type','in',['normal', 'contract']), ('state', '<>', 'close'), ('use_timesheets','=',1)]" name="account_id" on_change="on_change_account_id(account_id, user_id)" context="{'default_use_timesheets': 1, 'default_type': 'contract'}"/>
<field name="name"/>
<field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" widget="float_time" string="Hours" sum="Hours"/>
<field name="to_invoice" widget="selection"/>
</tree>
<form string="Timesheet Activities" version="7.0">
<field name="date"/>
- <field domain="[('type','=','normal'), ('state', '<>', 'close')]" name="account_id" on_change="on_change_account_id(account_id, user_id)"/>
+ <field domain="[('type','in',['normal', 'contract']), ('state', '<>', 'close'), ('use_timesheets','=',1)]" name="account_id" on_change="on_change_account_id(account_id, user_id)" context="{'default_use_timesheets': 1, 'default_type': 'contract'}"/>
<field name="name"/>
<field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" widget="float_time"/>
<field name="to_invoice"/>
<field name="inherit_id" ref="account.view_account_tax_template_form"/>
<field name="arch" type="xml">
<field position="after" name="price_include">
- <field groups="base.group_extended" name="tax_discount"/>
+ <field name="tax_discount"/>
</field>
<field name="tax_code_id" position="replace" >
<field name="tax_code_id" on_change="onchange_tax_code_id(tax_code_id)" />
</field>
- <field position="after" name="amount">
- <field groups="base.group_extended" name="base_reduction"/>
- <field groups="base.group_extended" name="amount_mva"/>
+ <field position="after" name="tax_discount">
+ <field name="base_reduction"/>
+ <field name="amount_mva"/>
</field>
</field>
</record>
<field name="inherit_id" ref="account.view_tax_form"/>
<field name="arch" type="xml">
<field position="after" name="price_include">
- <field groups="base.group_extended" name="tax_discount"/>
+ <field name="tax_discount"/>
</field>
<field name="tax_code_id" position="replace" >
<field name="tax_code_id" on_change="onchange_tax_code_id(tax_code_id)" />
</field>
- <field position="after" name="amount">
- <field groups="base.group_extended" name="base_reduction"/>
- <field groups="base.group_extended" name="amount_mva"/>
+ <field position="after" name="tax_discount">
+ <field name="base_reduction"/>
+ <field name="amount_mva"/>
</field>
</field>
</record>
<field ref="tax_code_template_ipi" name="ref_tax_code_id"/>
</record>
+ <record id="tax_template_out_ipi24" model="account.tax.template">
+ <field name="description">IPI 24%</field>
+ <field name="name">IPI SaÃda 24%</field>
+ <field name="amount">0.24</field>
+ <field name="type_tax_use">sale</field>
+ <field ref="account_template_201010301" name="account_collected_id"/>
+ <field ref="account_template_101050502" name="account_paid_id"/>
+ <field eval="0" name="price_include"/>
+ <field eval="0" name="tax_discount"/>
+ <field ref="l10n_br_account_chart_template" name="chart_template_id"/>
+ <field ref="tax_code_template_ipi" name="base_code_id"/>
+ <field ref="tax_code_template_ipi" name="tax_code_id"/>
+ <field ref="tax_code_template_ipi" name="ref_base_code_id"/>
+ <field ref="tax_code_template_ipi" name="ref_tax_code_id"/>
+ </record>
+
<record id="tax_template_out_ipi25" model="account.tax.template">
<field name="description">IPI 25%</field>
<field name="name">IPI SaÃda 25%</field>
<field ref="tax_code_template_ipi" name="ref_tax_code_id"/>
</record>
+ <record id="tax_template_out_ipi300" model="account.tax.template">
+ <field name="description">IPI 300%</field>
+ <field name="name">IPI SaÃda 300%</field>
+ <field name="amount">3.00</field>
+ <field name="type_tax_use">sale</field>
+ <field ref="account_template_201010301" name="account_collected_id"/>
+ <field ref="account_template_101050502" name="account_paid_id"/>
+ <field eval="0" name="price_include"/>
+ <field eval="0" name="tax_discount"/>
+ <field ref="l10n_br_account_chart_template" name="chart_template_id"/>
+ <field ref="tax_code_template_ipi" name="base_code_id"/>
+ <field ref="tax_code_template_ipi" name="tax_code_id"/>
+ <field ref="tax_code_template_ipi" name="ref_base_code_id"/>
+ <field ref="tax_code_template_ipi" name="ref_tax_code_id"/>
+ </record>
+
<record id="tax_template_out_ipi330" model="account.tax.template">
<field name="description">IPI 330%</field>
<field name="name">IPI SaÃda 330%</field>
<field ref="tax_code_template_ipi" name="ref_tax_code_id"/>
</record>
+ <record id="tax_template_in_ipi24" model="account.tax.template">
+ <field name="description">IPI 24%</field>
+ <field name="name">IPI Entrada 24%</field>
+ <field name="amount">0.24</field>
+ <field name="type_tax_use">purchase</field>
+ <field ref="account_template_101050502" name="account_collected_id"/>
+ <field ref="account_template_201010301" name="account_paid_id"/>
+ <field eval="0" name="price_include"/>
+ <field eval="0" name="tax_discount"/>
+ <field ref="l10n_br_account_chart_template" name="chart_template_id"/>
+ <field ref="tax_code_template_ipi" name="base_code_id"/>
+ <field ref="tax_code_template_ipi" name="tax_code_id"/>
+ <field ref="tax_code_template_ipi" name="ref_base_code_id"/>
+ <field ref="tax_code_template_ipi" name="ref_tax_code_id"/>
+ </record>
+
<record id="tax_template_in_ipi25" model="account.tax.template">
<field name="description">IPI 25%</field>
<field name="name">IPI Entrada 25%</field>
<field ref="tax_code_template_ipi" name="ref_tax_code_id"/>
</record>
+ <record id="tax_template_in_ipi300" model="account.tax.template">
+ <field name="description">IPI 300%</field>
+ <field name="name">IPI Entrada 300%</field>
+ <field name="amount">3.00</field>
+ <field name="type_tax_use">purchase</field>
+ <field ref="account_template_101050502" name="account_collected_id"/>
+ <field ref="account_template_201010301" name="account_paid_id"/>
+ <field eval="0" name="price_include"/>
+ <field eval="0" name="tax_discount"/>
+ <field ref="l10n_br_account_chart_template" name="chart_template_id"/>
+ <field ref="tax_code_template_ipi" name="base_code_id"/>
+ <field ref="tax_code_template_ipi" name="tax_code_id"/>
+ <field ref="tax_code_template_ipi" name="ref_base_code_id"/>
+ <field ref="tax_code_template_ipi" name="ref_tax_code_id"/>
+ </record>
+
<record id="tax_template_in_ipi330" model="account.tax.template">
<field name="description">IPI 330%</field>
<field name="name">IPI Entrada 330%</field>
id,code,name,parent_id:id,user_type:id,type,reconcile
0,0,Azienda,,account.data_account_type_view,view,FALSE
-1,1,ATTIVO ,0,account.data_account_type_view,view,TRUE
-11,11,IMMOBILIZZAZIONI IMMATERIALI ,1,account.data_account_type_view,view,TRUE
+1,1,ATTIVO ,0,account.account_type_asset_view1,view,TRUE
+11,11,IMMOBILIZZAZIONI IMMATERIALI ,1,account.account_type_asset_view1,view,TRUE
1101,1101,costi di impianto ,11,account.data_account_type_asset,other,TRUE
1106,1106,software ,11,account.data_account_type_asset,other,TRUE
1108,1108,avviamento ,11,account.data_account_type_asset,other,TRUE
1111,1111,fondo ammortamento costi di impianto ,11,account.data_account_type_asset,other,TRUE
1116,1116,fondo ammortamento software ,11,account.data_account_type_asset,other,TRUE
1118,1118,fondo ammortamento avviamento ,11,account.data_account_type_asset,other,TRUE
-12,12,IMMOBILIZZAZIONI MATERIALI ,1,account.data_account_type_view,view,TRUE
+12,12,IMMOBILIZZAZIONI MATERIALI ,1,account.account_type_asset_view1,view,TRUE
1201,1201,fabbricati ,12,account.data_account_type_asset,other,TRUE
1202,1202,impianti e macchinari ,12,account.data_account_type_asset,other,TRUE
1204,1204,attrezzature commerciali ,12,account.data_account_type_asset,other,TRUE
1217,1217,fondo ammortamento automezzi ,12,account.data_account_type_asset,other,TRUE
1218,1218,fondo ammortamento imballaggi durevoli ,12,account.data_account_type_asset,other,TRUE
1220,1220,fornitori immobilizzazioni c/acconti ,12,account.data_account_type_asset,other,TRUE
-13,13,IMMOBILIZZAZIONI FINANZIARIE ,1,account.data_account_type_view,view,TRUE
+13,13,IMMOBILIZZAZIONI FINANZIARIE ,1,account.account_type_asset_view1,view,TRUE
1301,1301,mutui attivi ,13,account.data_account_type_asset,other,TRUE
-14,14,RIMANENZE ,1,account.data_account_type_view,view,TRUE
+14,14,RIMANENZE ,1,account.account_type_asset_view1,view,TRUE
1401,1401,materie di consumo ,14,account.data_account_type_asset,other,TRUE
1404,1404,merci ,14,account.data_account_type_asset,other,TRUE
1410,1410,fornitori c/acconti ,14,account.data_account_type_asset,other,TRUE
-15,15,CREDITI COMMERCIALI ,1,account.data_account_type_view,view,TRUE
+15,15,CREDITI COMMERCIALI ,1,account.account_type_asset_view1,view,TRUE
1501,1501,crediti v/clienti ,15,account.data_account_type_receivable,receivable,TRUE
1502,1502,crediti commerciali diversi ,15,account.data_account_type_asset,other,TRUE
1503,1503,clienti c/spese anticipate ,15,account.data_account_type_receivable,receivable,TRUE
1531,1531,crediti da liquidare ,15,account.data_account_type_asset,other,TRUE
1540,1540,fondo svalutazione crediti ,15,account.data_account_type_asset,other,TRUE
1541,1541,fondo rischi su crediti ,15,account.data_account_type_asset,other,TRUE
-16,16,CREDITI DIVERSI ,1,account.data_account_type_view,view,TRUE
+16,16,CREDITI DIVERSI ,1,account.account_type_asset_view1,view,TRUE
1601,1601,IVA n/credito ,16,account.data_account_type_asset,other,TRUE
1602,1602,IVA c/acconto ,16,account.data_account_type_asset,other,TRUE
1605,1605,crediti per IVA ,16,account.data_account_type_asset,other,TRUE
1620,1620,personale c/acconti ,16,account.data_account_type_asset,other,TRUE
1630,1630,crediti v/istituti previdenziali ,16,account.data_account_type_asset,other,TRUE
1640,1640,debitori diversi ,16,account.data_account_type_receivable,receivable,TRUE
-18,18,DISPONIBILITÀ LIQUIDE ,1,account.data_account_type_view,view,TRUE
+18,18,DISPONIBILITÀ LIQUIDE ,1,account.account_type_asset_view1,view,TRUE
1801,1801,banche c/c ,18,account.data_account_type_bank,liquidity,TRUE
1810,1810,c/c postali ,18,account.data_account_type_bank,liquidity,TRUE
1820,1820,denaro in cassa ,18,account.data_account_type_cash,liquidity,TRUE
1821,1821,assegni ,18,account.data_account_type_cash,liquidity,TRUE
1822,1822,valori bollati ,18,account.data_account_type_cash,liquidity,TRUE
-19,19,RATEI E RISCONTI ATTIVI ,1,account.data_account_type_view,view,TRUE
+19,19,RATEI E RISCONTI ATTIVI ,1,account.account_type_asset_view1,view,TRUE
1901,1901,ratei attivi ,19,account.data_account_type_asset,other,TRUE
1902,1902,risconti attivi ,19,account.data_account_type_asset,other,TRUE
-2,2,PASSIVO ,0,account.data_account_type_view,view,TRUE
-20,20,PATRIMONIO NETTO ,2,account.data_account_type_view,view,TRUE
+2,2,PASSIVO ,0,account.account_type_liability_view1,view,TRUE
+20,20,PATRIMONIO NETTO ,2,account.account_type_liability_view1,view,TRUE
2101,2101,patrimonio netto ,20,account.data_account_type_liability,other,TRUE
2102,2102,utile d'esercizio ,20,account.data_account_type_liability,other,TRUE
2103,2103,perdita d'esercizio ,20,account.data_account_type_liability,other,TRUE
2104,2104,prelevamenti extra gestione ,20,account.data_account_type_liability,other,TRUE
2105,2105,titolare c/ritenute subite ,20,account.data_account_type_liability,other,TRUE
-22,22,FONDI PER RISCHI E ONERI ,2,account.data_account_type_view,view,TRUE
+22,22,FONDI PER RISCHI E ONERI ,2,account.account_type_liability_view1,view,TRUE
2201,2201,fondo per imposte ,22,account.data_account_type_liability,other,TRUE
2204,2204,fondo responsabilità civile ,22,account.data_account_type_liability,other,TRUE
2205,2205,fondo spese future ,22,account.data_account_type_liability,other,TRUE
2211,2211,fondo manutenzioni programmate ,22,account.data_account_type_liability,other,TRUE
-23,23,TRATTAMENTO FINE RAPPORTO DI LAVORO ,2,account.data_account_type_view,view,TRUE
+23,23,TRATTAMENTO FINE RAPPORTO DI LAVORO ,2,account.account_type_liability_view1,view,TRUE
2301,2301,debiti per TFRL ,23,account.data_account_type_liability,other,TRUE
-24,24,DEBITI FINANZIARI ,2,account.data_account_type_view,view,TRUE
+24,24,DEBITI FINANZIARI ,2,account.account_type_liability_view1,view,TRUE
2410,2410,mutui passivi ,24,account.data_account_type_liability,other,TRUE
2411,2411,banche c/sovvenzioni ,24,account.data_account_type_liability,other,TRUE
2420,2420,banche c/c passivi ,24,account.data_account_type_liability,other,TRUE
2422,2422,banche c/cambiali all'incasso ,24,account.data_account_type_liability,other,TRUE
2423,2423,banche c/anticipi su fatture ,24,account.data_account_type_liability,other,TRUE
2440,2440,debiti v/altri finanziatori ,24,account.data_account_type_liability,other,TRUE
-25,25,DEBITI COMMERCIALI ,2,account.data_account_type_view,view,TRUE
+25,25,DEBITI COMMERCIALI ,2,account.account_type_liability_view1,view,TRUE
2501,2501,debiti v/fornitori ,25,account.data_account_type_payable,payable,TRUE
2503,2503,cambiali passive ,25,account.data_account_type_liability,other,TRUE
2520,2520,fatture da ricevere ,25,account.data_account_type_liability,other,TRUE
2521,2521,debiti da liquidare ,25,account.data_account_type_liability,other,TRUE
2530,2530,clienti c/acconti ,25,account.data_account_type_payable,payable,TRUE
-26,26,DEBITI DIVERSI ,2,account.data_account_type_view,view,TRUE
+26,26,DEBITI DIVERSI ,2,account.account_type_liability_view1,view,TRUE
2601,2601,IVA n/debito ,26,account.data_account_type_liability,other,TRUE
2602,2602,debiti per ritenute da versare ,26,account.data_account_type_payable,payable,TRUE
2605,2605,erario c/IVA ,26,account.data_account_type_payable,payable,TRUE
2622,2622,clienti c/cessione ,26,account.data_account_type_liability,other,TRUE
2630,2630,debiti v/istituti previdenziali ,26,account.data_account_type_liability,other,TRUE
2640,2640,creditori diversi ,26,account.data_account_type_payable,payable,TRUE
-27,27,RATEI E RISCONTI PASSIVI ,2,account.data_account_type_view,view,TRUE
+27,27,RATEI E RISCONTI PASSIVI ,2,account.account_type_liability_view1,view,TRUE
2701,2701,ratei passivi ,27,account.data_account_type_liability,other,TRUE
2702,2702,risconti passivi ,27,account.data_account_type_liability,other,TRUE
-28,28,CONTI TRANSITORI E DIVERSI ,2,account.data_account_type_view,view,TRUE
+28,28,CONTI TRANSITORI E DIVERSI ,2,account.account_type_liability_view1,view,TRUE
2801,2801,bilancio di apertura ,28,account.data_account_type_liability,other,TRUE
2802,2802,bilancio di chiusura ,28,account.data_account_type_liability,other,TRUE
2810,2810,IVA c/liquidazioni ,28,account.data_account_type_liability,other,TRUE
2820,2820,banca ... c/c ,28,account.data_account_type_liability,other,TRUE
2821,2821,banca ... c/c ,28,account.data_account_type_liability,other,TRUE
2822,2822,banca ... c/c ,28,account.data_account_type_liability,other,TRUE
-29,29,CONTI DEI SISTEMI SUPPLEMENTARI ,2,account.data_account_type_view,view,TRUE
+29,29,CONTI DEI SISTEMI SUPPLEMENTARI ,2,account.account_type_liability_view1,view,TRUE
2901,2901,beni di terzi ,29,account.data_account_type_liability,other,TRUE
2902,2902,depositanti beni ,29,account.data_account_type_liability,other,TRUE
2911,2911,merci da ricevere ,29,account.data_account_type_liability,other,TRUE
2927,2927,creditori per fideiussioni ,29,account.data_account_type_liability,other,TRUE
2931,2931,rischi per avalli ,29,account.data_account_type_liability,other,TRUE
2932,2932,creditori per avalli ,29,account.data_account_type_liability,other,TRUE
-3,3,VALORE DELLA PRODUZIONE ,0,account.data_account_type_view,view,TRUE
-31,31,VENDITE E PRESTAZIONI ,3,account.data_account_type_view,view,TRUE
+3,3,VALORE DELLA PRODUZIONE ,0,account.account_type_income_view1,view,TRUE
+31,31,VENDITE E PRESTAZIONI ,3,account.account_type_income_view1,view,TRUE
3101,3101,merci c/vendite ,31,account.data_account_type_income,other,TRUE
3103,3103,rimborsi spese di vendita ,31,account.data_account_type_income,other,TRUE
3110,3110,resi su vendite ,31,account.data_account_type_income,other,TRUE
3111,3111,ribassi e abbuoni passivi ,31,account.data_account_type_income,other,TRUE
3112,3112,premi su vendite ,31,account.data_account_type_income,other,TRUE
-32,32,RICAVI E PROVENTI DIVERSI ,3,account.data_account_type_view,view,TRUE
+32,32,RICAVI E PROVENTI DIVERSI ,3,account.account_type_income_view1,view,TRUE
3201,3201,fitti attivi ,32,account.data_account_type_income,other,TRUE
3202,3202,proventi vari ,32,account.data_account_type_income,other,TRUE
3210,3210,arrotondamenti attivi ,32,account.data_account_type_income,other,TRUE
3220,3220,plusvalenze ordinarie diverse ,32,account.data_account_type_income,other,TRUE
3230,3230,sopravvenienze attive ordinarie diverse ,32,account.data_account_type_income,other,TRUE
3240,3240,insussistenze attive ordinarie diverse ,32,account.data_account_type_income,other,TRUE
-4,4,COSTI DELLA PRODUZIONE ,0,account.data_account_type_view,view,TRUE
-41,41,COSTO DEL VENDUTO ,4,account.data_account_type_view,view,TRUE
+4,4,COSTI DELLA PRODUZIONE ,0,account.account_type_expense_view1,view,TRUE
+41,41,COSTO DEL VENDUTO ,4,account.account_type_expense_view1,view,TRUE
4101,4101,merci c/acquisti ,41,account.data_account_type_expense,other,TRUE
4102,4102,materie di consumo c/acquisti ,41,account.data_account_type_expense,other,TRUE
4105,4105,merci c/apporti ,41,account.data_account_type_expense,other,TRUE
4122,4122,materie di consumo c/esistenze iniziali ,41,account.data_account_type_expense,other,TRUE
4131,4131,merci c/rimanenze finali ,41,account.data_account_type_expense,other,TRUE
4132,4132,materie di consumo c/rimanenze finali ,41,account.data_account_type_expense,other,TRUE
-42,42,COSTI PER SERVIZI ,4,account.data_account_type_view,view,TRUE
+42,42,COSTI PER SERVIZI ,4,account.account_type_expense_view1,view,TRUE
4201,4201,costi di trasporto ,42,account.data_account_type_expense,other,TRUE
4202,4202,costi per energia ,42,account.data_account_type_expense,other,TRUE
4203,4203,costi di pubblicità ,42,account.data_account_type_expense,other,TRUE
4211,4211,costi di manutenzione e riparazione ,42,account.data_account_type_expense,other,TRUE
4212,4212,provvigioni passive ,42,account.data_account_type_expense,other,TRUE
4213,4213,spese di incasso ,42,account.data_account_type_expense,other,TRUE
-43,43,COSTI PER GODIMENTO BENI DI TERZI ,4,account.data_account_type_view,view,TRUE
+43,43,COSTI PER GODIMENTO BENI DI TERZI ,4,account.account_type_expense_view1,view,TRUE
4301,4301,fitti passivi ,43,account.data_account_type_expense,other,TRUE
4302,4302,canoni di leasing ,43,account.data_account_type_expense,other,TRUE
-44,44,COSTI PER IL PERSONALE ,4,account.data_account_type_view,view,TRUE
+44,44,COSTI PER IL PERSONALE ,4,account.account_type_expense_view1,view,TRUE
4401,4401,salari e stipendi ,44,account.data_account_type_expense,other,TRUE
4402,4402,oneri sociali ,44,account.data_account_type_expense,other,TRUE
4403,4403,TFRL ,44,account.data_account_type_expense,other,TRUE
4404,4404,altri costi per il personale ,44,account.data_account_type_expense,other,TRUE
-45,45,AMMORTAMENTI IMMOBILIZZAZIONI IMMATERIALI ,4,account.data_account_type_view,view,TRUE
+45,45,AMMORTAMENTI IMMOBILIZZAZIONI IMMATERIALI ,4,account.account_type_expense_view1,view,TRUE
4501,4501,ammortamento costi di impianto ,45,account.data_account_type_expense,other,TRUE
4506,4506,ammortamento software ,45,account.data_account_type_expense,other,TRUE
4508,4508,ammortamento avviamento ,45,account.data_account_type_expense,other,TRUE
-46,46,AMMORTAMENTI IMMOBILIZZAZIONI MATERIALI ,4,account.data_account_type_view,view,TRUE
+46,46,AMMORTAMENTI IMMOBILIZZAZIONI MATERIALI ,4,account.account_type_expense_view1,view,TRUE
4601,4601,ammortamento fabbricati ,46,account.data_account_type_expense,other,TRUE
4602,4602,ammortamento impianti e macchinari ,46,account.data_account_type_expense,other,TRUE
4604,4604,ammortamento attrezzature commerciali ,46,account.data_account_type_expense,other,TRUE
4606,4606,ammortamento arredamento ,46,account.data_account_type_expense,other,TRUE
4607,4607,ammortamento automezzi ,46,account.data_account_type_expense,other,TRUE
4608,4608,ammortamento imballaggi durevoli ,46,account.data_account_type_expense,other,TRUE
-47,47,SVALUTAZIONI ,4,account.data_account_type_view,view,TRUE
+47,47,SVALUTAZIONI ,4,account.account_type_expense_view1,view,TRUE
4701,4701,svalutazioni immobilizzazioni immateriali ,47,account.data_account_type_expense,other,TRUE
4702,4702,svalutazioni immobilizzazioni materiali ,47,account.data_account_type_expense,other,TRUE
4706,4706,svalutazione crediti ,47,account.data_account_type_expense,other,TRUE
-48,48,ACCANTONAMENTI ,4,account.data_account_type_view,view,TRUE
-481,481,ACCANTONAMENTI PER RISCHI ,48,account.data_account_type_view,view,TRUE
+48,48,ACCANTONAMENTI ,4,account.account_type_expense_view1,view,TRUE
+481,481,ACCANTONAMENTI PER RISCHI ,48,account.account_type_expense_view1,view,TRUE
4814,4814,accantonamento per responsabilità civile ,481,account.data_account_type_expense,other,TRUE
-482,482,ALTRI ACCANTONAMENTI ,48,account.data_account_type_view,view,TRUE
+482,482,ALTRI ACCANTONAMENTI ,48,account.account_type_expense_view1,view,TRUE
4821,4821,accantonamento per spese future ,482,account.data_account_type_expense,other,TRUE
4823,4823,accantonamento per manutenzioni programmate ,482,account.data_account_type_expense,other,TRUE
-49,49,ONERI DIVERSI ,4,account.data_account_type_view,view,TRUE
+49,49,ONERI DIVERSI ,4,account.account_type_expense_view1,view,TRUE
4901,4901,oneri fiscali diversi ,49,account.data_account_type_expense,other,TRUE
4903,4903,oneri vari ,49,account.data_account_type_expense,other,TRUE
4905,4905,perdite su crediti ,49,account.data_account_type_expense,other,TRUE
4920,4920,minusvalenze ordinarie diverse ,49,account.data_account_type_expense,other,TRUE
4930,4930,sopravvenienze passive ordinarie diverse ,49,account.data_account_type_expense,other,TRUE
4940,4940,insussistenze passive ordinarie diverse ,49,account.data_account_type_expense,other,TRUE
-5,5,PROVENTI E ONERI FINANZIARI ,0,account.data_account_type_view,view,TRUE
-51,51,PROVENTI FINANZIARI ,5,account.data_account_type_view,view,TRUE
+5,5,PROVENTI E ONERI FINANZIARI ,0,account.account_type_income_view1,view,TRUE
+51,51,PROVENTI FINANZIARI ,5,account.account_type_income_view1,view,TRUE
5110,5110,interessi attivi v/clienti ,51,account.data_account_type_income,other,TRUE
5115,5115,interessi attivi bancari ,51,account.data_account_type_income,other,TRUE
5116,5116,interessi attivi postali ,51,account.data_account_type_income,other,TRUE
5140,5140,proventi finanziari diversi ,51,account.data_account_type_income,other,TRUE
-52,52,ONERI FINANZIARI ,5,account.data_account_type_view,view,TRUE
+52,52,ONERI FINANZIARI ,5,account.account_type_expense_view1,view,TRUE
5201,5201,interessi passivi v/fornitori ,52,account.data_account_type_expense,other,TRUE
5202,5202,interessi passivi bancari ,52,account.data_account_type_expense,other,TRUE
5203,5203,sconti passivi bancari ,52,account.data_account_type_expense,other,TRUE
5210,5210,interessi passivi su mutui ,52,account.data_account_type_expense,other,TRUE
5240,5240,oneri finanziari diversi ,52,account.data_account_type_expense,other,TRUE
-7,7,PROVENTI E ONERI STRAORDINARI ,0,account.data_account_type_view,view,TRUE
-71,71,PROVENTI STRAORDINARI ,7,account.data_account_type_view,view,TRUE
+7,7,PROVENTI E ONERI STRAORDINARI ,0,account.account_type_income_view1,view,TRUE
+71,71,PROVENTI STRAORDINARI ,7,account.account_type_income_view1,view,TRUE
7101,7101,plusvalenze straordinarie ,71,account.data_account_type_income,other,TRUE
7102,7102,sopravvenienze attive straordinarie ,71,account.data_account_type_income,other,TRUE
7103,7103,insussistenze attive straordinarie ,71,account.data_account_type_income,other,TRUE
-72,72,ONERI STRAORDINARI ,7,account.data_account_type_view,view,TRUE
+72,72,ONERI STRAORDINARI ,7,account.account_type_expense_view1,view,TRUE
7201,7201,minusvalenze straordinarie ,72,account.data_account_type_expense,other,TRUE
7202,7202,sopravvenienze passive straordinarie ,72,account.data_account_type_expense,other,TRUE
7203,7203,insussistenze passive straordinarie ,72,account.data_account_type_expense,other,TRUE
7204,7204,imposte esercizi precedenti ,72,account.data_account_type_expense,other,TRUE
-8,8,IMPOSTE DELL'ESERCIZIO ,0,account.data_account_type_view,view,TRUE
+8,8,IMPOSTE DELL'ESERCIZIO ,0,account.account_type_expense_view1,view,TRUE
8101,8101,imposte dell'esercizio ,8,account.data_account_type_expense,other,TRUE
-9,9,CONTI DI RISULTATO ,0,account.data_account_type_view,view,TRUE
+9,9,CONTI DI RISULTATO ,0,account.account_type_expense_view1,view,TRUE
9101,9101,conto di risultato economico ,9,account.data_account_type_expense,other,TRUE
9102,9102,stato patrimoniale,9,account.data_account_type_expense,other,TRUE
20a,20a,l10n_it_chart_template_generic,Iva al 20% (credito),4,0.2,,False,percent,1601,1601,purchase,template_impcode_pagata_20,template_ivacode_pagata_20,template_impcode_pagata_20,template_ivacode_pagata_20,1,1,False,-1,-1
10v,10v,l10n_it_chart_template_generic,Iva al 10% (debito),5,0.1,,False,percent,2601,2601,sale,template_impcode_riscossa_10,template_ivacode_riscossa_10,template_impcode_riscossa_10,template_ivacode_riscossa_10,-1,-1,False,1,1
10a,10a,l10n_it_chart_template_generic,Iva al 10% (credito),6,0.1,,False,percent,1601,1601,purchase,template_impcode_pagata_10,template_ivacode_pagata_10,template_impcode_pagata_10,template_ivacode_pagata_10,1,1,False,-1,-1
-10AO,10AO,l10n_it_chart_template_generic,Iva al 10% indetraibile,7,0.1,,True,percent,,,purchase,template_impcode_pagata_10ind,,template_impcode_pagata_10ind,,1,1,False,-1,-1
-10AOb,10AOb,l10n_it_chart_template_generic,Iva al 10% indetraibile (D),200,0,10AO,False,balance,1601,1601,purchase,,template_ivacode_pagata_10,,template_ivacode_pagata_10,1,1,False,-1,-1
-10AOa,10AOa,l10n_it_chart_template_generic,Iva al 10% indetraibile (I),100,1,10AO,False,percent,,,purchase,,template_ivacode_pagata_10ind,,template_ivacode_pagata_10ind,1,1,False,-1,-1
+10AO,10AO,l10n_it_chart_template_generic,Iva al 10% indetraibile,7,0.1,,False,percent,,,purchase,template_impcode_pagata_10ind,template_ivacode_pagata_10ind,template_impcode_pagata_10ind,template_ivacode_pagata_10ind,1,1,False,-1,-1
12v,12v,l10n_it_chart_template_generic,Iva 12% (debito),8,0.12,,False,percent,2601,2601,sale,template_impcode_riscossa_12,template_ivacode_riscossa_12,template_impcode_riscossa_12,template_ivacode_riscossa_12,-1,-1,False,1,1
12a,12a,l10n_it_chart_template_generic,Iva 12% (credito),9,0.12,,False,percent,1601,1601,purchase,template_impcode_pagata_12,template_ivacode_pagata_12,template_impcode_pagata_12,template_ivacode_pagata_12,1,1,False,-1,-1
2010,2010,l10n_it_chart_template_generic,Iva al 20% detraibile 10%,10,0.2,,True,percent,,,purchase,template_impcode_pagata_20det10,,template_impcode_pagata_20det10,,1,1,False,-1,-1
2040,2040,l10n_it_chart_template_generic,Iva al 20% detraibile 40%,12,0.2,,True,percent,,,purchase,template_impcode_pagata_20det40,,template_impcode_pagata_20det40,,1,1,False,-1,-1
2040b,2040b,l10n_it_chart_template_generic,Iva al 20% detraibile 40% (D),200,0,2040,False,balance,1601,1601,purchase,,template_ivacode_pagata_20det40,,template_ivacode_pagata_20det40,1,1,False,-1,-1
2040a,2040a,l10n_it_chart_template_generic,Iva al 20% detraibile 40% (I),100,0.6,2040,False,percent,,,purchase,,template_ivacode_pagata_20det40ind,,template_ivacode_pagata_20det40ind,1,1,False,-1,-1
-20AO,20AO,l10n_it_chart_template_generic,Iva al 20% indetraibile,13,0.2,,True,percent,,,purchase,template_impcode_pagata_20ind,,template_impcode_pagata_20ind,,1,1,False,-1,-1
-20AOb,20AOb,l10n_it_chart_template_generic,Iva al 20% indetraibile (D),200,0,20AO,False,balance,1601,1601,purchase,,template_ivacode_pagata_20ind,,template_ivacode_pagata_20ind,1,1,False,-1,-1
-20AOa,20AOa,l10n_it_chart_template_generic,Iva al 20% indetraibile (I),100,1,20AO,False,percent,,,purchase,,template_ivacode_pagata_20ind,,template_ivacode_pagata_20ind,1,1,False,-1,-1
+20AO,20AO,l10n_it_chart_template_generic,Iva al 20% indetraibile,13,0.2,,False,percent,,,purchase,template_impcode_pagata_20ind,template_ivacode_pagata_20ind,template_impcode_pagata_20ind,template_ivacode_pagata_20ind,1,1,False,-1,-1
20I5,20I5,l10n_it_chart_template_generic,IVA al 20% detraibile al 50%,14,0.2,,True,percent,,,purchase,template_impcode_pagata_20det50,,template_impcode_pagata_20det50,,1,1,False,-1,-1
20I5b,20I5b,l10n_it_chart_template_generic,IVA al 20% detraibile al 50% (D),200,0,20I5,False,balance,1601,1601,purchase,,template_ivacode_pagata_20det50,,template_ivacode_pagata_20det50,1,1,False,-1,-1
20I5a,20I5a,l10n_it_chart_template_generic,IVA al 20% detraibile al 50% (I),100,0.5,20I5,False,percent,,,purchase,,template_ivacode_pagata_20det50ind,,template_ivacode_pagata_20det50ind,1,1,False,-1,-1
2a,2a,l10n_it_chart_template_generic,Iva 2% (credito),16,0.02,,False,percent,1601,1601,purchase,template_impcode_pagata_2,template_ivacode_pagata_2,template_impcode_pagata_2,template_ivacode_pagata_2,1,1,False,-1,-1
4v,4v,l10n_it_chart_template_generic,Iva 4% (debito),17,0.04,,False,percent,2601,2601,sale,template_impcode_riscossa_4,template_ivacode_riscossa_4,template_impcode_riscossa_4,template_ivacode_riscossa_4,-1,-1,False,1,1
4a,4a,l10n_it_chart_template_generic,Iva 4% (credito),18,0.04,,False,percent,1601,1601,purchase,template_impcode_pagata_4,template_ivacode_pagata_4,template_impcode_pagata_4,template_ivacode_pagata_4,1,1,False,-1,-1
-4AO,4AO,l10n_it_chart_template_generic,Iva al 4% indetraibile,19,0.04,,True,percent,,,purchase,template_impcode_pagata_4ind,,template_impcode_pagata_4ind,,1,1,False,-1,-1
-4AOb,4AOb,l10n_it_chart_template_generic,Iva al 4% indetraibile (D),200,0,4AO,False,balance,1601,1601,purchase,,template_ivacode_pagata_4ind,,template_ivacode_pagata_4ind,1,1,False,-1,-1
-4AOa,4AOa,l10n_it_chart_template_generic,Iva al 4% indetraibile (I),100,1,4AO,False,percent,,,purchase,,template_ivacode_pagata_4ind,,template_ivacode_pagata_4ind,1,1,False,-1,-1
+4AO,4AO,l10n_it_chart_template_generic,Iva al 4% indetraibile,19,0.04,,False,percent,,,purchase,template_impcode_pagata_4ind,template_ivacode_pagata_4ind,template_impcode_pagata_4ind,template_ivacode_pagata_4ind,1,1,False,-1,-1
10I5,10I5,l10n_it_chart_template_generic,IVA al 10% detraibile al 50%,20,0.1,,True,percent,,,purchase,template_impcode_pagata_10det50,,template_impcode_pagata_10det50,,1,1,False,-1,-1
10I5b,10I5b,l10n_it_chart_template_generic,IVA al 10% detraibile al 50% (D),200,0,10I5,False,balance,1601,1601,purchase,,template_ivacode_pagata_10det50,,template_ivacode_pagata_10det50,1,1,False,-1,-1
10I5a,10I5a,l10n_it_chart_template_generic,IVA al 10% detraibile al 50% (I),100,0.5,10I5,False,percent,,,purchase,,template_ivacode_pagata_10det50ind,,template_ivacode_pagata_10det50ind,1,1,False,-1,-1
2140,2140,l10n_it_chart_template_generic,Iva al 21% detraibile 40%,33,0.21,,True,percent,,,purchase,template_impcode_pagata_21det40,,template_impcode_pagata_21det40,,1,1,False,-1,-1
2140b,2140b,l10n_it_chart_template_generic,Iva al 21% detraibile 40% (D),200,0,2140,False,balance,1601,1601,purchase,,template_ivacode_pagata_21det40,,template_ivacode_pagata_21det40,1,1,False,-1,-1
2140a,2140a,l10n_it_chart_template_generic,Iva al 21% detraibile 40% (I),100,0.6,2140,False,percent,,,purchase,,template_ivacode_pagata_21det40ind,,template_ivacode_pagata_21det40ind,1,1,False,-1,-1
-21AO,21AO,l10n_it_chart_template_generic,Iva al 21% indetraibile,34,0.21,,True,percent,,,purchase,template_impcode_pagata_21ind,,template_impcode_pagata_21ind,,1,1,False,-1,-1
-21AOb,21AOb,l10n_it_chart_template_generic,Iva al 21% indetraibile (D),200,0,21AO,False,balance,1601,1601,purchase,,template_ivacode_pagata_21ind,,template_ivacode_pagata_21ind,1,1,False,-1,-1
-21AOa,21AOa,l10n_it_chart_template_generic,Iva al 21% indetraibile (I),100,1,21AO,False,percent,,,purchase,,template_ivacode_pagata_21ind,,template_ivacode_pagata_21ind,1,1,False,-1,-1
+21AO,21AO,l10n_it_chart_template_generic,Iva al 21% indetraibile,34,0.21,,False,percent,,,purchase,template_impcode_pagata_21ind,template_ivacode_pagata_21ind,template_impcode_pagata_21ind,template_ivacode_pagata_21ind,1,1,False,-1,-1
21I5,21I5,l10n_it_chart_template_generic,IVA al 21% detraibile al 50%,35,0.21,,True,percent,,,purchase,template_impcode_pagata_21det50,,template_impcode_pagata_21det50,,1,1,False,-1,-1
21I5b,21I5b,l10n_it_chart_template_generic,IVA al 21% detraibile al 50% (D),200,0,21I5,False,balance,1601,1601,purchase,,template_ivacode_pagata_21det50,,template_ivacode_pagata_21det50,1,1,False,-1,-1
21I5a,21I5a,l10n_it_chart_template_generic,IVA al 21% detraibile al 50% (I),100,0.5,21I5,False,percent,,,purchase,,template_ivacode_pagata_21det50ind,,template_ivacode_pagata_21det50ind,1,1,False,-1,-1
2240,2240,l10n_it_chart_template_generic,Iva al 22% detraibile 40%,33,0.22,,True,percent,,,purchase,template_impcode_pagata_22det40,,template_impcode_pagata_22det40,,1,1,False,-1,-1
2240b,2240b,l10n_it_chart_template_generic,Iva al 22% detraibile 40% (D),200,0,2240,False,balance,1601,1601,purchase,,template_ivacode_pagata_22det40,,template_ivacode_pagata_22det40,1,1,False,-1,-1
2240a,2240a,l10n_it_chart_template_generic,Iva al 22% detraibile 40% (I),100,0.6,2240,False,percent,,,purchase,,template_ivacode_pagata_22det40ind,,template_ivacode_pagata_22det40ind,1,1,False,-1,-1
-22AO,22AO,l10n_it_chart_template_generic,Iva al 22% indetraibile,34,0.22,,True,percent,,,purchase,template_impcode_pagata_22ind,,template_impcode_pagata_22ind,,1,1,False,-1,-1
-22AOb,22AOb,l10n_it_chart_template_generic,Iva al 22% indetraibile (D),200,0,22AO,False,balance,1601,1601,purchase,,template_ivacode_pagata_22ind,,template_ivacode_pagata_22ind,1,1,False,-1,-1
-22AOa,22AOa,l10n_it_chart_template_generic,Iva al 22% indetraibile (I),100,1,22AO,False,percent,,,purchase,,template_ivacode_pagata_22ind,,template_ivacode_pagata_22ind,1,1,False,-1,-1
+22AO,22AO,l10n_it_chart_template_generic,Iva al 22% indetraibile,34,0.22,,False,percent,,,purchase,template_impcode_pagata_22ind,template_ivacode_pagata_22ind,template_impcode_pagata_22ind,template_ivacode_pagata_22ind,1,1,False,-1,-1
22I5,22I5,l10n_it_chart_template_generic,IVA al 22% detraibile al 50%,35,0.22,,True,percent,,,purchase,template_impcode_pagata_22det50,,template_impcode_pagata_22det50,,1,1,False,-1,-1
22I5b,22I5b,l10n_it_chart_template_generic,IVA al 22% detraibile al 50% (D),200,0,22I5,False,balance,1601,1601,purchase,,template_ivacode_pagata_22det50,,template_ivacode_pagata_22det50,1,1,False,-1,-1
22I5a,22I5a,l10n_it_chart_template_generic,IVA al 22% detraibile al 50% (I),100,0.5,22I5,False,percent,,,purchase,,template_ivacode_pagata_22det50ind,,template_ivacode_pagata_22det50ind,1,1,False,-1,-1
<record id="btw_code_chart_root" model="account.tax.code.template">
<field name="name">Nederlands BTW Plan</field>
<field name="parent_id" eval="False"/>
+ <field name="sequence" eval="1"/>
</record>
<!-- De gegevens voor de BTW (gegevens voor omzetbedragen staan verderop) -->
<record id="btw_totaal" model="account.tax.code.template">
<field name="code">B</field>
<field name="name">Gegevens omzetbelasting (BTW)</field>
<field name="parent_id" ref="btw_code_chart_root"/>
+ <field name="sequence" eval="5"/>
</record>
<!-- 1 Leveringen en/of diensten binnenland -->
<record id="btw_code_binnenland" model="account.tax.code.template">
<field name="code">1</field>
<field name="name">Leveringen en/of diensten binnenland (BTW)</field>
<field name="parent_id" ref="btw_totaal"/>
+ <field name="sequence" eval="10"/>
</record>
<record id="btw_code_1a" model="account.tax.code.template">
<field name="code">1a</field>
<field name="parent_id" ref="btw_code_binnenland"/>
<field name="name">Leveringen/diensten belast met 21% (BTW)</field>
+ <field name="sequence" eval="11"/>
</record>
<record id="btw_code_1b" model="account.tax.code.template">
<field name="code">1b</field>
<field name="parent_id" ref="btw_code_binnenland"/>
<field name="name">Leveringen/diensten belast met 6% (BTW)</field>
+ <field name="sequence" eval="12"/>
</record>
<record id="btw_code_1c" model="account.tax.code.template">
<field name="code">1c</field>
<field name="parent_id" ref="btw_code_binnenland"/>
<field name="name">Leveringen/diensten belast met overige tarieven behalve 0% (BTW)</field>
+ <field name="sequence" eval="13"/>
</record>
<record id="btw_code_1d" model="account.tax.code.template">
<field name="code">1d</field>
<field name="parent_id" ref="btw_code_binnenland"/>
<field name="name">Prive-gebruik (BTW)</field>
+ <field name="sequence" eval="14"/>
</record>
<record id="btw_code_1e" model="account.tax.code.template">
<field name="code">1e</field>
<field name="parent_id" ref="btw_code_binnenland"/>
<field name="name">Leveringen/diensten belast met 0% of niet bij u belast (BTW)</field>
+ <field name="sequence" eval="15"/>
</record>
<!-- 2. Verleggingsregeling -->
<record id="btw_code_verlegging" model="account.tax.code.template">
<field name="code">2</field>
<field name="name">Verleggingsregelingen: BTW naar u verlegd (BTW)</field>
<field name="parent_id" ref="btw_totaal"/>
+ <field name="sequence" eval="20"/>
</record>
<record id="btw_code_2a" model="account.tax.code.template">
<field name="code">2a</field>
<field name="parent_id" ref="btw_code_verlegging"/>
<field name="name">Heffing van omzetbelasting is naar u verlegd (BTW)</field>
+ <field name="sequence" eval="21"/>
</record>
<!-- Voor rubriek 3 hoeft geen omzetbelasting aangegeven te worden, wel het omzet bedrag, zie hiervoor verderop
3. Leveringen naar het buitenland -->
<field name="code">4</field>
<field name="name">Leveringen vanuit het buitenland (BTW)</field>
<field name="parent_id" ref="btw_totaal"/>
+ <field name="sequence" eval="40"/>
</record>
<record id="btw_code_4a" model="account.tax.code.template">
<field name="code">4a</field>
<field name="parent_id" ref="btw_code_vanuit_buitenland"/>
<field name="name">Leveringen uit landen buiten de EU (invoer) (BTW)</field>
<field name="sign" eval="-1" />
+ <field name="sequence" eval="41"/>
</record>
<record id="btw_code_4b" model="account.tax.code.template">
<field name="code">4b</field>
<field name="parent_id" ref="btw_code_vanuit_buitenland"/>
<field name="name">Verwerving van goederen uit landen binnen de EU (BTW)</field>
<field name="sign" eval="-1" />
+ <field name="sequence" eval="42"/>
</record>
<!-- 5. Voorbelasting, kleineondernemersregeling, schatting en eindtotaal -->
<record id="btw_code_voorbelasting" model="account.tax.code.template">
<field name="code">5</field>
<field name="name">Voorbelasting, kleineondernemersregeling, schatting en eindtotaal (BTW)</field>
<field name="parent_id" ref="btw_totaal"/>
+ <field name="sequence" eval="50"/>
</record>
<record id="btw_code_5a" model="account.tax.code.template">
<field name="code">5a</field>
<field name="parent_id" ref="btw_code_voorbelasting"/>
<field name="name">Verschuldigde omzetbelasting (rubrieken 1a t/m 4b) (BTW)</field>
+ <field name="sequence" eval="51"/>
</record>
<record id="btw_code_5b" model="account.tax.code.template">
<field name="code">5b</field>
<field eval="-1" name="sign"/>
<field name="parent_id" ref="btw_code_voorbelasting"/>
<field name="name">Voorbelasting (BTW)</field>
+ <field name="sequence" eval="52"/>
</record>
<record id="btw_code_5c" model="account.tax.code.template">
<field name="code">5c</field>
<field name="parent_id" ref="btw_code_voorbelasting"/>
<field name="name">Subtotaal (rubriek 5a min 5b) (BTW)</field>
+ <field name="sequence" eval="53"/>
</record>
<record id="btw_code_5d" model="account.tax.code.template">
<field name="code">5d</field>
<field name="parent_id" ref="btw_code_voorbelasting"/>
<field name="name">Vermindering volgens de kleineondernemersregeling (BTW)</field>
+ <field name="sequence" eval="54"/>
</record>
<record id="btw_code_5e" model="account.tax.code.template">
<field name="code">5e</field>
<field name="parent_id" ref="btw_code_voorbelasting"/>
<field name="name">Schatting vorige aangifte(n) (BTW)</field>
+ <field name="sequence" eval="55"/>
</record>
<record id="btw_code_5f" model="account.tax.code.template">
<field name="code">5f</field>
<field name="parent_id" ref="btw_code_voorbelasting"/>
<field name="name">Schatting deze aangifte (BTW)</field>
+ <field name="sequence" eval="56"/>
</record>
<record id="btw_code_5g" model="account.tax.code.template">
<field name="code">5g</field>
<field name="parent_id" ref="btw_code_voorbelasting"/>
<field name="name">Totaal te betalen/terug te vragen (BTW)</field>
+ <field name="sequence" eval="57"/>
</record>
<!-- De gegevens over de omzet -->
<field name="code">A</field>
<field name="name">Gegevens omzetbedragen</field>
<field name="parent_id" ref="btw_code_chart_root"/>
+ <field name="sequence" eval="1"/>
</record>
<!-- 1 Leveringen en/of diensten binnenland -->
<record id="omz_code_binnenland" model="account.tax.code.template">
<field name="code">1</field>
<field name="name">Leveringen en/of diensten binnenland (omzet)</field>
<field name="parent_id" ref="omz_totaal"/>
+ <field name="sequence" eval="10"/>
</record>
<record id="omz_code_1a" model="account.tax.code.template">
<field name="code">1a</field>
<field name="parent_id" ref="omz_code_binnenland"/>
<field name="name">Leveringen/diensten belast met 21% (omzet)</field>
+ <field name="sequence" eval="11"/>
</record>
<record id="omz_code_1b" model="account.tax.code.template">
<field name="code">1b</field>
<field name="parent_id" ref="omz_code_binnenland"/>
<field name="name">Leveringen/diensten belast met 6% (omzet)</field>
+ <field name="sequence" eval="12"/>
</record>
<record id="omz_code_1c" model="account.tax.code.template">
<field name="code">1c</field>
<field name="parent_id" ref="omz_code_binnenland"/>
<field name="name">Leveringen/diensten belast met overige tarieven behalve 0% (omzet)</field>
+ <field name="sequence" eval="13"/>
</record>
<record id="omz_code_1d" model="account.tax.code.template">
<field name="code">1d</field>
<field name="parent_id" ref="omz_code_binnenland"/>
<field name="name">Prive-gebruik (omzet)</field>
+ <field name="sequence" eval="14"/>
</record>
<record id="omz_code_1e" model="account.tax.code.template">
<field name="code">1e</field>
<field name="parent_id" ref="omz_code_binnenland"/>
<field name="name">Leveringen/diensten belast met 0% of niet bij u belast (omzet)</field>
+ <field name="sequence" eval="15"/>
</record>
<!-- 2. Verleggingsregeling -->
<record id="omz_code_verlegging" model="account.tax.code.template">
<field name="code">2</field>
<field name="name">Verleggingsregelingen: BTW naar u verlegd (omzet)</field>
<field name="parent_id" ref="omz_totaal"/>
+ <field name="sequence" eval="20"/>
</record>
<record id="omz_code_2a" model="account.tax.code.template">
<field name="code">2a</field>
<field name="parent_id" ref="omz_code_verlegging"/>
<field name="name">Heffing van omzetbelasting is naar u verlegd (omzet)</field>
+ <field name="sequence" eval="21"/>
</record>
<!-- 3. Leveringen naar het buitenland -->
<record id="omz_code_naar_buitenland" model="account.tax.code.template">
<field name="code">3</field>
<field name="name">Leveringen naar het buitenland (omzet)</field>
<field name="parent_id" ref="omz_totaal"/>
+ <field name="sequence" eval="30"/>
</record>
<record id="omz_code_3a" model="account.tax.code.template">
<field name="code">3a</field>
<field name="parent_id" ref="omz_code_naar_buitenland"/>
<field name="name">Leveringen naar landen buiten de EU (uitvoer) (omzet)</field>
+ <field name="sequence" eval="31"/>
</record>
<record id="omz_code_3b" model="account.tax.code.template">
<field name="code">3b</field>
<field name="parent_id" ref="omz_code_naar_buitenland"/>
<field name="name">Leveringen naar landen binnen de EU (omzet)</field>
+ <field name="sequence" eval="32"/>
</record>
<record id="omz_code_3c" model="account.tax.code.template">
<field name="code">3c</field>
<field name="parent_id" ref="omz_code_naar_buitenland"/>
<field name="name">Installatie/afstandsverkopen binnen de EU (omzet)</field>
+ <field name="sequence" eval="33"/>
</record>
<!-- 4. Leveringen vanuit het buitenland -->
<record id="omz_code_vanuit_buitenland" model="account.tax.code.template">
<field name="code">4</field>
<field name="name">Leveringen vanuit het buitenland (omzet)</field>
<field name="parent_id" ref="omz_totaal"/>
+ <field name="sequence" eval="40"/>
</record>
<record id="omz_code_4a" model="account.tax.code.template">
<field name="code">4a</field>
<field name="parent_id" ref="omz_code_vanuit_buitenland"/>
<field name="name">Leveringen uit landen buiten de EU (invoer) (omzet)</field>
+ <field name="sequence" eval="41"/>
</record>
<record id="omz_code_4b" model="account.tax.code.template">
<field name="code">4b</field>
<field name="parent_id" ref="omz_code_vanuit_buitenland"/>
<field name="name">Verwerving van goederen uit landen binnen de EU (omzet)</field>
+ <field name="sequence" eval="42"/>
</record>
<!-- Chart template -->
<field name="model">account.chart.template</field>
<field name="key">default</field>
<field name="res_id" ref="pl_chart_template"/>
- <field name="value" ref="base.PLZ"/>
+ <field name="value" ref="base.PLN"/>
</record>
</data>
partner_ids = []
s = ', '.join([decode(message.get(h)) for h in header_fields if message.get(h)])
for email_address in tools.email_split(s):
- related_partners = partner_obj.search(cr, uid, [('email', 'ilike', email_address), ('user_ids', '!=', False)], limit=1, context=context)
+ related_partners = partner_obj.search(cr, uid, [('email', '=ilike', email_address), ('user_ids', '!=', False)], limit=1, context=context)
if not related_partners:
- related_partners = partner_obj.search(cr, uid, [('email', 'ilike', email_address)], limit=1, context=context)
+ related_partners = partner_obj.search(cr, uid, [('email', '=ilike', email_address)], limit=1, context=context)
partner_ids += related_partners
return partner_ids
return True
def _check_product(self, cr, uid, ids, context=None):
- all_prod = []
boms = self.browse(cr, uid, ids, context=context)
- def check_bom(boms):
+ def check_bom(boms, all_prod):
res = True
for bom in boms:
if bom.product_id.id in all_prod:
- res = res and False
- all_prod.append(bom.product_id.id)
+ return False
lines = bom.bom_lines
if lines:
- res = res and check_bom([bom_id for bom_id in lines if bom_id not in boms])
+ res = res and check_bom([bom_id for bom_id in lines if bom_id not in boms], all_prod + [bom.product_id.id])
return res
- return check_bom(boms)
+ return check_bom(boms, [])
_constraints = [
(_check_recursion, 'Error ! You cannot create recursive BoM.', ['parent_id']),
default.update(name=_("%s (copy)") % (bom_data['name']), bom_id=False)
return super(mrp_bom, self).copy_data(cr, uid, id, default, context=context)
+ def unlink(self, cr, uid, ids, context=None):
+ if self.pool['mrp.production'].search(cr, uid, [
+ ('bom_id', 'in', ids), ('state', 'not in', ['done', 'cancel'])
+ ], context=context):
+ raise osv.except_osv(_('Warning!'), _('You can not delete a Bill of Material with running manufacturing orders.\nPlease close or cancel it first.'))
+ return super(mrp_bom, self).unlink(cr, uid, ids, context=context)
+
def rounding(f, r):
# TODO for trunk: log deprecation warning
<field name="field_parent">child_complete_ids</field>
<field name="arch" type="xml">
<tree string="BoM Structure" colors="blue:method">
- <field name="sequence" invisible="1"/>
<field name="name" groups="base.group_no_one"/>
<field name="code"/>
<field name="product_id"/>
<field name="name">mrp_bom multi-company</field>
<field name="model_id" search="[('model','=','mrp.bom')]" model="ir.model"/>
<field name="global" eval="True"/>
- <field name="domain_force">['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
+ <field name="domain_force">['|','|',('company_id.child_ids','child_of',[user.company_id.id]),('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
</record>
<record model="ir.rule" id="mrp_routing_rule">
-
!python {model: mrp.production}: |
order = self.browse(cr, uid, ref("mrp_production_servicetype_mo1"))
- assert order.state == 'confirmed', "Production order should be confirmed."
+ assert order.state == 'ready', "Production order should be ready."
for move_line in order.move_lines:
move_line.action_consume(move_line.product_qty)
-
--- /dev/null
+-
+ Set the current user as multicompany user
+-
+ !context
+ uid: 'stock.multicompany_user'
+
+-
+ check no error on getting default mrp.production values in multicompany setting
+-
+ !python {model: mrp.production}: |
+ location_obj = self.pool.get('stock.location')
+ fields = ['location_src_id', 'location_dest_id']
+ defaults = self.default_get(cr, uid, ['location_id', 'location_dest_id', 'type'], context)
+ log('got defaults: %s', defaults)
+ for field in fields:
+ if defaults.get(field):
+ try:
+ location_obj.check_access_rule(cr, uid, [defaults[field]], 'read', context)
+ except Exception, exc:
+ assert False, "unreadable location %s: %s" % (field, exc)
'Fixed' depicts a situation where the quantity of created byproduct is always equal to the quantity set on the BoM, regardless of how many are created in the production order.\
By opposition, 'Variable' means that the quantity will be computed as\
'(quantity of byproduct set on the BoM / quantity of manufactured product set on the BoM * quantity of manufactured product in the production order.)'"),
- 'bom_id': fields.many2one('mrp.bom', 'BoM'),
+ 'bom_id': fields.many2one('mrp.bom', 'BoM', ondelete='cascade'),
}
_defaults={
'subproduct_type': 'variable',
<field name="global" eval="True" />
<field name="domain_force">['|',('shop_id.company_id','=',False),('shop_id.company_id','child_of',[user.company_id.id])]</field>
</record>
+ <record id="rule_pos_order_report_multi_company" model="ir.rule">
+ <field name="name">Point Of Sale Order Analysis multi-company</field>
+ <field name="model_id" ref="model_report_pos_order"/>
+ <field name="global" eval="True"/>
+ <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
+ </record>
</data>
</openerp>
get_all_prices: function(){
var self = this;
var currency_rounding = this.pos.get('currency').rounding;
- var base = round_pr(this.get_quantity() * this.get_unit_price() * (1.0 - (this.get_discount() / 100.0)), currency_rounding);
+ var base = round_pr(round_pr(this.get_quantity() * this.get_unit_price(), currency_rounding) * (1.0 - (this.get_discount() / 100.0)), currency_rounding);
var totalTax = base;
var totalNoTax = base;
readonly=True, required=True, help="If you encode manually a Procurement, you probably want to use" \
" a make to order method."),
'note': fields.text('Note'),
- 'message': fields.char('Latest error', size=124, help="Exception occurred while computing procurement orders."),
+ 'message': fields.char('Latest error', help="Exception occurred while computing procurement orders."),
'state': fields.selection([
('draft','Draft'),
('cancel','Cancelled'),
ids.update(self.search(cr, user, args + [('default_code',operator,name)], limit=limit, context=context))
if not limit or len(ids) < limit:
# we may underrun the limit because of dupes in the results, that's fine
- ids.update(self.search(cr, user, args + [('name',operator,name)], limit=(limit and (limit-len(ids)) or False) , context=context))
+ ids.update(self.search(cr, user, args + [('name',operator,name), ('id', 'not in', list(ids))], limit=(limit and (limit-len(ids)) or False) , context=context))
ids = list(ids)
elif not ids and operator in expression.NEGATIVE_TERM_OPERATORS:
ids = self.search(cr, user, args + ['&', ('default_code', operator, name), ('name', operator, name)], limit=limit, context=context)
cat_ids=[]
res=[]
self.pricelist = form['price_list']
+ self.partner_id = form['partner_id']
self._set_quantity(form)
pool = pooler.get_pool(self.cr.dbname)
pro_ids=[]
if qty == 0:
val['qty'+str(i)] = 0.0
else:
- val['qty'+str(i)]=self._get_price(self.pricelist, product['id'], qty)
+ val['qty'+str(i)]=self._get_price(self.pricelist, product['id'], qty, partner_id=self.partner_id)
i += 1
products.append(val)
res.append({'name':cat[1],'products': products})
return res
- def _get_price(self, pricelist_id, product_id, qty):
+ def _get_price(self, pricelist_id, product_id, qty, partner_id=None):
sale_price_digits = self.get_digits(dp='Product Price')
pool = pooler.get_pool(self.cr.dbname)
pricelist = self.pool.get('product.pricelist').browse(self.cr, self.uid, [pricelist_id], context=self.localcontext)[0]
- price_dict = pool.get('product.pricelist').price_get(self.cr, self.uid, [pricelist_id], product_id, qty, context=self.localcontext)
+ price_dict = pool.get('product.pricelist').price_get(self.cr, self.uid, [pricelist_id], product_id, qty, partner=partner_id, context=self.localcontext)
+
if price_dict[pricelist_id]:
price = self.formatLang(price_dict[pricelist_id], digits=sale_price_digits, currency_obj=pricelist.currency_id)
else:
_description = 'Price List'
_columns = {
+ 'partner_id': fields.many2one('res.partner', 'Supplier',
+ help='For price lists based on the supplier price, fill in the '
+ 'supplier in question here'),
'price_list': fields.many2one('product.pricelist', 'PriceList', required=True),
'qty1': fields.integer('Quantity-1'),
'qty2': fields.integer('Quantity-2'),
if context is None:
context = {}
datas = {'ids': context.get('active_ids', [])}
- res = self.read(cr, uid, ids, ['price_list','qty1', 'qty2','qty3','qty4','qty5'], context=context)
+ res = self.read(cr, uid, ids, ['price_list','qty1', 'qty2','qty3','qty4','qty5', 'partner_id'], context=context)
res = res and res[0] or {}
res['price_list'] = res['price_list'][0]
+ res['partner_id'] = res['partner_id'] and res['partner_id'][0]
datas['form'] = res
return {
'type': 'ir.actions.report.xml',
<field name="qty3"/>
<field name="qty4"/>
<field name="qty5"/>
+ <field name="partner_id" />
</group>
<footer>
<button name="print_report" string="Print" type="object" class="oe_highlight" />
</form>
</field>
</record>
+ <record id="product_manufacturer_search_form_view" model="ir.ui.view">
+ <field name="name">product.manufacturer.search.form</field>
+ <field name="model">product.product</field>
+ <field name="inherit_id" ref="product.product_search_form_view"/>
+ <field name="arch" type="xml">
+ <field name="name" position="after">
+ <field name="manufacturer" string="Manufacturer" filter_domain="['|', ('manufacturer', 'ilike', self), ('manufacturer_pname', 'ilike', self)]"/>
+ </field>
+ </field>
+ </record>
</data>
</openerp>
'test/project_demo.yml',
'test/project_process.yml',
'test/task_process.yml',
+ 'test/hours_process.yml',
],
'installable': True,
'auto_install': False,
res.update(ids)
return list(res)
+ # Deprecated; the _progress_rate method does not use this anymore
def _get_project_and_children(self, cr, uid, ids, context=None):
""" retrieve all children projects of project ids;
return a dictionary mapping each project to its parent project (or None)
return res
def _progress_rate(self, cr, uid, ids, names, arg, context=None):
- child_parent = self._get_project_and_children(cr, uid, ids, context)
# compute planned_hours, total_hours, effective_hours specific to each project
+ # How this works: the WITH RECURSIVE statement will create an union line
+ # for each parent project with the hours of each child project; the final
+ # SUM(...) ensures we get a total of hours by project.
cr.execute("""
- SELECT project_id, COALESCE(SUM(planned_hours), 0.0),
- COALESCE(SUM(total_hours), 0.0), COALESCE(SUM(effective_hours), 0.0)
- FROM project_task WHERE project_id IN %s AND state <> 'cancelled'
- GROUP BY project_id
- """, (tuple(child_parent.keys()),))
+ WITH RECURSIVE recur_table(project_id,
+ parent_id,
+ planned_hours,
+ total_hours,
+ effective_hours) AS (
+ SELECT project.id,
+ parent.id,
+ COALESCE(task.planned, 0.0),
+ COALESCE(task.total, 0.0),
+ COALESCE(task.effective, 0.0)
+ FROM project_project project
+ LEFT JOIN account_analytic_account account
+ ON project.analytic_account_id = account.id
+ LEFT JOIN project_project parent
+ ON parent.analytic_account_id = account.parent_id
+ LEFT JOIN (SELECT project_id,
+ SUM(planned_hours) as planned,
+ SUM(total_hours) as total,
+ SUM(effective_hours) as effective
+ FROM project_task
+ WHERE state <> 'cancelled'
+ GROUP BY project_id) AS task
+ ON project.id = task.project_id
+ UNION ALL
+ SELECT project.id,
+ parent.id,
+ recur_table.planned_hours,
+ recur_table.total_hours,
+ recur_table.effective_hours
+ FROM project_project project
+ LEFT JOIN account_analytic_account account
+ ON project.analytic_account_id = account.id
+ LEFT JOIN project_project parent
+ ON parent.analytic_account_id = account.parent_id
+ LEFT JOIN (SELECT project_id,
+ SUM(planned_hours) as planned,
+ SUM(total_hours) as total,
+ SUM(effective_hours) as effective
+ FROM project_task
+ WHERE state <> 'cancelled'
+ GROUP BY project_id) AS task
+ ON project.id = task.project_id
+ JOIN recur_table ON project.id = recur_table.parent_id
+ )
+ SELECT project_id,
+ SUM(planned_hours),
+ SUM(total_hours),
+ SUM(effective_hours)
+ FROM recur_table
+ WHERE project_id IN %s
+ GROUP BY project_id
+ """, (tuple(ids),))
# aggregate results into res
- res = dict([(id, {'planned_hours':0.0,'total_hours':0.0,'effective_hours':0.0}) for id in ids])
- for id, planned, total, effective in cr.fetchall():
- # add the values specific to id to all parent projects of id in the result
- while id:
- if id in ids:
- res[id]['planned_hours'] += planned
- res[id]['total_hours'] += total
- res[id]['effective_hours'] += effective
- id = child_parent[id]
+ res = dict([(result_line[0], {'planned_hours': result_line[1],
+ 'total_hours': result_line[2],
+ 'effective_hours': result_line[3]})
+ for result_line in cr.fetchall()])
# compute progress rates
for id in ids:
if res[id]['total_hours']:
- res[id]['progress_rate'] = round(100.0 * res[id]['effective_hours'] / res[id]['total_hours'], 2)
+ res[id]['progress_rate'] = round(100.0 *
+ res[id]['effective_hours'] /
+ res[id]['total_hours'],
+ 2)
else:
res[id]['progress_rate'] = 0.0
return res
res = super(project, self).unlink(cr, uid, ids, context=context)
mail_alias.unlink(cr, uid, alias_ids, context=context)
return res
-
+
def _get_attached_docs(self, cr, uid, ids, field_name, arg, context):
res = {}
attachment = self.pool.get('ir.attachment')
task_attachments = attachment.search(cr, uid, [('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)], context=context, count=True)
res[id] = (project_attachments or 0) + (task_attachments or 0)
return res
-
+
def _task_count(self, cr, uid, ids, field_name, arg, context=None):
if context is None:
context = {}
def attachment_tree_view(self, cr, uid, ids, context):
task_ids = self.pool.get('project.task').search(cr, uid, [('project_id', 'in', ids)])
domain = [
- '|',
+ '|',
'&', ('res_model', '=', 'project.project'), ('res_id', 'in', ids),
'&', ('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)
]
new_name = _("%s (copy)") % (default.get('name', ''))
default.update({'name':new_name})
return super(task, self).copy_data(cr, uid, id, default, context)
-
+
def copy(self, cr, uid, id, default=None, context=None):
if context is None:
context = {}
--- /dev/null
+-
+ First, create the analytic accounts
+-
+ !record {model: account.analytic.account, id: parent_project_account}:
+ name: Parent Account
+ code: PAR
+ type: view
+-
+ !record {model: account.analytic.account, id: child1_project_account}:
+ name: Child Account 1
+ code: C1
+ type: view
+-
+ I create a main project
+-
+ !record {model: project.project, id: project_project_parent}:
+ company_id: base.main_company
+ name: Parent Project
+ user_id: base.user_demo
+ parent_id: all_projects_account
+ analytic_account_id: parent_project_account
+ alias_model: project.task
+-
+ A first child project
+-
+ !record {model: project.project, id: project_project_child_1}:
+ company_id: base.main_company
+ name: Child Project 1
+ user_id: base.user_demo
+ parent_id: parent_project_account
+ analytic_account_id: child1_project_account
+ alias_model: project.task
+-
+ A second child project
+-
+ !record {model: project.project, id: project_project_child_2}:
+ company_id: base.main_company
+ name: Child Project 2
+ user_id: base.user_demo
+ parent_id: parent_project_account
+ alias_model: project.task
+-
+ A child of the first child project
+-
+ !record {model: project.project, id: project_project_child_1_1}:
+ company_id: base.main_company
+ name: Child Project 1 1
+ user_id: base.user_demo
+ parent_id: child1_project_account
+ alias_model: project.task
+-
+ A task for the main project, with 20 hours planned and 20 hours remaining
+-
+ !record {model: project.task, id: project_task_parent_project}:
+ date_start: !eval time.strftime('%Y-05-%d %H:%M:%S')
+ name: Task parent project
+ planned_hours: 20.0
+ remaining_hours: 20.0
+ project_id: project_project_parent
+ user_id: base.user_demo
+-
+ I test the hours
+-
+ !python {model: project.project}: |
+ project = self.browse(cr, uid, ref("project_project_parent"))
+ assert abs(project.planned_hours - 20.0) < 1e-4, "Planned hours are not correct! 20.0 != %s" % project.planned_hours
+ assert abs(project.total_hours - 20.0) < 1e-4, "Total hours are not correct! 20.0 != %s" % project.total_hours
+ assert abs(project.effective_hours - 0.0) < 1e-4, "Effective hours are not correct! 0.0 != %s" % project.effective_hours
+ assert abs(project.progress_rate - 0.0) < 1e-4, "Progress rate is not correct! 0.0 != %s" % project.progress_rate
+-
+ A task for the first child project, with 30 hours planned and 30 hours remaining
+-
+ !record {model: project.task, id: project_task_child_project_1}:
+ date_start: !eval time.strftime('%Y-05-%d %H:%M:%S')
+ name: Task child project 1
+ planned_hours: 30.0
+ remaining_hours: 30.0
+ project_id: project_project_child_1
+ user_id: base.user_demo
+-
+ I test the hours
+-
+ !python {model: project.project}: |
+ project = self.browse(cr, uid, ref("project_project_parent"))
+ assert abs(project.planned_hours - 50.0) < 1e-4, "Planned hours are not correct! 50.0 != %s" % project.planned_hours
+ assert abs(project.total_hours - 50.0) < 1e-4, "Total hours are not correct! 50.0 != %s" % project.total_hours
+ assert abs(project.effective_hours - 0.0) < 1e-4, "Effective hours are not correct! 0.0 != %s" % project.effective_hours
+ assert abs(project.progress_rate - 0.0) < 1e-4, "Progress rate is not correct! 0.0 != %s" % project.progress_rate
+ project = self.browse(cr, uid, ref("project_project_child_1"))
+ assert abs(project.planned_hours - 30.0) < 1e-4, "Planned hours are not correct! 30.0 != %s" % project.planned_hours
+ assert abs(project.total_hours - 30.0) < 1e-4, "Total hours are not correct! 30.0 != %s" % project.total_hours
+ assert abs(project.effective_hours - 0.0) < 1e-4, "Effective hours are not correct! 0.0 != %s" % project.effective_hours
+ assert abs(project.progress_rate - 0.0) < 1e-4, "Progress rate is not correct! 0.0 != %s" % project.progress_rate
+-
+ A task for the second child project, with 40 hours planned and 10 hours remaining
+-
+ !record {model: project.task, id: project_task_child_project_2}:
+ date_start: !eval time.strftime('%Y-05-%d %H:%M:%S')
+ name: Task child project 2
+ planned_hours: 40.0
+ remaining_hours: 10.0
+ project_id: project_project_child_2
+ user_id: base.user_demo
+-
+ I test the hours
+-
+ !python {model: project.project}: |
+ project = self.browse(cr, uid, ref("project_project_parent"))
+ assert abs(project.planned_hours - 90.0) < 1e-4, "Planned hours are not correct! 90.0 != %s" % project.planned_hours
+ assert abs(project.total_hours - 60.0) < 1e-4, "Total hours are not correct! 60.0 != %s" % project.total_hours
+ assert abs(project.effective_hours - 0.0) < 1e-4, "Effective hours are not correct! 0.0 != %s" % project.effective_hours
+ assert abs(project.progress_rate - 0.0) < 1e-4, "Progress rate is not correct! 0.0 != %s" % project.progress_rate
+ project = self.browse(cr, uid, ref("project_project_child_2"))
+ assert abs(project.planned_hours - 40.0) < 1e-4, "Planned hours are not correct! 40.0 != %s" % project.planned_hours
+ assert abs(project.total_hours - 10.0) < 1e-4, "Total hours are not correct! 10.0 != %s" % project.total_hours
+ assert abs(project.effective_hours - 0.0) < 1e-4, "Effective hours are not correct! 0.0 != %s" % project.effective_hours
+ assert abs(project.progress_rate - 0.0) < 1e-4, "Progress rate is not correct! 0.0 != %s" % project.progress_rat
+-
+ A task for the child child project, with 50 hours planned and 50 hours remaining
+-
+ !record {model: project.task, id: project_task_child_project_1_1}:
+ date_start: !eval time.strftime('%Y-05-%d %H:%M:%S')
+ name: Task child project 1 1
+ planned_hours: 50.0
+ remaining_hours: 50.0
+ project_id: project_project_child_1_1
+ user_id: base.user_demo
+-
+ I test the hours
+-
+ !python {model: project.project}: |
+ project = self.browse(cr, uid, ref("project_project_parent"))
+ assert abs(project.planned_hours - 140.0) < 1e-4, "Planned hours are not correct! 140.0 != %s" % project.planned_hours
+ assert abs(project.total_hours - 110.0) < 1e-4, "Total hours are not correct! 110.0 != %s" % project.total_hours
+ assert abs(project.effective_hours - 0.0) < 1e-4, "Effective hours are not correct! 0.0 != %s" % project.effective_hours
+ assert abs(project.progress_rate - 0.0) < 1e-4, "Progress rate is not correct! 0.0 != %s" % project.progress_rate
+ project = self.browse(cr, uid, ref("project_project_child_1"))
+ assert abs(project.planned_hours - 80.0) < 1e-4, "Planned hours are not correct! 80.0 != %s" % project.planned_hours
+ assert abs(project.total_hours - 80.0) < 1e-4, "Total hours are not correct! 80.0 != %s" % project.total_hours
+ assert abs(project.effective_hours - 0.0) < 1e-4, "Effective hours are not correct! 0.0 != %s" % project.effective_hours
+ assert abs(project.progress_rate - 0.0) < 1e-4, "Progress rate is not correct! 0.0 != %s" % project.progress_rate
+ project = self.browse(cr, uid, ref("project_project_child_1_1"))
+ assert abs(project.planned_hours - 50.0) < 1e-4, "Planned hours are not correct! 50.0 != %s" % project.planned_hours
+ assert abs(project.total_hours - 50.0) < 1e-4, "Total hours are not correct! 50.0 != %s" % project.total_hours
+ assert abs(project.effective_hours - 0.0) < 1e-4, "Effective hours are not correct! 0.0 != %s" % project.effective_hours
+ assert abs(project.progress_rate - 0.0) < 1e-4, "Progress rate is not correct! 0.0 != %s" % project.progress_rate
+-
+ I create a task work on child 1 task
+-
+ !record {model: project.task.work, id: project_child_1_work}:
+ name: test work
+ hours: 10.0
+ task_id: project_task_child_project_1
+ user_id: base.user_demo
+-
+ and refresh the task to account for it
+-
+ !record {model: project.task, id: project_task_child_project_1}:
+ planned_hours: 20.0
+-
+ I test the hours
+-
+ !python {model: project.project}: |
+ project = self.browse(cr, uid, ref("project_project_parent"))
+ assert abs(project.planned_hours - 130.0) < 1e-4, "Planned hours are not correct! 130.0 != %s" % project.planned_hours
+ assert abs(project.total_hours - 110.0) < 1e-4, "Total hours are not correct! 110.0 != %s" % project.total_hours
+ assert abs(project.effective_hours - 10.0) < 1e-4, "Effective hours are not correct! 10.0 != %s" % project.effective_hours
+ assert abs(project.progress_rate - 9.09) < 1e-4, "Progress rate is not correct! 9.09 != %s" % project.progress_rate
+ project = self.browse(cr, uid, ref("project_project_child_1"))
+ assert abs(project.planned_hours - 70.0) < 1e-4, "Planned hours are not correct! 70.0 != %s" % project.planned_hours
+ assert abs(project.total_hours - 80.0) < 1e-4, "Total hours are not correct! 80.0 != %s" % project.total_hours
+ assert abs(project.effective_hours - 10.0) < 1e-4, "Effective hours are not correct! 10.0 != %s" % project.effective_hours
+ assert abs(project.progress_rate - 12.5) < 1e-4, "Progress rate is not correct! 12.5 != %s" % project.progress_rate
+
res = super(project_issue, self).message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, content_subtype=content_subtype, **kwargs)
- if thread_id:
+ if thread_id and subtype:
self.write(cr, uid, thread_id, {'date_action_last': time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
return res
res['product_uom_id'] = emp.product_id.uom_id.id
return res
- def create(self, cr, uid, vals, *args, **kwargs):
- timesheet_obj = self.pool.get('hr.analytic.timesheet')
- task_obj = self.pool.get('project.task')
- uom_obj = self.pool.get('product.uom')
+ def _create_analytic_entries(self, cr, uid, vals, context):
+ """Create the hr analytic timesheet from project task work"""
+ timesheet_obj = self.pool['hr.analytic.timesheet']
+ task_obj = self.pool['project.task']
vals_line = {}
+ timeline_id = False
+ acc_id = False
+
+ task_obj = task_obj.browse(cr, uid, vals['task_id'], context=context)
+ result = self.get_user_related_details(cr, uid, vals.get('user_id', uid))
+ vals_line['name'] = '%s: %s' % (tools.ustr(task_obj.name), tools.ustr(vals['name'] or '/'))
+ vals_line['user_id'] = vals['user_id']
+ vals_line['product_id'] = result['product_id']
+ if vals.get('date'):
+ vals_line['date' ] = vals['date'][:10]
+
+ # Calculate quantity based on employee's product's uom
+ vals_line['unit_amount'] = vals['hours']
+
+ default_uom = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.project_time_mode_id.id
+ if result['product_uom_id'] != default_uom:
+ vals_line['unit_amount'] = self.pool['product.uom']._compute_qty(cr, uid, default_uom, vals['hours'], result['product_uom_id'])
+ acc_id = task_obj.project_id and task_obj.project_id.analytic_account_id.id or acc_id
+ if acc_id:
+ vals_line['account_id'] = acc_id
+ res = timesheet_obj.on_change_account_id(cr, uid, False, acc_id)
+ if res.get('value'):
+ vals_line.update(res['value'])
+ vals_line['general_account_id'] = result['general_account_id']
+ vals_line['journal_id'] = result['journal_id']
+ vals_line['amount'] = 0.0
+ vals_line['product_uom_id'] = result['product_uom_id']
+ amount = vals_line['unit_amount']
+ prod_id = vals_line['product_id']
+ unit = False
+ timeline_id = timesheet_obj.create(cr, uid, vals=vals_line, context=context)
+
+ # Compute based on pricetype
+ amount_unit = timesheet_obj.on_change_unit_amount(cr, uid, timeline_id,
+ prod_id, amount, False, unit, vals_line['journal_id'], context=context)
+ if amount_unit and 'amount' in amount_unit.get('value',{}):
+ updv = { 'amount': amount_unit['value']['amount'] }
+ timesheet_obj.write(cr, uid, [timeline_id], updv, context=context)
+
+ return timeline_id
+
+ def create(self, cr, uid, vals, *args, **kwargs):
context = kwargs.get('context', {})
if not context.get('no_analytic_entry',False):
- task_obj = task_obj.browse(cr, uid, vals['task_id'])
- result = self.get_user_related_details(cr, uid, vals.get('user_id', uid))
- vals_line['name'] = '%s: %s' % (tools.ustr(task_obj.name), tools.ustr(vals['name'] or '/'))
- vals_line['user_id'] = vals['user_id']
- vals_line['product_id'] = result['product_id']
- vals_line['date'] = vals['date'][:10]
-
- # Calculate quantity based on employee's product's uom
- vals_line['unit_amount'] = vals['hours']
-
- default_uom = self.pool.get('res.users').browse(cr, uid, uid).company_id.project_time_mode_id.id
- if result['product_uom_id'] != default_uom:
- vals_line['unit_amount'] = uom_obj._compute_qty(cr, uid, default_uom, vals['hours'], result['product_uom_id'])
- acc_id = task_obj.project_id and task_obj.project_id.analytic_account_id.id or False
- if acc_id:
- vals_line['account_id'] = acc_id
- res = timesheet_obj.on_change_account_id(cr, uid, False, acc_id)
- if res.get('value'):
- vals_line.update(res['value'])
- vals_line['general_account_id'] = result['general_account_id']
- vals_line['journal_id'] = result['journal_id']
- vals_line['amount'] = 0.0
- vals_line['product_uom_id'] = result['product_uom_id']
- amount = vals_line['unit_amount']
- prod_id = vals_line['product_id']
- unit = False
- timeline_id = timesheet_obj.create(cr, uid, vals=vals_line, context=context)
-
- # Compute based on pricetype
- amount_unit = timesheet_obj.on_change_unit_amount(cr, uid, timeline_id,
- prod_id, amount, False, unit, vals_line['journal_id'], context=context)
- if amount_unit and 'amount' in amount_unit.get('value',{}):
- updv = { 'amount': amount_unit['value']['amount'] }
- timesheet_obj.write(cr, uid, [timeline_id], updv, context=context)
- vals['hr_analytic_timesheet_id'] = timeline_id
+ vals['hr_analytic_timesheet_id'] = self._create_analytic_entries(cr, uid, vals, context=context)
return super(project_work,self).create(cr, uid, vals, *args, **kwargs)
def write(self, cr, uid, ids, vals, context=None):
def write(self, cr, uid, ids, vals, context=None):
if context is None:
context = {}
+ task_work_obj = self.pool['project.task.work']
+ acc_id = False
+ missing_analytic_entries = {}
+
if vals.get('project_id',False) or vals.get('name',False):
vals_line = {}
hr_anlytic_timesheet = self.pool.get('hr.analytic.timesheet')
if len(task_obj.work_ids):
for task_work in task_obj.work_ids:
if not task_work.hr_analytic_timesheet_id:
+ if acc_id :
+ # missing timesheet activities to generate
+ missing_analytic_entries[task_work.id] = {
+ 'name' : task_work.name,
+ 'user_id' : task_work.user_id.id,
+ 'date' : task_work.date and task_work.date[:10] or False,
+ 'account_id': acc_id,
+ 'hours' : task_work.hours,
+ 'task_id' : task_obj.id
+ }
continue
line_id = task_work.hr_analytic_timesheet_id.id
if vals.get('project_id',False):
vals_line['account_id'] = acc_id
if vals.get('name',False):
- vals_line['name'] = '%s: %s' % (tools.ustr(vals['name']), tools.ustr(task_work.name) or '/')
+ vals_line['name'] = '%s: %s' % (tools.ustr(vals['name']), tools.ustr(task_work.name or '/'))
hr_anlytic_timesheet.write(cr, uid, [line_id], vals_line, {})
- return super(task,self).write(cr, uid, ids, vals, context)
+
+ res = super(task,self).write(cr, uid, ids, vals, context)
+
+ for task_work_id, analytic_entry in missing_analytic_entries.items():
+ timeline_id = task_work_obj._create_analytic_entries(cr, uid, analytic_entry, context=context)
+ task_work_obj.write(cr, uid, task_work_id, {'hr_analytic_timesheet_id' : timeline_id}, context=context)
+
+ return res
task()
if not acc_id:
acc_id = po_line.product_id.categ_id.property_account_expense_categ.id
if not acc_id:
- raise osv.except_osv(_('Error!'), _('Define expense account for this company: "%s" (id:%d).') % (po_line.product_id.name, po_line.product_id.id,))
+ raise osv.except_osv(_('Error!'), _('Define expense account for this product: "%s" (id:%d).') % (po_line.product_id.name, po_line.product_id.id,))
else:
acc_id = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category', context=context).id
fpos = po_line.order_id.fiscal_position or False
if porder.notes:
order_infos['notes'] = (order_infos['notes'] or '') + ('\n%s' % (porder.notes,))
if porder.origin:
- order_infos['origin'] = (order_infos['origin'] or '') + ' ' + porder.origin
+ if not porder.origin in order_infos['origin'] and not order_infos['origin'] in porder.origin:
+ order_infos['origin'] = (order_infos['origin'] or '') + ' ' + porder.origin
for order_line in porder.order_line:
line_key = make_key(order_line, ('name', 'date_planned', 'taxes_id', 'price_unit', 'product_id', 'move_dest_id', 'account_analytic_id'))
return {'cost': cost, 'currency': company_currency}
return super(stock_partial_picking, self)._product_cost_for_average_update(cr, uid, move)
+ def _partial_move_for(self, cr, uid, move, context=None):
+ partial_move = super(stock_partial_picking, self)._partial_move_for(cr, uid, move, context=context)
+ if move.picking_id.purchase_id and move.purchase_line_id:
+ pur_currency = move.purchase_line_id.order_id.currency_id.id
+ partial_move.update({
+ 'currency': pur_currency,
+ 'cost': move.purchase_line_id.price_unit
+ })
+ return partial_move
+
def __get_help_text(self, cursor, user, picking_id, context=None):
picking = self.pool.get('stock.picking').browse(cursor, user, picking_id, context=context)
if picking.purchase_id:
<field name="arch" type="xml">
<xpath expr="//div[contains(@class, 'oe_title')]" position="before">
<div class="oe_right oe_button_box" name="buttons">
+ <field name="picking_ids" invisible="1"/>
<button type="object"
name="view_picking"
- string="Incoming Shipments" states="approved"/>
+ string="Incoming Shipments"
+ attrs="{'invisible': ['|',('picking_ids','=',False),('picking_ids','=',[])]}"/>
<button type="object" name="invoice_open"
string="Invoices" attrs="{'invisible': [('state', '=', 'draft')]}"/>
</div>
'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order.requisition'),
})
return super(purchase_requisition, self).copy(cr, uid, id, default, context)
-
+
+ def unlink(self, cr, uid, ids, context=None):
+ """
+ Deletes Requisition and related RFQs
+ """
+ if context is None: context = {}
+ purchase_obj = self.pool.get('purchase.order')
+ purchase_ids = self._requisition_procurement_cancel(cr, uid, ids, context=context)
+ if purchase_ids:
+ purchase_obj.unlink(cr, uid, purchase_ids, context=context)
+ return super(purchase_requisition, self).unlink(cr, uid, ids, context=context)
+
+ def _requisition_procurement_cancel(self, cr, uid, ids, context=None):
+ """
+ Cancels procurement order related to requisition
+ @param ids: requisition ids
+ @return: Returns purchase orders associated with requisition if any
+ """
+ if context is None: context = {}
+ purchase_ids = []
+ procurement_ids = []
+ procurement_obj = self.pool.get('procurement.order')
+ for requisition in self.browse(cr, uid, ids, context=context):
+ purchase_ids.extend(purchase.id for purchase in requisition.purchase_ids)
+ if requisition.state == 'cancel':
+ continue
+ procurement_ids.extend(procurement_obj.search(cr, uid,
+ [('requisition_id', '=', requisition.id)], context=context))
+ if procurement_ids:
+ procurement_obj.action_cancel(cr, uid, procurement_ids)
+ return purchase_ids
+
def tender_cancel(self, cr, uid, ids, context=None):
+ if context is None: context = {}
purchase_order_obj = self.pool.get('purchase.order')
- for purchase in self.browse(cr, uid, ids, context=context):
- for purchase_id in purchase.purchase_ids:
- if str(purchase_id.state) in('draft'):
- purchase_order_obj.action_cancel(cr,uid,[purchase_id.id])
+ purchase_ids = self._requisition_procurement_cancel(cr, uid, ids, context=context)
+ if purchase_ids:
+ purchase_order_obj.action_cancel(cr, uid, purchase_ids, context=context)
procurement_ids = self.pool['procurement.order'].search(cr, uid, [('requisition_id', 'in', ids)], context=context)
self.pool['procurement.order'].action_done(cr, uid, procurement_ids)
- return self.write(cr, uid, ids, {'state': 'cancel'})
+ return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
def tender_in_progress(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state':'in_progress'} ,context=context)
I cancel requisition.
-
!python {model: purchase.requisition}: |
- self.tender_cancel(cr, uid, [ref("requisition1")])
+ self.tender_cancel(cr, uid, [ref("requisition2")])
-
I check requisition after cancelled.
-
- !assert {model: purchase.requisition, id: requisition1}:
+ !assert {model: purchase.requisition, id: requisition2}:
- state == 'cancel'
-
I reset requisition as "New".
-
!python {model: purchase.requisition}: |
- self.tender_reset(cr, uid, [ref('requisition1')])
+ self.tender_reset(cr, uid, [ref('requisition2')])
-
I duplicate requisition.
-
!python {model: purchase.requisition}: |
- self.copy(cr, uid, ref('requisition1'))
+ self.copy(cr, uid, ref('requisition2'))
-
I delete requisition.
-
- !python {model: purchase.order}: |
- self.unlink(cr, uid, [ref("requisition1")])
+ !python {model: purchase.requisition}: |
+ self.unlink(cr, uid, [ref("requisition2")])
(data, format) = netsvc.LocalService('report.purchase.requisition').create(cr, uid, [ref('purchase_requisition.requisition1')], {}, {})
if tools.config['test_report_directory']:
file(os.path.join(tools.config['test_report_directory'], 'purchase_requisition-purchase_requisition_report.'+format), 'wb+').write(data)
+-
+ I check that I cannot cancel the requisision
+-
+ !python {model: purchase.requisition}: |
+ from openerp.osv.osv import except_osv
+ try:
+ self.tender_cancel(cr, uid, [ref("requisition1")])
+ except except_osv, exc:
+ assert exc.args == (u'Unable to cancel this purchase order.', u'First cancel all receptions related to this purchase order.')
+ else:
+ assert False, 'tender_cancel should have failed'
- product_id: product.product_product_9
product_qty: 10.0
product_uom_id: product.product_uom_unit
-
+-
+ !record {model: purchase.requisition, id: requisition2}:
+ exclusive: exclusive
+ line_ids:
+ - product_id: product.product_product_13
+ product_qty: 10.0
+ product_uom_id: product.product_uom_unit
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
-# Author : Nicolas Bessi (Camptocamp)
+# Authors : Nicolas Bessi (Camptocamp)
+# Yannick Vaucher (Camptocamp)
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
##############################################################################
from openerp import pooler
+from openerp.osv import orm
+from tools.translate import _
class WebKitHelper(object):
"""Set of usefull report helper"""
self.uid = uid
self.pool = pooler.get_pool(self.cursor.dbname)
self.report_id = report_id
-
- def embed_image(self, type, img, width=0, height=0) :
+
+ def embed_image(self, type, img, width=0, height=0, unit="px"):
"Transform a DB image into an embedded HTML image"
if width :
- width = 'width="%spx"'%(width)
+ width = 'width: %s%s;'%(width, unit)
else :
width = ' '
if height :
- height = 'height="%spx"'%(height)
+ height = 'height: %s%s;'%(height, unit)
else :
height = ' '
- toreturn = '<img %s %s src="data:image/%s;base64,%s" />'%(
+ toreturn = '<img style="%s%s" src="data:image/%s;base64,%s" />'%(
width,
height,
type,
return toreturn
- def get_logo_by_name(self, name):
+ def get_logo_by_name(self, name, company_id=None):
"""Return logo by name"""
header_obj = self.pool.get('ir.header_img')
- header_img_id = header_obj.search(
- self.cursor,
- self.uid,
- [('name','=',name)]
- )
+ domain = [('name','=',name)]
+ if company_id:
+ domain.append(('company_id', '=', company_id))
+ header_img_id = header_obj.search(self.cursor,
+ self.uid,
+ domain)
if not header_img_id :
- return u''
+ msg = _("No header image named '%s' found.") % name
+ if company_id:
+ company_obj = self.pool.get('res.company')
+ company = company_obj.browse(self.cursor, self.uid, company_id)
+ msg = _("No header image named '%s' found for company %s.") % (name, company.name)
+ raise orm.except_orm('Error', msg)
if isinstance(header_img_id, list):
header_img_id = header_img_id[0]
head = header_obj.browse(self.cursor, self.uid, header_img_id)
return (head.img, head.type)
- def embed_logo_by_name(self, name, width=0, height=0):
+ def embed_logo_by_name(self, name, width=0, height=0, unit="px", company_id=None):
"""Return HTML embedded logo by name"""
- img, type = self.get_logo_by_name(name)
- return self.embed_image(type, img, width, height)
+ img, type = self.get_logo_by_name(name, company_id=company_id)
+ return self.embed_image(type, img, width, height, unit)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
body_mako_tpl = mako_template(template)
helper = WebKitHelper(cursor, uid, report_xml.id, context)
if report_xml.precise_mode:
+ objs = parser_instance.localcontext['objects']
for obj in objs:
parser_instance.localcontext['objects'] = [obj]
try :
return {
'name': pick_name,
'origin': order.name,
- 'date': self.date_to_datetime(cr, uid, order.date_order, context),
+ 'date': self.date_to_datetime(cr, uid, order.date_confirm, context),
'type': 'out',
'state': 'auto',
'move_type': order.picking_policy,
if line.state == 'done':
continue
- date_planned = self._get_date_planned(cr, uid, order, line, order.date_order, context=context)
+ date_planned = self._get_date_planned(cr, uid, order, line, order.date_confirm, context=context)
if line.product_id:
if line.product_id.type in ('product', 'consu'):
<xpath expr="//button[@name='action_view_invoice']" position="after">
<field name="picking_ids" invisible="1"/>
<button name="action_view_delivery" string="View Delivery Order" type="object" class="oe_highlight"
- attrs="{'invisible': ['|','|','|',('picking_ids','=',False),('picking_ids','=',[]), ('state', 'not in', ('progress','manual')),('shipped','=',True)]}" groups="base.group_user"/>
+ attrs="{'invisible': ['|',('picking_ids','=',False),('picking_ids','=',[])]}" groups="base.group_user"/>
</xpath>
<xpath expr="//button[@name='action_cancel']" position="after">
<button name="ship_cancel" states="shipping_except" string="Cancel Order"/>
values = super(stock_move, self)._prepare_chained_picking(cr, uid, picking_name, picking, picking_type, moves_todo, context=context)
if picking.sale_id:
values['sale_id'] = picking.sale_id.id
+ if values.get('type') == 'out' and picking.sale_id.order_policy == 'picking':
+ values['invoice_state'] = '2binvoiced'
+ picking.write({'invoice_state': 'none'})
return values
class stock_picking(osv.osv):
{
'name': 'Warehouse Management',
- 'version': '1.1',
+ 'version': '1.1.1',
'author': 'OpenERP SA',
'summary': 'Inventory, Logistic, Storage',
'description' : """
'test/opening_stock.yml',
'test/shipment.yml',
'test/stock_report.yml',
+ 'test/stock_move_chain_validation.yml',
'test/setlast_tracking.yml',
],
'installable': True,
--- /dev/null
+__name__ = ("update internal picking sequence code and sequence")
+
+def migrate(cr, version):
+ old_type = 'stock.picking'
+ new_type = 'stock.picking.internal'
+ cr.execute ("UPDATE ir_sequence_type SET code=%(newtype)s WHERE code=%(oldtype)s",
+ {'newtype': new_type,
+ 'oldtype': old_type})
+ cr.execute ("UPDATE ir_sequence SET code=%(newtype)s WHERE code=%(oldtype)s",
+ {'newtype': new_type,
+ 'oldtype': old_type})
<div attrs="{'invisible':[('type','=','service')]}">
<field name="produce_delay" class="oe_inline"/> days
</div>
- <field name="active"/>
</group>
</group>
<xpath expr="//group[@string='Sale Conditions']" position="inside">
<field name="sale_delay" class="oe_inline"/> days
</div>
</xpath>
+ <group name="status" position="inside" version="7.0">
+ <field name="active"/>
+ </group>
<group name="status" position="before" version="7.0">
<group string="Stock and Expected Variations" attrs="{'invisible': [('type', '=', 'service')]}" groups="base.group_user">
<label for="qty_available"/>
class report_stock_lines_date(osv.osv):
_name = "report.stock.lines.date"
- _description = "Dates of Inventories"
+ _description = "Dates of Inventories and latest Moves"
_auto = False
_order = "date"
_columns = {
- 'id': fields.integer('Inventory Line Id', readonly=True),
+ 'id': fields.integer('Product Id', readonly=True),
'product_id': fields.many2one('product.product', 'Product', readonly=True, select=True),
- 'date': fields.datetime('Latest Inventory Date'),
+ 'date': fields.datetime('Date of latest Inventory', readonly=True),
+ 'move_date': fields.datetime('Date of latest Stock Move', readonly=True),
+ "active" : fields.boolean("Active", readonly=True),
}
def init(self, cr):
drop_view_if_exists(cr, 'report_stock_lines_date')
select
p.id as id,
p.id as product_id,
- max(s.date) as date
+ max(s.date) as date,
+ max(m.date) as move_date,
+ p.active as active
from
product_product p
- left outer join stock_inventory_line l on (p.id=l.product_id)
- left join stock_inventory s on (l.inventory_id=s.id)
- and s.state = 'done'
- where p.active='true'
+ left join (
+ stock_inventory_line l
+ inner join stock_inventory s on (l.inventory_id=s.id and s.state = 'done')
+ ) on (p.id=l.product_id)
+ left join stock_move m on (m.product_id=p.id and m.state = 'done')
group by p.id
)""")
<tree string="Dates of Inventories" create="false">
<field name="product_id"/>
<field name="date" />
+ <field name="move_date"/>
</tree>
</field>
</record>
<field name="name">report.stock.lines.date.search</field>
<field name="model">report.stock.lines.date</field>
<field name="arch" type="xml">
- <search string="Dates of Inventories">
+ <search string="Dates of Inventories & Moves">
<field name="date"/>
<filter icon="terp-accessories-archiver" name="stockable" string="Stockable" domain="[('product_id.type','=', 'product')]"/>
<filter icon="terp-accessories-archiver" string="Consumable" domain="[('product_id.type','=', 'consu')]"/>
<separator/>
- <filter icon="terp-accessories-archiver-minus" string="Non Inv" domain="[('date','=', False)]"/>
+ <filter icon="terp-accessories-archiver-minus" string="No Inventory yet" domain="[('date','=', False)]"/>
+ <filter icon="terp-accessories-archiver-minus" string="No Stock Move yet" domain="[('move_date','=', False)]"/>
<field name="product_id"/>
</search>
</field>
<field name="name">report.stock.lines.date.form</field>
<field name="model">report.stock.lines.date</field>
<field name="arch" type="xml">
- <form string="Dates of Inventories" version="7.0">
+ <form string="Dates of Inventories & Moves" version="7.0">
<group>
<field name="product_id"/>
<field name="date"/>
+ <field name="move_date"/>
</group>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="action_stock_line_date">
- <field name="name">Last Product Inventories</field>
+ <field name="name">Latest Inventories & Moves</field>
<field name="res_model">report.stock.lines.date</field>
<field name="view_type">form</field>
<field name="context">{'search_default_stockable':1}</field>
<field name="view_mode">tree,form</field>
- <field name="help">Display the last inventories done on your products and easily sort them with specific filtering criteria. If you do frequent and partial inventories, you need this report in order to ensure that the stock of each product is controlled at least once a year.</field>
+ <field name="help">Display the latest Inventories and Moves done on your products and easily sort them with specific filtering criteria. If you do frequent and partial inventories, you need this report in order to ensure that the stock of each product is controlled at least once a year. This also lets you find out which products have seen little move lately and may deserve special measures (discounted sale, quality control...)</field>
</record>
<menuitem parent="next_id_61" action="action_stock_line_date" id="menu_report_stock_line_date" sequence="2"/>
res[pick]['min_date'] = dt1
res[pick]['max_date'] = dt2
return res
+
+ def _get_stock_move_changes(self, cr, uid, ids, context=None):
+ '''Return the ids of pickings that should change, due to changes
+ in stock moves.'''
+ move_pool = self.pool['stock.move']
+ picking_ids = set()
+ for move_obj in move_pool.browse(cr, uid, ids, context=context):
+ if move_obj.picking_id:
+ picking_ids.add(move_obj.picking_id.id)
+ return list(picking_ids)
def create(self, cr, user, vals, context=None):
if ('name' not in vals) or (vals.get('name')=='/'):
- seq_obj_name = self._name
+ seq_obj_name = 'stock.picking.%s' % vals.get('type', 'internal')
vals['name'] = self.pool.get('ir.sequence').get(cr, user, seq_obj_name)
new_id = super(stock_picking, self).create(cr, user, vals, context)
return new_id
* Transferred: has been processed, can't be modified or cancelled anymore\n
* Cancelled: has been cancelled, can't be confirmed anymore"""
),
- 'min_date': fields.function(get_min_max_date, fnct_inv=_set_minimum_date, multi="min_max_date",
- store=True, type='datetime', string='Scheduled Time', select=1, help="Scheduled time for the shipment to be processed"),
+ 'min_date': fields.function(
+ get_min_max_date,
+ fnct_inv=_set_minimum_date, multi='min_max_date',
+ store={
+ 'stock.move': (
+ _get_stock_move_changes,
+ ['date_expected'], 10,
+ )
+ },
+ type='datetime', string='Scheduled Time', select=True,
+ help="Scheduled time for the shipment to be processed"
+ ),
'date': fields.datetime('Creation Date', help="Creation date, usually the time of the order.", select=True, states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}),
'date_done': fields.datetime('Date of Transfer', help="Date of Completion", states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}),
- 'max_date': fields.function(get_min_max_date, fnct_inv=_set_maximum_date, multi="min_max_date",
- store=True, type='datetime', string='Max. Expected Date', select=2),
+ 'max_date': fields.function(
+ get_min_max_date,
+ fnct_inv=_set_maximum_date, multi='min_max_date',
+ store={
+ 'stock.move': (
+ _get_stock_move_changes,
+ ['date_expected'], 10,
+ )
+ },
+ type='datetime', string='Max. Expected Date', select=True
+ ),
'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'product_id': fields.related('move_lines', 'product_id', type='many2one', relation='product.product', string='Product'),
'auto_picking': fields.boolean('Auto-Picking', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}),
@return: True
"""
pickings = self.browse(cr, uid, ids, context=context)
- self.write(cr, uid, ids, {'state': 'confirmed'})
+ to_update = []
+ for pick in pickings:
+ if pick.state != 'confirmed':
+ to_update.append(pick.id)
+ if to_update:
+ self.write(cr, uid, to_update, {'state': 'confirmed'})
todo = []
for picking in pickings:
for r in picking.move_lines:
""" Cancels picking and moves.
@return: True
"""
- wf_service = netsvc.LocalService("workflow")
for pick in self.browse(cr, uid, ids):
move_ids = [x.id for x in pick.move_lines]
self.pool.get('stock.move').cancel_assign(cr, uid, move_ids)
- wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
return True
def action_assign_wkf(self, cr, uid, ids, context=None):
""" Changes picking state to assigned.
@return: True
"""
- self.write(cr, uid, ids, {'state': 'assigned'})
+ to_update = []
+ for pick in self.browse(cr, uid, ids, context=context):
+ if pick.state != 'assigned':
+ to_update.append(pick.id)
+ if to_update:
+ self.write(cr, uid, to_update, {'state': 'assigned'})
return True
def test_finished(self, cr, uid, ids):
if all([x.state != 'waiting' for x in pick.move_lines]):
return True
for move in pick.move_lines:
+ if (move.state) == 'waiting':
+ move.check_assign()
if (move.state in ('confirmed', 'draft')) and (mt == 'one'):
return False
if (mt == 'direct') and (move.state == 'assigned') and (move.product_qty):
'product_uos_qty': uos_qty[move.id],
'picking_id' : new_picking,
'state': 'assigned',
- 'move_dest_id': False,
+ 'move_dest_id': move.move_dest_id.id,
'price_unit': move.price_unit,
'product_uom': product_uoms[move.id]
}
# fix for bug lp:707031
# called write of related picking because changing move availability does
# not trigger workflow of picking in order to change the state of picking
+ seen = set()
wf_service = netsvc.LocalService('workflow')
for move in self.browse(cr, uid, ids, context):
- if move.picking_id:
+ if move.picking_id and move.picking_id.id not in seen:
wf_service.trg_write(uid, 'stock.picking', move.picking_id.id, cr)
+ seen.add(move.picking_id.id)
return True
#
pickings[move.picking_id.id] = 1
r = res.pop(0)
product_uos_qty = self.pool.get('stock.move').onchange_quantity(cr, uid, [move.id], move.product_id.id, r[0], move.product_id.uom_id.id, move.product_id.uos_id.id)['value']['product_uos_qty']
- cr.execute('update stock_move set location_id=%s, product_qty=%s, product_uos_qty=%s where id=%s', (r[1], r[0],product_uos_qty, move.id))
+ move.write({
+ 'location_id': r[1],
+ 'product_qty': r[0],
+ 'product_uos_qty': product_uos_qty,
+ })
while res:
r = res.pop(0)
context = {}
src_company_ctx = dict(context,force_company=move.location_id.company_id.id)
dest_company_ctx = dict(context,force_company=move.location_dest_id.company_id.id)
+ # do not take the company of the one of the user
+ # used to select the correct period
+ company_ctx = dict(context, company_id=move.company_id.id)
account_moves = []
# Outgoing moves (or cross-company output part)
if move.location_id.company_id \
{
'journal_id': j_id,
'line_id': move_lines,
- 'ref': move.picking_id and move.picking_id.name}, context=context)
+ 'company_id': move.company_id.id,
+ 'ref': move.picking_id and move.picking_id.name}, context=company_ctx)
def action_done(self, cr, uid, ids, context=None):
""" Makes the move done and if all moves are done, it will finish the picking.
todo.append(move.id)
if todo:
self.action_confirm(cr, uid, todo, context=context)
- todo = []
for move in self.browse(cr, uid, ids, context=context):
if move.state in ['done','cancel']:
self._create_product_valuation_moves(cr, uid, move, context=context)
if move.state not in ('confirmed','done','assigned'):
- todo.append(move.id)
-
- if todo:
- self.action_confirm(cr, uid, todo, context=context)
-
- self.write(cr, uid, move_ids, {'state': 'done', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
- for id in move_ids:
- wf_service.trg_trigger(uid, 'stock.move', id, cr)
-
+ self.action_confirm(cr, uid, [move.id], context=context)
+ self.write(cr, uid, [move.id],
+ {'state': 'done',
+ 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)},
+ context=context)
for pick_id in picking_ids:
wf_service.trg_write(uid, 'stock.picking', pick_id, cr)
debit_line_vals = {
'name': move.name,
'product_id': move.product_id and move.product_id.id or False,
+ 'product_uom_id': move.product_uom and move.product_uom.id or False,
'quantity': move.product_qty,
'ref': move.picking_id and move.picking_id.name or False,
'date': time.strftime('%Y-%m-%d'),
credit_line_vals = {
'name': move.name,
'product_id': move.product_id and move.product_id.id or False,
+ 'product_uom_id': move.product_uom and move.product_uom.id or False,
'quantity': move.product_qty,
'ref': move.picking_id and move.picking_id.name or False,
'date': time.strftime('%Y-%m-%d'),
'product_uos_qty': product_qty,
'picking_id' : move.picking_id.id,
'state': 'assigned',
- 'move_dest_id': False,
+ 'move_dest_id': move.move_dest_id.id,
'price_unit': move.price_unit,
}
prodlot_id = prodlot_ids[move.id]
_name = "stock.inventory.line"
_description = "Inventory Line"
_rec_name = "inventory_id"
+ _order = "inventory_id, location_name, product_code, product_name, prodlot_name"
+
+ def _get_product_name_change(self, cr, uid, ids, context=None):
+ return self.pool.get('stock.inventory.line').search(cr, uid, [('product_id', 'in', ids)], context=context)
+
+ def _get_location_change(self, cr, uid, ids, context=None):
+ return self.pool.get('stock.inventory.line').search(cr, uid, [('location_id', 'in', ids)], context=context)
+
+ def _get_prodlot_change(self, cr, uid, ids, context=None):
+ return self.pool.get('stock.inventory.line').search(cr, uid, [('prod_lot_id', 'in', ids)], context=context)
+
_columns = {
'inventory_id': fields.many2one('stock.inventory', 'Inventory', ondelete='cascade', select=True),
'location_id': fields.many2one('stock.location', 'Location', required=True),
'company_id': fields.related('inventory_id','company_id',type='many2one',relation='res.company',string='Company',store=True, select=True, readonly=True),
'prod_lot_id': fields.many2one('stock.production.lot', 'Serial Number', domain="[('product_id','=',product_id)]"),
'state': fields.related('inventory_id','state',type='char',string='Status',readonly=True),
+ 'product_name': fields.related('product_id', 'name', type='char', string='Product name', store={
+ 'product.product': (_get_product_name_change, ['name', 'default_code'], 20),
+ 'stock.inventory.line': (lambda self, cr, uid, ids, c={}: ids, ['product_id'], 20),}),
+ 'product_code': fields.related('product_id', 'default_code', type='char', string='Product code', store={
+ 'product.product': (_get_product_name_change, ['name', 'default_code'], 20),
+ 'stock.inventory.line': (lambda self, cr, uid, ids, c={}: ids, ['product_id'], 20),}),
+ 'location_name': fields.related('location_id', 'complete_name', type='char', string='Location name', store={
+ 'stock.location': (_get_location_change, ['name', 'location_id', 'active'], 20),
+ 'stock.inventory.line': (lambda self, cr, uid, ids, c={}: ids, ['location_id'], 20),}),
+ 'prodlot_name': fields.related('prod_lot_id', 'name', type='char', string='Serial Number name', store={
+ 'stock.production.lot': (_get_prodlot_change, ['name'], 20),
+ 'stock.inventory.line': (lambda self, cr, uid, ids, c={}: ids, ['prod_lot_id'], 20),}),
}
def _default_stock_location(self, cr, uid, context=None):
<record id="seq_type_picking_internal" model="ir.sequence.type">
<field name="name">Picking INT</field>
- <field name="code">stock.picking</field>
+ <field name="code">stock.picking.internal</field>
</record>
<!--
<record id="seq_picking_internal" model="ir.sequence">
<field name="name">Picking INT</field>
- <field name="code">stock.picking</field>
+ <field name="code">stock.picking.internal</field>
<field name="prefix">INT/</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
<field name="model">stock.production.lot</field>
<field name="arch" type="xml">
<form string="Serial Number" version="7.0">
- <div class="oe_button_box oe_right">
- <button name="action_traceability" string="Upstream Traceability" type="object" context="{'type': 'move_history_ids2', 'field': 'prodlot_id'}"/>
- <button name="action_traceability" string="Downstream Traceability" type="object" context="{'type': 'move_history_ids', 'field': 'prodlot_id'}"/>
- </div>
- <div class="oe_title">
- <label for="name" class="oe_edit_only"/>
- <h1>
- <field name="name"/>
- </h1>
- </div>
+ <group>
+ <div>
+ <div class="oe_button_box oe_right">
+ <button name="action_traceability" string="Upstream Traceability" type="object" context="{'type': 'move_history_ids2', 'field': 'prodlot_id'}"/>
+ <button name="action_traceability" string="Downstream Traceability" type="object" context="{'type': 'move_history_ids', 'field': 'prodlot_id'}"/>
+ </div>
+ <div class="oe_title">
+ <label for="name" class="oe_edit_only"/>
+ <h1>
+ <field name="name"/>
+ </h1>
+ </div>
+ </div>
+ </group>
<group>
<group>
<field name="product_id"/>
<group>
<field name="partner_id" on_change="onchange_partner_in(partner_id)"/>
<field name="backorder_id" readonly="1" attrs="{'invisible': [('backorder_id','=',False)]}"/>
- <field name="invoice_state" string="Invoice Control" groups="account.group_account_invoice" attrs="{'invisible':[('invoice_state', '=', 'none')]}"/>
+ <field name="invoice_state" string="Invoice Control" groups="account.group_account_invoice"/>
<field name="stock_journal_id" widget="selection" groups="account.group_account_user"/>
</group>
<group>
--- /dev/null
+
+-
+ Set the current user as multicompany user
+-
+ !context
+ uid: 'stock.multicompany_user'
+
+-
+ check no error on getting default stock.move values in multicompany setting
+-
+ !python {model: stock.move}: |
+ location_obj = self.pool.get('stock.location')
+ fields = ['location_id', 'location_dest_id']
+ for type in ('in', 'internal', 'out'):
+ context['picking_type'] = type
+ defaults = self.default_get(cr, uid, ['location_id', 'location_dest_id', 'type'], context)
+ log('type: %s got defaults: %s', type, defaults)
+ for field in fields:
+ if defaults.get(field):
+ try:
+ location_obj.check_access_rule(cr, uid, [defaults[field]], 'read', context)
+ except Exception, exc:
+ assert False, "unreadable location %s: %s" % (field, exc)
+ assert defaults['type'] == type, "wrong move type"
+
+-
+ check onchange_move_type does not return unreadable in multicompany setting
+-
+ !python {model: stock.move}: |
+ location_obj = self.pool.get('stock.location')
+ fields = ['location_id', 'location_dest_id']
+ for type in ('in', 'internal', 'out'):
+ result = self.onchange_move_type(cr, uid, [], type, context)['value']
+ log('type: %s got: %s', type, result)
+ for field in fields:
+ if result.get(field):
+ try:
+ location_obj.check_access_rule(cr, uid, [result[field]], 'read', context)
+ except Exception, exc:
+ assert False, "unreadable location %s: %s" % (field, exc)
+
+-
+ check default location readability for stock_fill_inventory in multicompany setting
+-
+ !python {model: stock.fill.inventory}: |
+ location_obj = self.pool.get('stock.location')
+ defaults = self.default_get(cr, uid, ['location_id'], context)
+ log('got defaults: %s', defaults)
+ if defaults.get('location_id'):
+ try:
+ location_obj.check_access_rule(cr, uid, [defaults['location_id']], 'read', context)
+ except Exception, exc:
+ assert False, "unreadable source location: %s" % exc
+
+-
+ check default locations for warehouse in multicompany setting
+-
+ !python {model: stock.warehouse}: |
+ location_obj = self.pool.get('stock.location')
+ fields = ['lot_input_id', 'lot_stock_id', 'lot_output_id']
+ defaults = self.default_get(cr, uid, fields, context)
+ log('got defaults: %s', defaults)
+ for field in fields:
+ if defaults.get(field):
+ try:
+ location_obj.check_access_rule(cr, uid, [defaults[field]], 'read', context)
+ except Exception, exc:
+ assert False, "unreadable default %s: %s" % (field, exc)
--- /dev/null
+-
+ I create an outgoing move for 2 LCD17
+-
+ !record {model: stock.move, id: dest_move_lcd17}:
+ product_qty: 2
+ product_id: product.product_product_7
+ product_uom: product.product_uom_unit
+ location_id: stock_location_components
+ location_dest_id: stock_location_customers
+-
+ I create 2 moves for 1 LCD17 chained with the outgoing one
+-
+ !record {model: stock.move, id: move_lcd17_1}:
+ product_qty: 1
+ product_id: product.product_product_7
+ product_uom: product.product_uom_unit
+ location_id: stock_location_stock
+ location_dest_id: stock_location_components
+ move_dest_id: dest_move_lcd17
+-
+ !record {model: stock.move, id: move_lcd17_2}:
+ product_qty: 1
+ product_id: product.product_product_7
+ product_uom: product.product_uom_unit
+ location_id: stock_location_stock
+ location_dest_id: stock_location_components
+ move_dest_id: dest_move_lcd17
+-
+ I confirm all moves
+-
+ !python {model: stock.move}: |
+ self.action_confirm(cr, uid, [ref('stock.dest_move_lcd17'),ref('stock.move_lcd17_1'),ref('stock.move_lcd17_2')], context=context)
+-
+ I process the 2 moves chained with the outgoing move
+-
+ !python {model: stock.move}: |
+ self.action_done(cr, uid, [ref('stock.move_lcd17_1'),ref('stock.move_lcd17_2')], context=context)
+-
+ the outgoing move must be assigned
+-
+ !python {model: stock.move}: |
+ move = self.browse(cr, uid, ref('stock.dest_move_lcd17'), context=context)
+ assert move.state == 'assigned', "out move was not assigned when internal moves where processed"
+-
+ I create an outgoing move for 2 LCD17
+-
+ !record {model: stock.move, id: dest_move2_lcd17}:
+ product_qty: 2
+ product_id: product.product_product_7
+ product_uom: product.product_uom_unit
+ location_id: stock_location_components
+ location_dest_id: stock_location_customers
+-
+ I create 2 moves for 1 LCD17 chained with the outgoing one
+-
+ !record {model: stock.move, id: move2_lcd17_1}:
+ product_qty: 1
+ product_id: product.product_product_7
+ product_uom: product.product_uom_unit
+ location_id: stock_location_stock
+ location_dest_id: stock_location_components
+ move_dest_id: dest_move2_lcd17
+-
+ !record {model: stock.move, id: move2_lcd17_2}:
+ product_qty: 1
+ product_id: product.product_product_7
+ product_uom: product.product_uom_unit
+ location_id: stock_location_stock
+ location_dest_id: stock_location_components
+ move_dest_id: dest_move2_lcd17
+-
+ I confirm all moves
+-
+ !python {model: stock.move}: |
+ self.action_confirm(cr, uid, [ref('stock.dest_move2_lcd17'),ref('stock.move2_lcd17_1'),ref('stock.move2_lcd17_2')], context=context)
+-
+ I process the 1st move chained with the outgoing move
+-
+ !python {model: stock.move}: |
+ self.action_done(cr, uid, [ref('stock.move2_lcd17_1')], context=context)
+-
+ the outgoing move must not be assigned
+-
+ !python {model: stock.move}: |
+ move = self.browse(cr, uid, ref('stock.dest_move2_lcd17'), context=context)
+ assert move.state != 'assigned', "out move was assigned when only 1st internal moves where processed"
+-
+ I process the 2nd move chained with the outgoing move
+-
+ !python {model: stock.move}: |
+ self.action_done(cr, uid, [ref('stock.move2_lcd17_2')], context=context)
+-
+ the outgoing move must not be assigned
+-
+ !python {model: stock.move}: |
+ move = self.browse(cr, uid, ref('stock.dest_move2_lcd17'), context=context)
+ assert move.state == 'assigned', "out move was not assigned when internal moves where processed"
_name = "stock.fill.inventory"
_description = "Import Inventory"
+ # Maximum size of a batch of lines we can import without risking OOM
+ MAX_IMPORT_LINES = 10000
+
def _default_location(self, cr, uid, ids, context=None):
try:
location = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock')
for location in location_ids:
datas = {}
res[location] = {}
- move_ids = move_obj.search(cr, uid, ['|',('location_dest_id','=',location),('location_id','=',location),('state','=','done')], context=context)
+ all_move_ids = move_obj.search(cr, uid, ['|',('location_dest_id','=',location),('location_id','=',location),('state','=','done')], context=context)
local_context = dict(context)
local_context['raise-exception'] = False
- for move in move_obj.browse(cr, uid, move_ids, context=context):
- lot_id = move.prodlot_id.id
- prod_id = move.product_id.id
- if move.location_dest_id.id != move.location_id.id:
- if move.location_dest_id.id == location:
- qty = uom_obj._compute_qty_obj(cr, uid, move.product_uom,move.product_qty, move.product_id.uom_id, context=local_context)
- else:
- qty = -uom_obj._compute_qty_obj(cr, uid, move.product_uom,move.product_qty, move.product_id.uom_id, context=local_context)
-
-
- if datas.get((prod_id, lot_id)):
- qty += datas[(prod_id, lot_id)]['product_qty']
-
- datas[(prod_id, lot_id)] = {'product_id': prod_id, 'location_id': location, 'product_qty': qty, 'product_uom': move.product_id.uom_id.id, 'prod_lot_id': lot_id}
+ # To avoid running out of memory, process limited batches
+ for i in xrange(0, len(all_move_ids), self.MAX_IMPORT_LINES):
+ move_ids = all_move_ids[i:i+self.MAX_IMPORT_LINES]
+ for move in move_obj.browse(cr, uid, move_ids, context=context):
+ lot_id = move.prodlot_id.id
+ prod_id = move.product_id.id
+ if move.location_dest_id.id != move.location_id.id:
+ if move.location_dest_id.id == location:
+ qty = uom_obj._compute_qty_obj(cr, uid, move.product_uom,move.product_qty, move.product_id.uom_id, context=local_context)
+ else:
+ qty = -uom_obj._compute_qty_obj(cr, uid, move.product_uom,move.product_qty, move.product_id.uom_id, context=local_context)
+
+
+ if datas.get((prod_id, lot_id)):
+ qty += datas[(prod_id, lot_id)]['product_qty']
+
+ # Floating point sum could introduce tiny rounding errors :
+ # Use the UoM API for the rounding (same UoM in & out).
+ qty = uom_obj._compute_qty_obj(cr, uid,
+ move.product_id.uom_id, qty,
+ move.product_id.uom_id)
+ datas[(prod_id, lot_id)] = {'product_id': prod_id, 'location_id': location, 'product_qty': qty, 'product_uom': move.product_id.uom_id.id, 'prod_lot_id': lot_id}
if datas:
flag = True
view_type="form"
target="new"
id="action_stock_invoice_onshipping"/>
+
+ <act_window name="Create Invoices"
+ res_model="stock.invoice.onshipping"
+ src_model="stock.picking"
+ key2="client_action_multi"
+ multi="True"
+ view_mode="form"
+ view_type="form"
+ target="new"
+ id="action_stock_invoice_onshipping"/>
</data>
</openerp>
'name': _('%s-%s-return') % (new_pick_name, pick.name),
'move_lines': [],
'state':'draft',
+ 'backorder_id': False,
'type': new_type,
'date':date_cur,
'invoice_state': data['invoice_state'],
def _prepare_chained_picking(self, cr, uid, picking_name, picking, picking_type, moves_todo, context=None):
res = super(stock_move, self)._prepare_chained_picking(cr, uid, picking_name, picking, picking_type, moves_todo, context=context)
- res.update({'invoice_state': moves_todo[0][1][6] or 'none'})
+ state = moves_todo[0][1][6] or 'none'
+ if picking.sale_id:
+ if res.get('type') == 'out' and picking.sale_id.order_policy == 'picking':
+ state = '2binvoiced'
+ res.update(invoice_state=state)
return res
stock_move()
'author': 'OpenERP SA',
'depends': ['mail'],
'data': [
+ 'security/survey_security.xml',
+ 'security/ir.model.access.csv',
'survey_report.xml',
'survey_data.xml',
'wizard/survey_selection.xml',
'wizard/survey_answer.xml',
- 'security/survey_security.xml',
- 'security/ir.model.access.csv',
'survey_view.xml',
'wizard/survey_print_statistics.xml',
'wizard/survey_print_answer.xml',
access_survey_response_line_survey_user,survey.response.line.survey.user,model_survey_response_line,base.group_survey_user,1,1,1,1\r
access_survey_question_column_heading_survey_user,survey.question.column.heading.survey.user,model_survey_question_column_heading,base.group_survey_user,1,0,0,0\r
access_survey_question_column_heading_user,survey.question.column.heading user,model_survey_question_column_heading,base.group_tool_user,1,1,1,1\r
+access_survey_invitee,survey.invitee,model_survey,base.group_survey_invitee,1,0,0,0\r
+access_survey_page_invitee,survey.page.invitee,model_survey_page,base.group_survey_invitee,1,0,0,0\r
+access_survey_question_invitee,survey.question.invitee,model_survey_question,base.group_survey_invitee,1,0,0,0\r
+access_survey_answer_invitee,survey.answer.invitee,model_survey_answer,base.group_survey_invitee,1,0,0,0\r
+access_survey_response_invitee,survey.response.invitee,model_survey_response,base.group_survey_invitee,1,1,1,0\r
+access_survey_response_line_invitee,survey.response.line.invitee,model_survey_response_line,base.group_survey_invitee,1,1,1,0\r
+access_survey_response_answer_invitee,survey.response.answer.invitee,model_survey_response_answer,base.group_survey_invitee,1,1,1,0\r
+access_survey_history_invitee,survey.history.invitee,model_survey_history,base.group_survey_invitee,1,0,1,0\r
+access_survey_question_column_heading_invitee,survey.question.column.heading.invitee,model_survey_question_column_heading,base.group_survey_invitee,1,0,0,0\r
+access_res_partner_invitee,res.partner.invitee,base.model_res_partner,base.group_survey_invitee,1,0,0,0\r
<field name="name">Survey / User</field>
<field name="users" eval="[(4, ref('base.user_root'))]"/>
</record>
- </data>
+ <record model="res.groups" id="base.group_survey_invitee">
+ <field name="name">Survey / Invitee</field>
+ </record>
+ <record id="rule_survey_invitee" model="ir.rule">
+ <field name="model_id" ref="model_survey" />
+ <field name="domain_force">[("invited_user_ids", "=", user.id)]</field>
+ <field name="groups" eval="[(6, 0,[ref('base.group_survey_invitee')])]" />
+ </record>
+ <record id="rule_survey_page_invitee" model="ir.rule">
+ <field name="model_id" ref="model_survey_page" />
+ <field name="domain_force">[("survey_id.invited_user_ids", "=", user.id)]</field>
+ <field name="groups" eval="[(6, 0,[ref('base.group_survey_invitee')])]" />
+ </record>
+ <record id="rule_survey_question_invitee" model="ir.rule">
+ <field name="model_id" ref="model_survey_question" />
+ <field name="domain_force">[("survey.invited_user_ids", "=", user.id)]</field>
+ <field name="groups" eval="[(6, 0,[ref('base.group_survey_invitee')])]" />
+ </record>
+ <record id="rule_survey_answer_invitee" model="ir.rule">
+ <field name="model_id" ref="model_survey_answer" />
+ <field name="domain_force">[("question_id.survey.invited_user_ids", "=", user.id)]</field>
+ <field name="groups" eval="[(6, 0,[ref('base.group_survey_invitee')])]" />
+ </record>
+ <record id="rule_survey_response_invitee" model="ir.rule">
+ <field name="model_id" ref="model_survey_response" />
+ <field name="domain_force">[("user_id", "=", user.id)]</field>
+ <field name="groups" eval="[(6, 0,[ref('base.group_survey_invitee')])]" />
+ </record>
+ <record id="rule_survey_response_line_invitee" model="ir.rule">
+ <field name="model_id" ref="model_survey_response_line" />
+ <field name="domain_force">[("response_id.user_id", "=", user.id)]</field>
+ <field name="groups" eval="[(6, 0,[ref('base.group_survey_invitee')])]" />
+ </record>
+ <record id="rule_survey_response_answer_invitee" model="ir.rule">
+ <field name="model_id" ref="model_survey_response_answer" />
+ <field name="domain_force">[("response_id.response_id.user_id", "=", user.id)]</field>
+ <field name="groups" eval="[(6, 0,[ref('base.group_survey_invitee')])]" />
+ </record>
+ <record id="rule_survey_history_invitee" model="ir.rule">
+ <field name="model_id" ref="model_survey_history" />
+ <field name="domain_force">[("survey_id.invited_user_ids", "=", user.id)]</field>
+ <field name="groups" eval="[(6, 0,[ref('base.group_survey_invitee')])]" />
+ </record>
+ <record id="rule_survey_question_column_heading_invitee" model="ir.rule">
+ <field name="model_id" ref="model_survey_question_column_heading" />
+ <field name="domain_force">[("question_id.survey.invited_user_ids", "=", user.id)]</field>
+ <field name="groups" eval="[(6, 0,[ref('base.group_survey_invitee')])]" />
+ </record>
+ <record id="rule_res_partner_invitee" model="ir.rule">
+ <field name="model_id" ref="base.model_res_partner" />
+ <field name="domain_force">[("id", "=", user.id)]</field>
+ <field name="groups" eval="[(6, 0,[ref('base.group_survey_invitee')])]" />
+ </record>
+ </data>
</openerp>
id="report_survey_form"
model="survey"
name="survey.form"
- string="Survey"/>
+ string="Survey"
+ groups="base.group_survey_user" />
<report auto="True"
id="survey_analysis"
model="survey"
name="survey.analysis"
rml=""
- string="Survey Statistics"/>
+ string="Survey Statistics"
+ groups="base.group_survey_user" />
<report auto="True"
id="survey_browse_response"
model="survey"
name="survey.browse.response"
rml=""
- string="Survey Answers"/>
+ string="Survey Answers"
+ groups="base.group_survey_user" />
</data>
</openerp>
</field>
</record>
+ <record model="ir.ui.view" id="survey_form_invitee">
+ <field name="name">survey_form_invitee</field>
+ <field name="model">survey</field>
+ <field name="groups_id" eval="[(6, 0, [ref('base.group_survey_invitee')])]" />
+ <field name="priority" eval="1" />
+ <field name="inherit_id" ref="survey_form" />
+ <field name="arch" type="xml">
+ <data>
+ <form position="replace">
+ <form string="Survey" version="7.0">
+ <sheet>
+ <field name="state" invisible="True" />
+ <div class="oe_button_box oe_right">
+ <button name="fill_survey" states="open" string="Answer Survey" type="object" icon="gtk-execute" context="{'survey_id': active_id}" attrs="{'invisible':[('state','!=','open')]}"/>
+ </div>
+ <div class="oe_title">
+ <label for="title" class="oe_edit_only"/>
+ <h1>
+ <field name="title" attrs="{'readonly':[('state','=','close')]}"/>
+ </h1>
+ </div>
+ <field name="note" />
+ </sheet>
+ </form>
+ </form>
+ </data>
+ </field>
+ </record>
+
<record id="survey_search" model="ir.ui.view">
<field name="name">survey_search</field>
<field name="model">survey</field>
id="act_survey_pages"
name="Pages"
res_model="survey.page"
- src_model="survey"/>
+ src_model="survey"
+ groups="base.group_survey_user"/>
<act_window
context="{'search_default_survey': active_id, 'default_survey': active_id}"
id="act_survey_question"
name="Questions"
res_model="survey.question"
- src_model="survey"/>
+ src_model="survey"
+ groups="base.group_survey_user"/>
<act_window
id="act_survey_page_question"
name="Questions"
res_model="survey.question"
- src_model="survey.page"/>
+ src_model="survey.page" />
<act_window domain="[('question_id', '=', active_id)]"
id="act_survey_answer"
id="act_survey_request"
name="Survey Requests"
res_model="survey.request"
- src_model="survey"/>
+ src_model="survey"
+ groups="base.group_survey_user"/>
</data>
</openerp>
context = {'active_model':'survey', 'active_id': ref('survey_Initial_partner_feedback'), 'active_ids': [ref('survey_Initial_partner_feedback')]}
values = self.default_get(cr, uid, ['mail_from', 'mail_subject', 'send_mail_existing', 'mail_subject_existing', 'mail', 'partner_ids', 'send_mail'], context)
values['mail_from'] = 'Surveyor'
- new_id = self.create(cr, uid, values)
+ new_id = self.create(cr, uid, values, context)
self.action_send(cr, uid, [new_id], context)
-
I set the value in "Total start survey" field.
from openerp import addons, netsvc, tools
from openerp.osv import fields, osv
-from openerp.tools import to_xml
+from openerp.tools import to_xml, SUPERUSER_ID
from openerp.tools.translate import _
from openerp.tools.safe_eval import safe_eval
raise osv.except_osv(_('Warning!'),_("You cannot answer this survey more than %s times.") % (user_limit))
if sur_rec.max_response_limit and sur_rec.max_response_limit <= sur_rec.tot_start_survey and not sur_name_rec.page_no + 1:
- survey_obj.write(cr, uid, survey_id, {'state':'close', 'date_close':strftime("%Y-%m-%d %H:%M:%S")})
+ survey_obj.write(cr, SUPERUSER_ID, survey_id, {'state':'close', 'date_close':strftime("%Y-%m-%d %H:%M:%S")})
p_id = p_id[sur_name_rec.page_no + 1]
surv_name_wiz.write(cr, uid, [context['sur_name_id'],], {'page_no' : sur_name_rec.page_no + 1})
result['fields'] = fields
result['context'] = context
else:
- survey_obj.write(cr, uid, survey_id, {'tot_comp_survey' : sur_rec.tot_comp_survey + 1})
- sur_response_obj.write(cr, uid, [sur_name_read.response], {'state' : 'done'})
+ survey_obj.write(cr, SUPERUSER_ID, survey_id, {'tot_comp_survey' : sur_rec.tot_comp_survey + 1})
+ sur_response_obj.write(cr, uid, int(sur_name_read.response), {'state' : 'done'})
# mark the survey request as done; call 'survey_req_done' on its actual model
survey_req_obj = self.pool.get(context.get('active_model'))
'date': strftime('%Y-%m-%d %H:%M:%S'), 'survey_id': sur_name_read['survey_id'][0]})
survey_id = sur_name_read['survey_id'][0]
sur_rec = survey_obj.read(cr, uid, survey_id)
- survey_obj.write(cr, uid, survey_id, {'tot_start_survey' : sur_rec['tot_start_survey'] + 1})
+ survey_obj.write(cr, SUPERUSER_ID, survey_id, {'tot_start_survey' : sur_rec['tot_start_survey'] + 1})
if context.has_key('cur_id'):
if context.has_key('request') and context.get('request',False):
self.pool.get(context.get('object',False)).write(cr, uid, [int(context.get('cur_id',False))], {'response' : response_id})
survey_obj = self.pool.get('survey')
msg = ""
name = ""
+ survey_id = 0
for sur in survey_obj.browse(cr, uid, context.get('active_ids', []), context=context):
name += "\n --> " + sur.title + "\n"
if sur.state != 'open':
data['mail_subject'] = _("Invitation for %s") % (sur.title)
data['mail_subject_existing'] = _("Invitation for %s") % (sur.title)
data['mail_from'] = sur.responsible_id.email
+ survey_id = sur.id
if msg:
raise osv.except_osv(_('Warning!'), _('The following surveys are not in open state: %s') % msg)
data['mail'] = _('''
Your login ID: %%(login)s\n
Your password: %%(passwd)s\n
\n\n
-Thanks,''') % (name, self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context))
+Thanks,''') % (
+ name,
+ self.pool.get('ir.config_parameter').get_param(
+ cr, uid, 'web.base.url', default='http://localhost:8069',
+ context=context)
+ + '#id=%d&view_type=form&model=survey' % survey_id)
return data
def create_report(self, cr, uid, res_ids, report_name=False, file_name=False):
mail_message = self.pool.get('mail.message')
model_data_obj = self.pool.get('ir.model.data')
- group_id = model_data_obj._get_id(cr, uid, 'base', 'group_survey_user')
- group_id = model_data_obj.browse(cr, uid, group_id, context=context).res_id
+ group_id = model_data_obj.get_object_reference(
+ cr, uid, 'base', 'group_survey_invitee')[1]
act_id = self.pool.get('ir.actions.act_window')
act_id = act_id.search(cr, uid, [('res_model', '=' , 'survey.name.wiz'), \
'address_id': partner.id,
'groups_id': [[6, 0, [group_id]]],
'action_id': act_id[0],
- 'survey_id': [[6, 0, survey_ids]]
+ 'survey_id': [[6, 0, survey_ids]],
+ 'partner_id': partner.id,
+ 'tz': context.get('tz'),
}
user = user_ref.create(cr, uid, res_data)
if user not in new_user:
<act_window id="action_act_view_survey_send_invitation"
key2="client_action_multi" name="Send Invitations"
res_model="survey.send.invitation" src_model="survey"
- view_mode="form" target="new" view_type="form" />
+ view_mode="form" target="new" view_type="form"
+ groups="base.group_survey_user"/>
<!-- Survey send invitation Display Log Form View -->
report = zlib.decompress(report)
report_mimetype = self.TYPES_MAPPING.get(
report_struct['format'], 'octet-stream')
- file_name = action.get('name', 'report')
- if 'name' not in action:
- reports = req.session.model('ir.actions.report.xml')
- res_id = reports.search([('report_name', '=', action['report_name']),],
- 0, False, False, context)
- if len(res_id) > 0:
- file_name = reports.read(res_id[0], ['name'], context)['name']
- else:
- file_name = action['report_name']
+ file_name = action['report_name']
+ # Try to get current object model and their ids from context
+ if 'context' in action:
+ action_context = action['context']
+ if (action_context.get('active_model')
+ and action_context['active_ids']):
+ # Use built-in ORM method to get data from DB
+ m = req.session.model(action_context['active_model'])
+ r = []
+ try:
+ r = m.name_get(action_context['active_ids'], context)
+ except xmlrpclib.Fault:
+ #we assume this went wrong because of incorrect/missing
+ #_rec_name. We don't have access to _columns here to do
+ # a proper check
+ pass
+ # Parse result to create a better filename
+ item_names = [item[1] or str(item[0]) for item in r]
+ if action.get('name'):
+ item_names.insert(0, action['name'])
+ if item_names:
+ file_name = '-'.join(item_names)[:251]
file_name = '%s.%s' % (file_name, report_struct['format'])
+ # Create safe filename
+ p = re.compile('[/:(")<>|?*]|(\\\)')
+ file_name = p.sub('_', file_name)
return req.make_response(report,
headers=[
position: relative;
display: inline-block;
}
+.openerp .oe_form_editable .oe_form_required.oe_form_field_one2many table.oe_list_content, .openerp .oe_form_editable .oe_form_required.oe_form_field_many2many table.oe_list_content {
+ background-color: #d2d2ff !important;
+}
+.openerp .oe_form_editable .oe_form_invalid.oe_form_field_one2many table.oe_list_content, .openerp .oe_form_editable .oe_form_invalid.oe_form_field_many2many table.oe_list_content {
+ background-color: #ff6666 !important;
+}
.openerp .oe_form_invalid input, .openerp .oe_form_invalid select, .openerp .oe_form_invalid textarea {
background-color: #F66 !important;
border: 1px solid #D00 !important;
}
.openerp .oe_form_sheet_width {
min-width: 650px;
- max-width: 860px;
- margin: 0 auto;
+ max-width: auto;
+ margin: 16px;
}
.openerp .oe_form_sheet {
background: white;
.openerp .oe_form div.oe_chatter {
box-sizing: border-box;
min-width: 682px;
- max-width: 892px;
- margin: 0 auto;
+ max-width: 0 auto;
+ margin: 16px;
padding: 16px 16px 48px;
}
.openerp .oe_form div.oe_form_configuration p, .openerp .oe_form div.oe_form_configuration ul, .openerp .oe_form div.oe_form_configuration ol {
.openerp .oe_form .oe_form_field_one2many > .oe_view_manager .oe_list_pager_single_page {
display: none;
}
+.openerp .oe_form_field_one2many, .openerp .oe_form_field_many2many {
+ overflow-x: auto;
+}
.openerp .oe_form_field_one2many > .oe_view_manager .oe_list_pager_single_page, .openerp .oe_form_field_many2many > .oe_view_manager .oe_list_pager_single_page {
display: none !important;
}
$tag-border-selected: #a6a6fe
$hover-background: #f0f0fa
$link-color: #7C7BAD
-$sheet-max-width: 860px
+$sheet-max-width: auto
$sheet-min-width: 650px
$sheet-padding: 16px
// }}}
.oe_form_dropdown_section
position: relative
display: inline-block
+ .oe_form_editable
+ .oe_form_required
+ &.oe_form_field_one2many, &.oe_form_field_many2many
+ table.oe_list_content
+ background-color: #d2d2ff !important
+ .oe_form_invalid
+ &.oe_form_field_one2many, &.oe_form_field_many2many
+ table.oe_list_content
+ background-color: #ff6666 !important
.oe_form_invalid
input, select, textarea
background-color: #F66 !important
.oe_form_sheet_width
min-width: 650px
max-width: $sheet-max-width
- margin: 0 auto
+ margin: 16px
.oe_form_sheet
background: white
min-height: 330px
div.oe_chatter
box-sizing: border-box
min-width: $sheet-min-width + 2* $sheet-padding
- max-width: $sheet-max-width + 2* $sheet-padding
- margin: 0 auto
+ max-width: 0 auto
+ margin: 16px
padding: 16px 16px 48px
div.oe_form_configuration
p, ul, ol
display: none
.oe_form_field_one2many,.oe_form_field_many2many
// TODO: oe_form_field_one2many_list?
+ overflow-x: auto
> .oe_view_manager
.oe_list_pager_single_page
display: none !important
filter: alpha(opacity = 0)
border: none
width: 0
- border-right: none
+ border-right: none
.label
border-bottom: 1px solid #cacaca
background: transparent
var offset = options.offset || 0,
limit = options.limit || false;
var end_pos = limit && limit !== -1 ? offset + limit : this.ids.length;
- return this.read_ids(this.ids.slice(offset, end_pos), fields);
+ return this.read_ids(this.ids.slice(offset, end_pos), fields, options);
},
set_ids: function (ids) {
this.ids = ids;
return instance.web.date_to_str(facetValue.get('value'));
},
complete: function (needle) {
- var d = Date.parse(needle);
+ var d;
+ try {
+ var t = (this.attrs && this.attrs.type === 'datetime') ? 'datetime' : 'date';
+ var v = instance.web.parse_value(needle, {'widget': t});
+ if (t === 'datetime'){
+ d = instance.web.str_to_datetime(v);
+ }
+ else{
+ d = instance.web.str_to_date(v);
+ }
+ } catch (e) {
+ // pass
+ }
if (!d) { return $.when(null); }
var date_string = instance.web.format_value(d, this.attrs);
var label = _.str.sprintf(_.str.escapeHTML(
this.$el.off('.formBlur');
}
this._super();
+ if (this.$buttons) {
+ this.$buttons.remove();
+ }
},
load_form: function(data) {
var self = this;
};
instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
multi_selection: false,
- disable_utility_classes: true,
init: function(field_manager, node) {
this._super(field_manager, node);
lazy_build_o2m_kanban_view();
}
return $.when(false);
},
+ is_false: function() {
+ return this.dataset.ids.length == 0;
+ },
is_syntax_valid: function() {
if (! this.viewmanager || ! this.viewmanager.views[this.viewmanager.active_view])
return true;
instance.web.form.One2ManyDataSet = instance.web.BufferedDataSet.extend({
get_context: function() {
this.context = this.o2m.build_context();
+ var self = this;
+ _.each(arguments, function(context) {
+ self.context.add(context);
+ });
return this.context;
}
});
if (!this.fields_view || !this.editable()){
return true;
}
- this.o2m._dirty_flag = true;
var r;
return _.every(this.records.records, function(record){
r = record;
*/
instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
multi_selection: false,
- disable_utility_classes: true,
init: function(field_manager, node) {
this._super(field_manager, node);
this.is_loaded = $.Deferred();
var text = _t("Download");
var value = row_data[this.id].value;
var download_url;
- if (value && value.substr(0, 10).indexOf(' ') == -1) {
- download_url = "data:application/octet-stream;base64," + value;
- } else {
- download_url = instance.session.url('/web/binary/saveas', {model: options.model, field: this.id, id: options.id});
- if (this.filename) {
- download_url += '&filename_field=' + this.filename;
- }
+ download_url = instance.session.url('/web/binary/saveas', {model: options.model, field: this.id, id: options.id});
+ if (this.filename) {
+ download_url += '&filename_field=' + this.filename;
}
if (this.filename && row_data[this.filename]) {
text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
* @return {*}
*/
ir_actions_common: function(executor, options) {
+ var self = this;
if (this.inner_widget && executor.action.target !== 'new') {
if (this.getParent().has_uncommitted_changes()) {
return $.Deferred().reject();
var self = this;
var index = this.dataset.get_id_index(event_id);
if (index !== null) {
- this.dataset.unlink(this.dataset.ids[index]);
+ this.dataset.unlink([this.dataset.ids[index]]);
}
},
});
record.do_reload();
new_group.do_save_sequences();
}).fail(function(error, evt) {
- evt.preventDefault();
- alert(_t("An error has occured while moving the record to this group: ") + data.fault_code);
self.do_reload(); // TODO: use draggable + sortable in order to cancel the dragging when the rcp fails
});
}
},
do_show_more: function(evt) {
var self = this;
- var ids = self.view.dataset.ids.splice(0);
return this.dataset.read_slice(this.view.fields_keys.concat(['__last_update']), {
'limit': self.view.limit,
'offset': self.dataset_offset += self.view.limit
}).then(function(records) {
- self.view.dataset.ids = ids.concat(self.dataset.ids);
self.do_add_records(records);
self.compute_cards_auto_height();
self.view.postprocess_m2m_tags();
}
def _auto_init(self, cr, context=None):
- super(ir_attachment, self)._auto_init(cr, context)
+ result = super(ir_attachment, self)._auto_init(cr, context)
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('ir_attachment_res_idx',))
if not cr.fetchone():
cr.execute('CREATE INDEX ir_attachment_res_idx ON ir_attachment (res_model, res_id)')
cr.commit()
+ return result
def check(self, cr, uid, ids, mode, context=None, values=None):
"""Restricts the access to an ir.attachment, according to referred model
'name': fields.char('Name', size=64, required=True, translate=True),
'code': fields.char('Code', size=64, required=True),
'field_ids': fields.one2many('res.partner.bank.type.field', 'bank_type_id', 'Type Fields'),
- 'format_layout': fields.text('Format Layout', translate=True)
+ 'format_layout': fields.text('Format Layout', translate=False)
}
_defaults = {
'format_layout': lambda *args: "%(bank_name)s: %(acc_number)s"
'state': fields.selection(_bank_type_get, 'Bank Account Type', required=True,
change_default=True),
'sequence': fields.integer('Sequence'),
+ 'active': fields.boolean('Active', select=True),
'footer': fields.boolean("Display on Reports", help="Display this bank account on the footer of printed documents like invoices and sales orders.")
}
cursor, user, 'country_id', context=context),
'state_id': lambda obj, cursor, user, context: obj._default_value(
cursor, user, 'state_id', context=context),
- 'name': '/'
+ 'name': '/',
+ 'active': True,
}
def fields_get(self, cr, uid, allfields=None, context=None):
<group col="4">
<field name="state"/>
<field name="acc_number" placeholder="Account Number"/>
+ <field name="active"/>
+ <newline/>
<field name="company_id" groups="base.group_multi_company" on_change="onchange_company_id(company_id)"
invisible="context.get('company_hide', True)" widget="selection"/>
<field name="footer" invisible="context.get('footer_hide', True)"/>
<record id="pl" model="res.country">
<field name="name">Poland</field>
<field name="code">pl</field>
- <field name="currency_id" ref="PLZ"/>
+ <field name="currency_id" ref="PLN"/>
</record>
<record id="pm" model="res.country">
<field name="name">Saint Pierre and Miquelon</field>
'direction': 'ltr',
'date_format':_get_default_date_format,
'time_format':_get_default_time_format,
- 'grouping': '[]',
+ 'grouping': '[3, 0]',
'decimal_point': '.',
'thousands_sep': ',',
}
import openerp
from openerp import SUPERUSER_ID
from openerp import pooler, tools
-from openerp.osv import osv, fields
+from openerp.osv import osv, fields, orm
from openerp.osv.expression import get_unaccent_wrapper
from openerp.tools.translate import _
from openerp.tools.yaml_import import is_comment
class res_partner_category(osv.osv):
def name_get(self, cr, uid, ids, context=None):
- """Return the categories' display name, including their direct
- parent by default.
-
- :param dict context: the ``partner_category_display`` key can be
- used to select the short version of the
- category name (without the direct parent),
- when set to ``'short'``. The default is
- the long version."""
+ """ Return the categories' display name, including their direct
+ parent by default.
+
+ If ``context['partner_category_display']`` is ``'short'``, the short
+ version of the category name (without the direct parent) is used.
+ The default is the long version.
+ """
+ if not isinstance(ids, list):
+ ids = [ids]
if context is None:
context = {}
+
if context.get('partner_category_display') == 'short':
return super(res_partner_category, self).name_get(cr, uid, ids, context=context)
- if isinstance(ids, (int, long)):
- ids = [ids]
- reads = self.read(cr, uid, ids, ['name', 'parent_id'], context=context)
+
res = []
- for record in reads:
- name = record['name']
- if record['parent_id']:
- name = record['parent_id'][1] + ' / ' + name
- res.append((record['id'], name))
+ for category in self.browse(cr, uid, ids, context=context):
+ names = []
+ current = category
+ while current:
+ names.append(current.name)
+ current = current.parent_id
+ res.append((category.id, ' / '.join(reversed(names))))
return res
def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
adr_pref.add('default')
result = {}
visited = set()
+ partner = orm.browse_null()
for partner in self.browse(cr, uid, filter(None, ids), context=context):
current_partner = partner
while current_partner:
# 'data' section, but should probably not alter the data,
# as there is no rollback.
if tools.config.options['test_enable']:
- report.record_result(load_test(module_name, idref, mode))
-
+ report.record_result(load_test(module_name, idref, mode),
+ details=(dict(module=module_name,
+ msg="Exception during load of legacy "
+ "data-based tests (yml...)")))
# Run the `fast_suite` and `checks` tests given by the module.
if module_name == 'base':
# Also run the core tests after the database is created.
- report.record_result(openerp.modules.module.run_unit_tests('openerp'))
- report.record_result(openerp.modules.module.run_unit_tests(module_name))
+ report.record_result(openerp.modules.module.run_unit_tests('openerp'),
+ details=dict(module='openerp',
+ msg="Failure or error in server core "
+ "unit tests"))
+ report.record_result(openerp.modules.module.run_unit_tests(module_name),
+ details=dict(module=module_name,
+ msg="Failure or error in unit tests, "
+ "check logs for more details"))
processed_modules.append(package.name)
view = getattr(self, '_get_default_%s_view' % view_type)(
cr, user, context)
except AttributeError:
+ if config['debug_mode']: raise
# what happens here, graph case?
raise except_orm(_('Invalid Architecture!'), _("There is no view of type '%s' defined for the structure!") % view_type)
cr.commit() # start a new transaction
- self._add_sql_constraints(cr)
+ if getattr(self, '_auto', True):
+ self._add_sql_constraints(cr)
if create:
self._execute_sql(cr)
from openerp.tools.translate import translate
from openerp.osv.orm import MetaModel, Model, TransientModel, AbstractModel
import openerp.exceptions
+from openerp.tools.config import config
import time
import random
_logger.info("%s, retry %d/%d in %.04f sec..." % (errorcodes.lookup(e.pgcode), tries, MAX_TRIES_ON_CONCURRENCY_FAILURE, wait_time))
time.sleep(wait_time)
except orm.except_orm, inst:
+ if config['debug_mode']: raise
_, _, tb = sys.exc_info()
raise except_osv(inst.name, inst.value), None, tb
except except_osv:
raise
except IntegrityError, inst:
+ if config['debug_mode']: raise
osv_pool = pooler.get_pool(dbname)
for key in osv_pool._sql_error.keys():
if key in inst[0]:
if extra_style:
style.__dict__.update(extra_style)
result = []
- for i in self._textual(node).split('\n'):
- result.append(platypus.Paragraph(i, style, **(utils.attr_get(node, [], {'bulletText':'str'}))))
+ textuals = self._textual(node).split('\n')
+ keep_empty_lines = (len(textuals) > 1) and len(node.text.strip())
+ for i in textuals:
+ if keep_empty_lines and len(i.strip()) == 0:
+ i = '<font color="white"> </font>'
+ result.append(
+ platypus.Paragraph(
+ i, style, **(
+ utils.attr_get(node, [], {'bulletText':'str'}))
+ )
+ )
return result
elif node.tag=='barCode':
try:
n2.tag = tag
n2.attrib.update(attr or {})
yield n2
- tagname = ''
+ continue
except GeneratorExit:
pass
except Exception, e:
r = resource.getrusage(resource.RUSAGE_SELF)
cpu_time = r.ru_utime + r.ru_stime
def time_expired(n, stack):
- _logger.info('Worker (%d) CPU time limit (%s) reached.', config['limit_time_cpu'])
+ _logger.info('Worker (%d) CPU time limit (%s) reached.', os.getpid(), config['limit_time_cpu'])
# We dont suicide in such case
raise Exception('CPU time limit exceeded.')
signal.signal(signal.SIGXCPU, time_expired)
# This also mimics SimpleXMLRPCDispatcher._marshaled_dispatch() for
# exception handling.
try:
- result = openerp.netsvc.dispatch_rpc(service, method, params)
+ def fix(res):
+ """
+ This fix is a minor hook to avoid xmlrpclib to raise TypeError exception:
+ - To respect the XML-RPC protocol, all "int" and "float" keys must be cast to string to avoid
+ TypeError, "dictionary key must be string"
+ - And since "allow_none" is disabled, we replace all None values with a False boolean to avoid
+ TypeError, "cannot marshal None unless allow_none is enabled"
+ """
+ if res is None:
+ return False
+ elif type(res) == dict:
+ return dict((str(key), fix(value)) for key, value in res.items())
+ else:
+ return res
+
+ result = fix(openerp.netsvc.dispatch_rpc(service, method, params))
response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None)
except Exception, e:
if legacy_exceptions:
def __init__(self):
self.successes = 0
self.failures = 0
+ self.failures_details = []
def record_success(self):
self.successes += 1
- def record_failure(self):
+ def record_failure(self, details=None):
self.failures += 1
+ if details is not None:
+ self.failures_details.append(details)
- def record_result(self, result):
+ def record_result(self, result, details=None):
+ """Record either success or failure, with the provided details in the latter case.
+
+ :param result: a boolean
+ :param details: a dict with keys ``'module'``, ``'testfile'``, ``'msg'``, ``'msg_args'``
+ """
if result is None:
pass
elif result is True:
self.record_success()
elif result is False:
- self.record_failure()
+ self.record_failure(details=details)
def __str__(self):
res = 'Assertions report: %s successes, %s failures' % (self.successes, self.failures)
if rec_src_count:
count = int(rec_src_count)
if len(ids) != count:
- self.assertion_report.record_failure()
msg = 'assertion "%s" failed!\n' \
' Incorrect search count:\n' \
' expected count: %d\n' \
- ' obtained count: %d\n' \
- % (rec_string, count, len(ids))
- _logger.error(msg)
+ ' obtained count: %d\n'
+ msg_args = (rec_string, count, len(ids))
+ _logger.error(msg, msg_args)
+ self.assertion_report.record_failure(details=dict(module=self.module,
+ msg=msg,
+ msg_args=msg_args))
return
assert ids is not None,\
expected_value = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True
expression_value = unsafe_eval(f_expr, globals_dict)
if expression_value != expected_value: # assertion failed
- self.assertion_report.record_failure()
msg = 'assertion "%s" failed!\n' \
' xmltag: %s\n' \
' expected value: %r\n' \
- ' obtained value: %r\n' \
- % (rec_string, etree.tostring(test), expected_value, expression_value)
- _logger.error(msg)
+ ' obtained value: %r\n'
+ msg_args = (rec_string, etree.tostring(test), expected_value, expression_value)
+ self.assertion_report.record_failure(details=dict(module=self.module,
+ msg=msg,
+ msg_args=msg_args))
+ _logger.error(msg, msg_args)
return
else: # all tests were successful for this assertion tag (no break)
self.assertion_report.record_success()
row.setdefault('tnrs', []).append((type, name, res_id))
row.setdefault('comments', set()).update(comments)
- for src, row in grouped_rows.items():
+ for src, row in sorted(grouped_rows.items()):
if not lang:
# translation template, so no translation value
row['translation'] = ''
except (IOError, etree.XMLSyntaxError):
_logger.exception("couldn't export translation for report %s %s %s", name, report_type, fname)
+ elif model == 'ir.model':
+ model_pool = pool.get(obj.model)
+ if model_pool:
+ push_translation(module, 'code', '_description', 0, model_pool._description)
+
for field_name,field_def in obj._table._columns.items():
if field_def.translate:
name = model + "," + field_name
# -*- coding: utf-8 -*-
+import os
import threading
import types
import time # used to eval time.strftime expressions
from datetime import datetime, timedelta
import logging
+from copy import deepcopy
import openerp.pooler as pooler
import openerp.sql_db as sql_db
import misc
return node
def _log_assert_failure(self, msg, *args):
- self.assertion_report.record_failure()
+ from openerp.modules import module # cannot be made before (loop)
+ basepath = module.get_module_path(self.module)
+ self.assertion_report.record_failure(
+ details=dict(module=self.module,
+ testfile=os.path.relpath(self.filename, basepath),
+ msg=msg,
+ msg_args=deepcopy(args)))
_logger.error(msg, *args)
def _get_assertion_id(self, assertion):