diff --git a/addons/account/__init__.py b/addons/account/__init__.py index 36cc1c85b5a..a243b94c2da 100644 --- a/addons/account/__init__.py +++ b/addons/account/__init__.py @@ -35,5 +35,5 @@ import product import ir_sequence import company import res_currency - +import edi # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 9445688ec6d..ec61248e147 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -53,7 +53,7 @@ module named account_voucher. 'website': 'http://www.openerp.com', 'images' : ['images/accounts.jpeg','images/bank_statement.jpeg','images/cash_register.jpeg','images/chart_of_accounts.jpeg','images/customer_invoice.jpeg','images/journal_entries.jpeg'], 'init_xml': [], - "depends" : ["base_setup", "product", "analytic", "process","board"], + "depends" : ["base_setup", "product", "analytic", "process", "board", "edi"], 'update_xml': [ 'security/account_security.xml', 'security/ir.model.access.csv', @@ -123,7 +123,9 @@ module named account_voucher. 'board_account_view.xml', "wizard/account_report_profit_loss_view.xml", "wizard/account_report_balance_sheet_view.xml", - "account_bank_view.xml" + "edi/invoice_action_data.xml", + "account_bank_view.xml", + "account_pre_install.yml" ], 'demo_xml': [ 'demo/account_demo.xml', @@ -145,10 +147,9 @@ module named account_voucher. 'test/account_fiscalyear_close.yml', 'test/account_bank_statement.yml', 'test/account_cash_statement.yml', + 'test/test_edi_invoice.yml', 'test/account_report.yml', - - - ], + ], 'installable': True, 'active': False, 'certificate': '0080331923549', diff --git a/addons/account/account.py b/addons/account/account.py index d4712c656a4..8aa1569520e 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -404,7 +404,7 @@ class account_account(osv.osv): return True _columns = { - 'name': fields.char('Name', size=128, required=True, select=True), + 'name': fields.char('Name', size=256, required=True, select=True), 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Forces all moves for this account to have this secondary currency."), 'code': fields.char('Code', size=64, required=True, select=1), 'type': fields.selection([ @@ -431,9 +431,9 @@ class account_account(osv.osv): 'debit': fields.function(__compute, fnct_inv=_set_credit_debit, digits_compute=dp.get_precision('Account'), string='Debit', multi='balance'), 'foreign_balance': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Foreign Balance', multi='balance', help="Total amount (in Secondary currency) for transactions held in secondary currency for this account."), - 'adjusted_balance': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Adjusted Balance', multi='balance', + 'adjusted_balance': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Adjusted Balance', multi='balance', help="Total amount (in Company currency) for transactions held in secondary currency for this account."), - 'unrealized_gain_loss': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Unrealized Gain or Loss', multi='balance', + 'unrealized_gain_loss': fields.function(__compute, digits_compute=dp.get_precision('Account'), string='Unrealized Gain or Loss', multi='balance', help="Value of Loss or Gain due to changes in exchange rate when doing multi-currency transactions."), 'reconcile': fields.boolean('Allow Reconciliation', help="Check this box if this account allows reconciliation of journal items."), 'exchange_rate': fields.related('currency_id', 'rate', type='float', string='Exchange Rate', digits=(12,6)), @@ -461,7 +461,7 @@ class account_account(osv.osv): } _defaults = { - 'type': 'view', + 'type': 'other', 'reconcile': False, 'active': True, 'currency_mode': 'current', @@ -716,6 +716,19 @@ class account_journal(osv.osv): _order = 'code' + def _check_currency(self, cr, uid, ids, context=None): + for journal in self.browse(cr, uid, ids, context=context): + if journal.currency: + if journal.default_credit_account_id and not journal.default_credit_account_id.currency_id.id == journal.currency.id: + return False + if journal.default_debit_account_id and not journal.default_debit_account_id.currency_id.id == journal.currency.id: + return False + return True + + _constraints = [ + (_check_currency, 'Configuration error! The currency chosen should be shared by the default accounts too.', ['currency','default_debit_account_id','default_credit_account_id']), + ] + def copy(self, cr, uid, id, default={}, context=None, done_list=[], local=False): journal = self.browse(cr, uid, id, context=context) if not default: @@ -848,19 +861,6 @@ class account_fiscalyear(osv.osv): } _order = "date_start" - def _check_fiscal_year(self, cr, uid, ids, context=None): - current_fiscal_yr = self.browse(cr, uid, ids, context=context)[0] - obj_fiscal_ids = self.search(cr, uid, [('company_id', '=', current_fiscal_yr.company_id.id)], context=context) - obj_fiscal_ids.remove(ids[0]) - data_fiscal_yr = self.browse(cr, uid, obj_fiscal_ids, context=context) - - for old_fy in data_fiscal_yr: - if old_fy.company_id.id == current_fiscal_yr['company_id'].id: - # Condition to check if the current fiscal year falls in between any previously defined fiscal year - if old_fy.date_start <= current_fiscal_yr['date_start'] <= old_fy.date_stop or \ - old_fy.date_start <= current_fiscal_yr['date_stop'] <= old_fy.date_stop: - return False - return True def _check_duration(self, cr, uid, ids, context=None): obj_fy = self.browse(cr, uid, ids[0], context=context) @@ -869,8 +869,7 @@ class account_fiscalyear(osv.osv): return True _constraints = [ - (_check_duration, 'Error! The start date of the fiscal year must be before his end date.', ['date_start','date_stop']), - (_check_fiscal_year, 'Error! You can not define overlapping fiscal years for the same company.',['date_start', 'date_stop']) + (_check_duration, 'Error! The start date of the fiscal year must be before his end date.', ['date_start','date_stop']) ] def create_period3(self, cr, uid, ids, context=None): @@ -2001,8 +2000,11 @@ class account_tax(osv.osv): cur_price_unit+=amount2 return res - def compute_all(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None): + def compute_all(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None, force_excluded=False): """ + :param force_excluded: boolean used to say that we don't want to consider the value of field price_include of + tax. It's used in encoding by line where you don't matter if you encoded a tax with that boolean to True or + False RETURN: { 'total': 0.0, # Total without taxes 'total_included: 0.0, # Total with taxes @@ -2014,10 +2016,10 @@ class account_tax(osv.osv): tin = [] tex = [] for tax in taxes: - if tax.price_include: - tin.append(tax) - else: + if not tax.price_include or force_excluded: tex.append(tax) + else: + tin.append(tax) tin = self.compute_inv(cr, uid, tin, price_unit, quantity, address_id=address_id, product=product, partner=partner) for r in tin: totalex -= r.get('amount', 0.0) @@ -2183,6 +2185,7 @@ class account_model(osv.osv): account_move_obj = self.pool.get('account.move') account_move_line_obj = self.pool.get('account.move.line') pt_obj = self.pool.get('account.payment.term') + period_obj = self.pool.get('account.period') if context is None: context = {} @@ -2190,14 +2193,14 @@ class account_model(osv.osv): if datas.get('date', False): context.update({'date': datas['date']}) - period_id = self.pool.get('account.period').find(cr, uid, dt=context.get('date', False)) - if not period_id: - raise osv.except_osv(_('No period found !'), _('Unable to find a valid period !')) - period_id = period_id[0] - move_date = context.get('date', time.strftime('%Y-%m-%d')) move_date = datetime.strptime(move_date,"%Y-%m-%d") for model in self.browse(cr, uid, ids, context=context): + ctx = context.copy() + ctx.update({'company_id': model.company_id.id}) + period_ids = period_obj.find(cr, uid, dt=context.get('date', False), context=ctx) + period_id = period_ids and period_ids[0] or False + ctx.update({'journal_id': model.journal_id.id,'period_id': period_id}) try: entry['name'] = model.name%{'year': move_date.strftime('%Y'), 'month': move_date.strftime('%m'), 'date': move_date.strftime('%Y-%m')} except: @@ -2246,9 +2249,7 @@ class account_model(osv.osv): 'date': context.get('date',time.strftime('%Y-%m-%d')), 'date_maturity': date_maturity }) - c = context.copy() - c.update({'journal_id': model.journal_id.id,'period_id': period_id}) - account_move_line_obj.create(cr, uid, val, context=c) + account_move_line_obj.create(cr, uid, val, context=ctx) return move_ids @@ -2395,7 +2396,7 @@ class account_account_template(osv.osv): _description ='Templates for Accounts' _columns = { - 'name': fields.char('Name', size=128, required=True, select=True), + 'name': fields.char('Name', size=256, required=True, select=True), 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Forces all moves for this account to have this secondary currency."), 'code': fields.char('Code', size=64, select=1), 'type': fields.selection([ diff --git a/addons/account/account_bank.py b/addons/account/account_bank.py index 2a9e432894f..acf070a3ad7 100644 --- a/addons/account/account_bank.py +++ b/addons/account/account_bank.py @@ -42,8 +42,12 @@ class bank(osv.osv): return (bank.bank_name or '') + ' ' + bank.acc_number def post_write(self, cr, uid, ids, context={}): + if isinstance(ids, (int, long)): + ids = [ids] + obj_acc = self.pool.get('account.account') obj_data = self.pool.get('ir.model.data') + for bank in self.browse(cr, uid, ids, context): if bank.company_id and not bank.journal_id: # Find the code and parent of the bank account to create diff --git a/addons/account/account_installer.xml b/addons/account/account_installer.xml index 230078c4e33..e249f8fa872 100644 --- a/addons/account/account_installer.xml +++ b/addons/account/account_installer.xml @@ -57,7 +57,7 @@ Accounting 5 - + diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index e445e3f7bc8..27881bf1280 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -251,7 +251,7 @@ class account_invoice(osv.osv): 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}), 'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}), 'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}), - 'check_total': fields.float('Total', digits_compute=dp.get_precision('Account'), states={'open':[('readonly',True)],'close':[('readonly',True)]}), + 'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), states={'open':[('readonly',True)],'close':[('readonly',True)]}), 'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean', store={ 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ? @@ -286,6 +286,9 @@ class account_invoice(osv.osv): 'internal_number': False, 'user_id': lambda s, cr, u, c: u, } + _sql_constraints = [ + ('number_uniq', 'unique(number, company_id)', 'Invoice Number must be unique per Company!'), + ] def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False): journal_obj = self.pool.get('account.journal') @@ -795,6 +798,7 @@ class account_invoice(osv.osv): """Creates invoice related analytics and financial move lines""" ait_obj = self.pool.get('account.invoice.tax') cur_obj = self.pool.get('res.currency') + period_obj = self.pool.get('account.period') context = {} for inv in self.browse(cr, uid, ids): if not inv.journal_id.sequence_id: @@ -923,10 +927,10 @@ class account_invoice(osv.osv): 'narration':inv.comment } period_id = inv.period_id and inv.period_id.id or False + ctx.update({'company_id': inv.company_id.id}) if not period_id: - period_ids = self.pool.get('account.period').search(cr, uid, [('date_start','<=',inv.date_invoice or time.strftime('%Y-%m-%d')),('date_stop','>=',inv.date_invoice or time.strftime('%Y-%m-%d')), ('company_id', '=', inv.company_id.id)]) - if period_ids: - period_id = period_ids[0] + period_ids = period_obj.find(cr, uid, inv.date_invoice, context=ctx) + period_id = period_ids and period_ids[0] or False if period_id: move['period_id'] = period_id for i in line: @@ -1323,9 +1327,9 @@ class account_invoice_line(osv.osv): raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") ) if not product: if type in ('in_invoice', 'in_refund'): - return {'value': {'categ_id': False}, 'domain':{'product_uom':[]}} + return {'value': {}, 'domain':{'product_uom':[]}} else: - return {'value': {'price_unit': 0.0, 'categ_id': False}, 'domain':{'product_uom':[]}} + return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}} part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) fpos_obj = self.pool.get('account.fiscal.position') fpos = fposition_id and fpos_obj.browse(cr, uid, fposition_id, context=context) or False @@ -1378,7 +1382,6 @@ class account_invoice_line(osv.osv): if res2: domain = {'uos_id':[('category_id','=',res2 )]} - result['categ_id'] = res.categ_id.id res_final = {'value':result, 'domain':domain} if not company_id or not currency_id: diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index efc7a24031c..54a60ee1b57 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -153,7 +153,7 @@ - - + @@ -237,4 +219,28 @@ + + + \ No newline at end of file diff --git a/addons/point_of_sale/test/01_order_to_payment.yml b/addons/point_of_sale/test/01_order_to_payment.yml index fd8ffbaa3e0..7c6b4817666 100644 --- a/addons/point_of_sale/test/01_order_to_payment.yml +++ b/addons/point_of_sale/test/01_order_to_payment.yml @@ -74,7 +74,7 @@ - I click on the "Make Payment" wizard to pay the PoS order with a partial amount of 100.0 EUR - - !record {model: pos.make.payment, id: pos_make_payment_0, context: {'active_id': ref('pos_order_pos0'), 'active_ids': [ref('pos_order_pos0')]} }: + !record {model: pos.make.payment, id: pos_make_payment_0, context: '{"active_id": ref("pos_order_pos0"), "active_ids": [ref("pos_order_pos0")]}' }: amount: 100.0 - I click on the validate button to register the payment. @@ -95,7 +95,7 @@ - I pay the remaining balance. - - !record {model: pos.make.payment, id: pos_make_payment_1, context: {'active_id': [ref('pos_order_pos0')], 'active_ids': [ref('pos_order_pos0')]} }: + !record {model: pos.make.payment, id: pos_make_payment_1, context: '{"active_id": ref("pos_order_pos0"), "active_ids": [ref("pos_order_pos0")]}' }: amount: !eval > (450*2 + 300*3*1.05)*0.95-100.0 - diff --git a/addons/point_of_sale/test/02_order_to_invoice.yml b/addons/point_of_sale/test/02_order_to_invoice.yml index 3436e176678..0de4e518ebd 100644 --- a/addons/point_of_sale/test/02_order_to_invoice.yml +++ b/addons/point_of_sale/test/02_order_to_invoice.yml @@ -18,7 +18,7 @@ - I click on the "Make Payment" wizard to pay the PoS order - - !record {model: pos.make.payment, id: pos_make_payment_2, context: {'active_id': ref('pos_order_pos1'), 'active_ids': [ref('pos_order_pos1')]} }: + !record {model: pos.make.payment, id: pos_make_payment_2, context: '{"active_id": ref("pos_order_pos1"), "active_ids": [ref("pos_order_pos1")]}' }: amount: !eval > (450*2 + 300*3*1.05)*0.95 - diff --git a/addons/procurement/board_mrp_procurement_view.xml b/addons/procurement/board_mrp_procurement_view.xml index bf11fe79166..b45edd3cbdd 100644 --- a/addons/procurement/board_mrp_procurement_view.xml +++ b/addons/procurement/board_mrp_procurement_view.xml @@ -16,8 +16,8 @@ form - - + + diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index 3c22b2838ef..db953c8a319 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -202,6 +202,13 @@ class product_pricelist(osv.osv): else: categ_where = '(categ_id IS NULL)' + if partner: + partner_where = 'base <> -2 OR %s IN (SELECT name FROM product_supplierinfo WHERE product_id = %s) ' + partner_args = (partner, product_id) + else: + partner_where = 'base <> -2 ' + partner_args = () + cr.execute( 'SELECT i.*, pl.currency_id ' 'FROM product_pricelist_item AS i, ' @@ -209,11 +216,12 @@ class product_pricelist(osv.osv): 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = %s) ' 'AND (product_id IS NULL OR product_id = %s) ' 'AND (' + categ_where + ' OR (categ_id IS NULL)) ' + 'AND (' + partner_where + ') ' 'AND price_version_id = %s ' 'AND (min_quantity IS NULL OR min_quantity <= %s) ' 'AND i.price_version_id = v.id AND v.pricelist_id = pl.id ' 'ORDER BY sequence', - (tmpl_id, product_id, pricelist_version_ids[0], qty)) + (tmpl_id, product_id) + partner_args + (pricelist_version_ids[0], qty)) res1 = cr.dictfetchall() uom_price_already_computed = False for res in res1: @@ -296,148 +304,6 @@ class product_pricelist(osv.osv): res.update({'item_id': {ids[-1]: res_multi.get('item_id', ids[-1])}}) return res - def price_get_old(self, cr, uid, ids, prod_id, qty, partner=None, context=None): - ''' - context = { - 'uom': Unit of Measure (int), - 'partner': Partner ID (int), - 'date': Date of the pricelist (%Y-%m-%d), - } - ''' - price = False - item_id = 0 - if context is None: - context = {} - currency_obj = self.pool.get('res.currency') - product_obj = self.pool.get('product.product') - supplierinfo_obj = self.pool.get('product.supplierinfo') - price_type_obj = self.pool.get('product.price.type') - - if context and ('partner_id' in context): - partner = context['partner_id'] - context['partner_id'] = partner - date = time.strftime('%Y-%m-%d') - if context and ('date' in context): - date = context['date'] - result = {} - result['item_id'] = {} - for id in ids: - cr.execute('SELECT * ' \ - 'FROM product_pricelist_version ' \ - 'WHERE pricelist_id = %s AND active=True ' \ - 'AND (date_start IS NULL OR date_start <= %s) ' \ - 'AND (date_end IS NULL OR date_end >= %s) ' \ - 'ORDER BY id LIMIT 1', (id, date, date)) - plversion = cr.dictfetchone() - - if not plversion: - raise osv.except_osv(_('Warning !'), - _('No active version for the selected pricelist !\n' \ - 'Please create or activate one.')) - - cr.execute('SELECT id, categ_id ' \ - 'FROM product_template ' \ - 'WHERE id = (SELECT product_tmpl_id ' \ - 'FROM product_product ' \ - 'WHERE id = %s)', (prod_id,)) - tmpl_id, categ = cr.fetchone() - categ_ids = [] - while categ: - categ_ids.append(str(categ)) - cr.execute('SELECT parent_id ' \ - 'FROM product_category ' \ - 'WHERE id = %s', (categ,)) - categ = cr.fetchone()[0] - if str(categ) in categ_ids: - raise osv.except_osv(_('Warning !'), - _('Could not resolve product category, ' \ - 'you have defined cyclic categories ' \ - 'of products!')) - if categ_ids: - categ_where = '(categ_id IN (' + ','.join(categ_ids) + '))' - else: - categ_where = '(categ_id IS NULL)' - - cr.execute( - 'SELECT i.*, pl.currency_id ' - 'FROM product_pricelist_item AS i, ' - 'product_pricelist_version AS v, product_pricelist AS pl ' - 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = %s) ' - 'AND (product_id IS NULL OR product_id = %s) ' - 'AND (' + categ_where + ' OR (categ_id IS NULL)) ' - 'AND price_version_id = %s ' - 'AND (min_quantity IS NULL OR min_quantity <= %s) ' - 'AND i.price_version_id = v.id AND v.pricelist_id = pl.id ' - 'ORDER BY sequence', - (tmpl_id, prod_id, plversion['id'], qty)) - res1 = cr.dictfetchall() - - for res in res1: - item_id = 0 - if res: - if res['base'] == -1: - if not res['base_pricelist_id']: - price = 0.0 - else: - price_tmp = self.price_get(cr, uid, - [res['base_pricelist_id']], prod_id, - qty, context=context)[res['base_pricelist_id']] - ptype_src = self.browse(cr, uid, - res['base_pricelist_id']).currency_id.id - price = currency_obj.compute(cr, uid, ptype_src, - res['currency_id'], price_tmp, round=False) - break - elif res['base'] == -2: - where = [] - if partner: - where = [('name', '=', partner) ] - sinfo = supplierinfo_obj.search(cr, uid, - [('product_id', '=', tmpl_id)] + where) - price = 0.0 - if sinfo: - cr.execute('SELECT * ' \ - 'FROM pricelist_partnerinfo ' \ - 'WHERE suppinfo_id IN %s' \ - 'AND min_quantity <= %s ' \ - 'ORDER BY min_quantity DESC LIMIT 1', (tuple(sinfo),qty,)) - res2 = cr.dictfetchone() - if res2: - price = res2['price'] - break - else: - price_type = price_type_obj.browse(cr, uid, int(res['base'])) - price = currency_obj.compute(cr, uid, - price_type.currency_id.id, res['currency_id'], - product_obj.price_get(cr, uid, [prod_id], - price_type.field, context=context)[prod_id], round=False, context=context) - - if price: - price_limit = price - - price = price * (1.0+(res['price_discount'] or 0.0)) - price = rounding(price, res['price_round']) - price += (res['price_surcharge'] or 0.0) - if res['price_min_margin']: - price = max(price, price_limit+res['price_min_margin']) - if res['price_max_margin']: - price = min(price, price_limit+res['price_max_margin']) - item_id = res['id'] - break - - else: - # False means no valid line found ! But we may not raise an - # exception here because it breaks the search - price = False - result[id] = price - result['item_id'] = {id: item_id} - if context and ('uom' in context): - product = product_obj.browse(cr, uid, prod_id) - uom = product.uos_id or product.uom_id - result[id] = self.pool.get('product.uom')._compute_price(cr, - uid, uom.id, result[id], context['uom']) - - return result - product_pricelist() diff --git a/addons/product/product.py b/addons/product/product.py index 882a77e6f12..ecf1761318e 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -334,7 +334,7 @@ class product_template(osv.osv): def onchange_uom(self, cursor, user, ids, uom_id,uom_po_id): if uom_id: return {'value': {'uom_po_id': uom_id}} - return False + return {} def write(self, cr, uid, ids, vals, context=None): if 'uom_po_id' in vals: diff --git a/addons/product/product_data.xml b/addons/product/product_data.xml index fc085aab922..122c8594de4 100644 --- a/addons/product/product_data.xml +++ b/addons/product/product_data.xml @@ -23,8 +23,8 @@ + Resource: product.uom + --> PCE diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml index a0669795ad5..ac9ae8c5ec3 100644 --- a/addons/product/product_view.xml +++ b/addons/product/product_view.xml @@ -376,7 +376,6 @@ Products by Categories - diff --git a/addons/product/test/product_test.yml b/addons/product/test/product_test.yml index f19b6a4a63c..ae75a552c79 100644 --- a/addons/product/test/product_test.yml +++ b/addons/product/test/product_test.yml @@ -5,7 +5,6 @@ name: 20KG uom_type: bigger category_id: product.product_uom_categ_kgm - rounding: 0.010 factor_inv: 20 - I create a 10KG UOM for 'Sugar' @@ -14,14 +13,12 @@ name: 10KG uom_type: bigger category_id: product.product_uom_categ_kgm - rounding: 0.010 factor_inv: 10 - I create a new product 'Sugar' in 20KG UOM. - !record {model: product.product, id: product_sugar_id1}: categ_id: 'product.product_category_rawmaterial0' - cost_method: standard name: Sugar 20KG procure_method: make_to_order standard_price: 400.0 diff --git a/addons/project/board_project_view.xml b/addons/project/board_project_view.xml index f2c44fab574..a58557c94ba 100644 --- a/addons/project/board_project_view.xml +++ b/addons/project/board_project_view.xml @@ -81,16 +81,16 @@ form
- - - - - - - - - - + + + + + + + + + +
diff --git a/addons/project/project.py b/addons/project/project.py index 1fc83771005..abebd2d35ff 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -75,11 +75,14 @@ class project(osv.osv): def onchange_partner_id(self, cr, uid, ids, part=False, context=None): partner_obj = self.pool.get('res.partner') if not part: - return {'value':{'contact_id': False, 'pricelist_id': False}} + return {'value':{'contact_id': False}} addr = partner_obj.address_get(cr, uid, [part], ['contact']) - pricelist = partner_obj.read(cr, uid, part, ['property_product_pricelist'], context=context) - pricelist_id = pricelist.get('property_product_pricelist', False) and pricelist.get('property_product_pricelist')[0] or False - return {'value':{'contact_id': addr['contact'], 'pricelist_id': pricelist_id}} + val = {'contact_id': addr['contact']} + if 'pricelist_id' in self.fields_get(cr, uid, context=context): + pricelist = partner_obj.read(cr, uid, part, ['property_product_pricelist'], context=context) + pricelist_id = pricelist.get('property_product_pricelist', False) and pricelist.get('property_product_pricelist')[0] or False + val['pricelist_id'] = pricelist_id + return {'value': val} def _progress_rate(self, cr, uid, ids, names, arg, context=None): res = {}.fromkeys(ids, 0.0) @@ -421,21 +424,55 @@ class task(osv.osv): _log_create = True _date_name = "date_start" - def _read_group_type_id(self, cr, uid, ids, domain, context=None): - context = context or {} - stage_obj = self.pool.get('project.task.type') - stage_ids = stage_obj.search(cr, uid, ['|',('id','in',ids)] + [('project_default','=',1)], context=context) - return stage_obj.name_get(cr, uid, stage_ids, context=context) - def _read_group_user_id(self, cr, uid, ids, domain, context={}): - context = context or {} - if type(context.get('project_id', None)) not in (int, long): - return None - proj = self.pool.get('project.project').browse(cr, uid, context['project_id'], context=context) - ids += map(lambda x: x.id, proj.members) - stage_obj = self.pool.get('res.users') - stage_ids = stage_obj.search(cr, uid, [('id','in',ids)], context=context) - return stage_obj.name_get(cr, uid, ids, context=context) + def _resolve_project_id_from_context(self, cr, uid, context=None): + """Return ID of project based on the value of 'project_id' + context key, or None if it cannot be resolved to a single project. + """ + if context is None: context = {} + if type(context.get('project_id')) in (int, long): + project_id = context['project_id'] + return project_id + if isinstance(context.get('project_id'), basestring): + project_name = context['project_id'] + project_ids = self.pool.get('project.project').name_search(cr, uid, name=project_name) + if len(project_ids) == 1: + return project_ids[0][0] + + def _read_group_type_id(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): + stage_obj = self.pool.get('project.task.type') + project_id = self._resolve_project_id_from_context(cr, uid, context=context) + order = stage_obj._order + access_rights_uid = access_rights_uid or uid + if read_group_order == 'type_id desc': + # lame way to allow reverting search, should just work in the trivial case + order = '%s desc' % order + if project_id: + domain = ['|', ('id','in',ids), ('project_ids','in',project_id)] + else: + domain = ['|', ('id','in',ids), ('project_default','=',1)] + stage_ids = stage_obj._search(cr, uid, domain, order=order, access_rights_uid=access_rights_uid, context=context) + result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) + # restore order of the search + result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) + return result + + def _read_group_user_id(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): + res_users = self.pool.get('res.users') + project_id = self._resolve_project_id_from_context(cr, uid, context=context) + access_rights_uid = access_rights_uid or uid + if project_id: + ids += self.pool.get('project.project').read(cr, access_rights_uid, project_id, ['members'], context=context)['members'] + order = res_users._order + # lame way to allow reverting search, should just work in the trivial case + if read_group_order == 'user_id desc': + order = '%s desc' % order + # de-duplicate and apply search order + ids = res_users._search(cr, uid, [('id','in',ids)], order=order, access_rights_uid=access_rights_uid, context=context) + result = res_users.name_get(cr, access_rights_uid, ids, context=context) + # restore order of the search + result.sort(lambda x,y: cmp(ids.index(x[0]), ids.index(y[0]))) + return result _group_by_full = { 'type_id': _read_group_type_id, @@ -557,7 +594,12 @@ class task(osv.osv): 'state': fields.selection([('draft', 'New'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done'), ('cancelled', 'Cancelled')], 'State', readonly=True, required=True, help='If the task is created the state is \'Draft\'.\n If the task is started, the state becomes \'In Progress\'.\n If review is needed the task is in \'Pending\' state.\ \n If the task is over, the states is set to \'Done\'.'), - 'kanban_state': fields.selection([('blocked', 'Blocked'),('normal', 'Normal'),('done', 'Done')], 'Kanban State', readonly=True, required=False), + 'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready To Pull')], 'Kanban State', + help="A task's kanban state indicates special situations affecting it:\n" + " * Normal is the default situation\n" + " * Blocked indicates something is preventing the progress of this task\n" + " * Ready To Pull indicates the task is ready to be pulled to the next stage", + readonly=True, required=False), 'create_date': fields.datetime('Create Date', readonly=True,select=True), 'date_start': fields.datetime('Starting Date',select=True), 'date_end': fields.datetime('Ending Date',select=True), @@ -837,37 +879,39 @@ class task(osv.osv): self.write(cr, uid, ids, {'state': 'draft'}, context=context) return True - def do_delegate(self, cr, uid, task_id, delegate_data={}, context=None): + def do_delegate(self, cr, uid, ids, delegate_data={}, context=None): """ Delegate Task to another users. """ - task = self.browse(cr, uid, task_id, context=context) - self.copy(cr, uid, task.id, { - 'name': delegate_data['name'], - 'user_id': delegate_data['user_id'], - 'planned_hours': delegate_data['planned_hours'], - 'remaining_hours': delegate_data['planned_hours'], - 'parent_ids': [(6, 0, [task.id])], - 'state': 'draft', - 'description': delegate_data['new_task_description'] or '', - 'child_ids': [], - 'work_ids': [] - }, context=context) - newname = delegate_data['prefix'] or '' - self.write(cr, uid, [task.id], { - 'remaining_hours': delegate_data['planned_hours_me'], - 'planned_hours': delegate_data['planned_hours_me'] + (task.effective_hours or 0.0), - 'name': newname, - }, context=context) - if delegate_data['state'] == 'pending': - self.do_pending(cr, uid, [task.id], context) - else: - self.do_close(cr, uid, [task.id], context=context) - user_pool = self.pool.get('res.users') - delegate_user = user_pool.browse(cr, uid, delegate_data['user_id'], context=context) - message = _("The task '%s' has been delegated to %s.") % (delegate_data['name'], delegate_user.name) - self.log(cr, uid, task.id, message) - return True + assert delegate_data['user_id'], _("Delegated User should be specified") + delegrated_tasks = {} + for task in self.browse(cr, uid, ids, context=context): + delegrated_task_id = self.copy(cr, uid, task.id, { + 'name': delegate_data['name'], + 'project_id': delegate_data['project_id'] and delegate_data['project_id'][0] or False, + 'user_id': delegate_data['user_id'] and delegate_data['user_id'][0] or False, + 'planned_hours': delegate_data['planned_hours'] or 0.0, + 'parent_ids': [(6, 0, [task.id])], + 'state': 'draft', + 'description': delegate_data['new_task_description'] or '', + 'child_ids': [], + 'work_ids': [] + }, context=context) + newname = delegate_data['prefix'] or '' + task.write({ + 'remaining_hours': delegate_data['planned_hours_me'], + 'planned_hours': delegate_data['planned_hours_me'] + (task.effective_hours or 0.0), + 'name': newname, + }, context=context) + if delegate_data['state'] == 'pending': + self.do_pending(cr, uid, task.id, context=context) + elif delegate_data['state'] == 'done': + self.do_close(cr, uid, task.id, context=context) + + message = _("The task '%s' has been delegated to %s.") % (delegate_data['name'], delegate_data['user_id'][1]) + self.log(cr, uid, task.id, message) + delegrated_tasks[task.id] = delegrated_task_id + return delegrated_tasks def do_pending(self, cr, uid, ids, context={}): self.write(cr, uid, ids, {'state': 'pending'}, context=context) @@ -930,6 +974,20 @@ class task(osv.osv): def prev_type(self, cr, uid, ids, *args): return self._change_type(cr, uid, ids, False, *args) + # Overridden to reset the kanban_state to normal whenever + # the stage (type_id) of the task changes. + def write(self, cr, uid, ids, vals, context=None): + if isinstance(ids, (int, long)): + ids = [ids] + if vals and not 'kanban_state' in vals and 'type_id' in vals: + new_stage = vals.get('type_id') + vals_reset_kstate = dict(vals, kanban_state='normal') + for t in self.browse(cr, uid, ids, context=context): + write_vals = vals_reset_kstate if t.type_id != new_stage else vals + super(task,self).write(cr, uid, [t.id], write_vals, context=context) + return True + return super(task,self).write(cr, uid, ids, vals, context=context) + def unlink(self, cr, uid, ids, context=None): if context == None: context = {} diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml index d78f208526a..a3bfe9be673 100644 --- a/addons/project/project_data.xml +++ b/addons/project/project_data.xml @@ -1,30 +1,12 @@ - - - - - Project - project.project - - - - Project task - project.task - - - - + + Projects 3 - - + @@ -47,7 +29,5 @@ Deployment - - diff --git a/addons/project/project_demo.xml b/addons/project/project_demo.xml index ac4bc14c701..7592d24a145 100644 --- a/addons/project/project_demo.xml +++ b/addons/project/project_demo.xml @@ -210,7 +210,7 @@ Documentation - 06-02-2011 + 2011-02-06 @@ -241,7 +241,6 @@ Training and Presentation - 09-21-2011 diff --git a/addons/project/wizard/project_task_delegate.py b/addons/project/wizard/project_task_delegate.py index eaf5dea3084..0b4457d5e7f 100644 --- a/addons/project/wizard/project_task_delegate.py +++ b/addons/project/wizard/project_task_delegate.py @@ -31,14 +31,23 @@ class project_task_delegate(osv.osv_memory): _columns = { 'name': fields.char('Delegated Title', size=64, required=True, help="New title of the task delegated to the user"), - 'prefix': fields.char('Your Task Title', size=64, required=True, help="Title for your validation task"), + 'prefix': fields.char('Your Task Title', size=64, help="Title for your validation task"), + 'project_id': fields.many2one('project.project', 'Project', help="User you want to delegate this task to"), 'user_id': fields.many2one('res.users', 'Assign To', required=True, help="User you want to delegate this task to"), 'new_task_description': fields.text('New Task Description', help="Reinclude the description of the task in the task of the user"), 'planned_hours': fields.float('Planned Hours', help="Estimated time to close this task by the delegated user"), - 'planned_hours_me': fields.float('Hours to Validate', required=True, help="Estimated time for you to validate the work done by the user to whom you delegate this task"), - 'state': fields.selection([('pending','Pending'), ('done','Done'), ], 'Validation State', required=True, help="New state of your own task. Pending will be reopened automatically when the delegated task is closed") + 'planned_hours_me': fields.float('Hours to Validate', help="Estimated time for you to validate the work done by the user to whom you delegate this task"), + 'state': fields.selection([('pending','Pending'), ('done','Done'), ], 'Validation State', help="New state of your own task. Pending will be reopened automatically when the delegated task is closed") } + def onchange_project_id(self, cr, uid, ids, project_id=False, context=None): + project_project = self.pool.get('project.project') + if not project_id: + return {'value':{'user_id': False}} + project = project_project.browse(cr, uid, project_id, context=context) + return {'value': {'user_id': project.user_id and project.user_id.id or False}} + + def default_get(self, cr, uid, fields, context=None): """ This function gets default values @@ -47,10 +56,15 @@ class project_task_delegate(osv.osv_memory): if context is None: context = {} record_id = context and context.get('active_id', False) or False + if not record_id: + return res task_pool = self.pool.get('project.task') task = task_pool.browse(cr, uid, record_id, context=context) task_name =tools.ustr(task.name) + if 'project_id' in fields: + res.update({'project_id': task.project_id and task.project_id.id}) + if 'name' in fields: if task_name.startswith(_('CHECK: ')): newname = tools.ustr(task_name).replace(_('CHECK: '), '') @@ -105,9 +119,17 @@ class project_task_delegate(osv.osv_memory): task_id = context.get('active_id', False) task_pool = self.pool.get('project.task') delegate_data = self.read(cr, uid, ids, context=context)[0] - delegate_data['user_id'] = delegate_data['user_id'][0] - delegate_data['name'] = tools.ustr(delegate_data['name']) - task_pool.do_delegate(cr, uid, task_id, delegate_data, context=context) - return {'type': 'ir.actions.act_window_close'} + delegated_tasks = task_pool.do_delegate(cr, uid, [task_id], delegate_data, context=context) + models_data = self.pool.get('ir.model.data') + + action_model, action_id = models_data.get_object_reference(cr, uid, 'project', 'action_view_task') + view_model, task_view_form_id = models_data.get_object_reference(cr, uid, 'project', 'view_task_form2') + view_model, task_view_tree_id = models_data.get_object_reference(cr, uid, 'project', 'view_task_tree2') + action = self.pool.get(action_model).read(cr, uid, action_id, context=context) + action['res_id'] = delegated_tasks[task_id] + action['view_id'] = False + action['views'] = [(task_view_form_id, 'form'), (task_view_tree_id, 'tree')] + action['help'] = False + return action project_task_delegate() diff --git a/addons/project/wizard/project_task_delegate_view.xml b/addons/project/wizard/project_task_delegate_view.xml index f9b27af05b3..072325c2b53 100644 --- a/addons/project/wizard/project_task_delegate_view.xml +++ b/addons/project/wizard/project_task_delegate_view.xml @@ -8,18 +8,19 @@ form
+ + - - - + + - + - - + + @@ -40,7 +41,6 @@ form form - {'record_id' : active_id} new diff --git a/addons/project_gtd/__openerp__.py b/addons/project_gtd/__openerp__.py index a9a1aecd0c5..761b2b44708 100644 --- a/addons/project_gtd/__openerp__.py +++ b/addons/project_gtd/__openerp__.py @@ -21,7 +21,7 @@ { - 'name': 'Todo List based on the Getting Things Done methodology', + 'name': 'Todo List based on the GTD methodology', 'version': '1.0', 'category': 'Project Management', 'complexity': "easy", diff --git a/addons/project_gtd/project_gtd.py b/addons/project_gtd/project_gtd.py index d28e5b1dcd6..bec70779551 100644 --- a/addons/project_gtd/project_gtd.py +++ b/addons/project_gtd/project_gtd.py @@ -106,25 +106,17 @@ class project_task(osv.osv): timebox_obj = self.pool.get('project.gtd.timebox') if (res['type'] == 'search') and context.get('gtd', False): tt = timebox_obj.browse(cr, uid, timebox_obj.search(cr,uid,[]), context=context) - search_extended ='''''' - search_extended += '''''' % (_('Inbox'),) - search_extended += '''''' + search_extended ='' for time in tt: if time.icon: icon = time.icon else : icon="" - search_extended += '''''' - search_extended += ''' - - - ''' + search_extended += '''\n''' + search_extended +='''''' + + res['arch'] = res['arch'].replace('', search_extended) - res['arch'] = unicode(res['arch'], 'utf8').replace('', search_extended) - attrs_sel = self.pool.get('project.gtd.context').name_search(cr, uid, '', [], context=context) - context_id_info = self.pool.get('project.task').fields_get(cr, uid, ['context_id'], context=context) - context_id_info['context_id']['selection'] = attrs_sel - res['fields'].update(context_id_info) return res project_task() diff --git a/addons/project_gtd/project_gtd_data.xml b/addons/project_gtd/project_gtd_data.xml index 659b0fe9cb2..7d495023f87 100644 --- a/addons/project_gtd/project_gtd_data.xml +++ b/addons/project_gtd/project_gtd_data.xml @@ -18,10 +18,6 @@ This Week terp-go-week - - This Month - terp-go-month - Long Term terp-project diff --git a/addons/project_gtd/project_gtd_view.xml b/addons/project_gtd/project_gtd_view.xml index a9166f7fbfa..66e7a8a69e3 100644 --- a/addons/project_gtd/project_gtd_view.xml +++ b/addons/project_gtd/project_gtd_view.xml @@ -82,7 +82,7 @@