[IMP] Contract management + fixes

bzr revid: fp@tinyerp.com-20111116145958-pzp36c49wh6028p9
This commit is contained in:
Fabien Pinckaers 2011-11-16 15:59:58 +01:00
parent 1d54432f87
commit 36acb24ce4
14 changed files with 194 additions and 277 deletions

View File

@ -91,15 +91,18 @@
</group>
<notebook colspan="4">
<page string="Account Data">
<field name="partner_id"/>
<field name="contact_id"/>
<field name="currency_id"/>
<newline/>
<field name="date_start"/>
<field name="date"/>
<newline/>
<field name="quantity_max"/>
<field name="user_id"/>
<group colspan="2" col="2">
<separator colspan="2" string="Contacts"/>
<field name="partner_id" on_change="on_change_partner_id(partner_id)"/>
<field name="contact_id"/>
<field name="user_id"/>
</group>
<group colspan="2" col="2" name="contract">
<separator colspan="2" string="Contract Data"/>
<field name="date_start"/>
<field name="date"/>
<field name="quantity_max"/>
</group>
</page>
<page string="Description">
<field colspan="4" name="description" nolabel="1"/>

View File

@ -376,13 +376,11 @@ class account_analytic_account(osv.osv):
def _is_overdue_quantity(self, cr, uid, ids, fieldnames, args, context=None):
result = dict.fromkeys(ids, 0)
for record in self.browse(cr, uid, ids, context=context):
if record.quantity == 0.0 and record.quantity_max == 0.0:
result[record.id] = 0
else:
if record.quantity_max > 0.0:
result[record.id] = int(record.quantity >= record.quantity_max)
else:
result[record.id] = 0
return result
def _get_analytic_account(self, cr, uid, ids, context=None):

View File

@ -1,7 +1,6 @@
<openerp>
<data>
<menuitem icon="terp-project" id="base.menu_main_pm" name="Project" sequence="10"/>
<menuitem id="menu_invoicing" name="Billing" parent="base.menu_main_pm" sequence="4" groups="base.group_extended"/>
<menuitem id="base.menu_invoiced" name="Invoicing" parent="base.menu_base_partner" sequence="5"/>
<record id="action_hr_tree_invoiced_all" model="ir.actions.act_window">
<field name="name">All Uninvoiced Entries</field>
@ -11,16 +10,7 @@
<field name="domain">[('invoice_id','=',False)]</field>
<field name="search_view_id" ref="account.view_account_analytic_line_filter"/>
</record>
<menuitem action="action_hr_tree_invoiced_all" id="menu_action_hr_tree_invoiced_all" parent="menu_invoicing" groups="analytic.group_analytic_accounting"/>
<record id="action_account_analytic_managed_overpassed" model="ir.actions.act_window">
<field name="name">Overpassed Accounts</field>
<field name="res_model">account.analytic.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
<field name="domain">[('date','&lt;=',time.strftime('%Y-%m-%d')),('state','=','open')]</field>
</record>
<menuitem action="action_account_analytic_managed_overpassed" id="menu_action_account_analytic_managed_overpassed" sequence="50" parent="menu_invoicing" groups="base.group_extended"/>
<menuitem action="action_hr_tree_invoiced_all" id="menu_action_hr_tree_invoiced_all" parent="base.menu_invoiced"/>
<record id="view_account_analytic_account_overdue_search" model="ir.ui.view">
<field name="name">account.analytic.account.search</field>
@ -29,11 +19,10 @@
<field name="arch" type="xml">
<search string="Analytic Account">
<group col="8" colspan="4">
<filter icon="terp-check" name="draft" string="Draft" domain="[('state','=','draft')]" help="Contracts not signed yet"/>
<filter icon="terp-camera_test" name="open" string="Open" domain="[('state','=','open')]" help="Contracts in progress"/>
<filter icon="terp-camera_test" name="open" string="Open" domain="[('state','in',('open','draft'))]" help="Contracts in progress"/>
<filter icon="terp-gtk-media-pause" name="pending" string="Pending" domain="[('state','=','pending')]" help="Pending contracts to renew with your customer"/>
<separator orientation="vertical"/>
<filter icon="terp-go-today" string="To Renew" domain="['|', '&amp;', ('date', '!=', False), ('date', '&lt;', time.strftime('%%Y-%%m-%%d')), ('is_overdue_quantity', '=', True)]"
<filter icon="terp-go-today" string="To Renew" domain="['|', '&amp;', ('date', '!=', False), ('date', '&lt;=', time.strftime('%%Y-%%m-%%d')), ('is_overdue_quantity', '=', True)]"
name="renew"
help="The contracts to be renewed because the deadline is passed or the working hours are higher than the allocated hours" />
<filter icon="terp-go-month"
@ -71,7 +60,7 @@
<record id="action_account_analytic_overdue" model="ir.actions.act_window">
<field name="name">Overdue Accounts</field>
<field name="name">Analytic Accounts/Contracts</field>
<field name="res_model">account.analytic.account</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
@ -80,7 +69,7 @@
<field name="search_view_id" ref="view_account_analytic_account_overdue_search"/>
<field name="help">You will find here the contracts to be renewed because the deadline is passed or the working hours are higher than the allocated hours. OpenERP automatically sets these analytic accounts to the pending state, in order to raise a warning during the timesheets recording. Salesmen should review all pending accounts and reopen or close the according to the negotiation with the customer.</field>
</record>
<menuitem action="action_account_analytic_overdue" id="menu_action_account_analytic_overdue" sequence="50" parent="sale.menu_invoiced" groups="base.group_extended"/>
<menuitem action="action_account_analytic_overdue" id="menu_action_account_analytic_overdue" sequence="50" parent="base.menu_invoiced"/>
</data>
</openerp>

View File

@ -8,6 +8,27 @@
Remove information on Account data => because they move on analysis sheet
create a page with invoicing informations
-->
<record id="account_analytic_account_invoice_stat_form" model="ir.ui.view">
<field name="name">account.analytic.account.invoice.stat.form.inherit</field>
<field name="model">account.analytic.account</field>
<field name="type">form</field>
<field name="inherit_id" ref="hr_timesheet_invoice.account_analytic_account_form_form"/>
<field eval="18" name="priority"/>
<field name="arch" type="xml">
<group name="invoice_stats" position="inside">
<field name="hours_qtt_non_invoiced"/>
<field name="ca_to_invoice"/>
<label string="" colspan="1"/>
<button
name="%(hr_timesheet_invoice.action_hr_timesheet_invoice_create_final)d"
string="Create Invoice"
type="action"
attrs="{'readonly':[('ca_to_invoice','=',0.0)]}"
icon="gtk-go-forward"/>
</group>
</field>
</record>
<record id="hr_timesheet.account_analytic_account_form_form" model="ir.ui.view">
<field name="name">account.analytic.account.invoice.form.inherit</field>
<field name="model">account.analytic.account</field>
@ -16,7 +37,7 @@
<field eval="18" name="priority"/>
<field name="arch" type="xml">
<notebook position="inside">
<page string="Analysis summary">
<page string="Invoice Analysis">
<separator colspan="4" string="Work done stats"/>
<field name="total_cost"/>
@ -39,10 +60,6 @@
<field name="last_invoice_date"/>
<field name="last_worked_invoiced_date"/>
<field name="last_worked_date"/>
<separator colspan="4" string="To be invoiced"/>
<field name="hours_qtt_non_invoiced"/>
<field name="ca_to_invoice"/>
</page>
<page string="Stats by month">
<field colspan="4" name="month_ids" nolabel="1">

View File

@ -150,7 +150,7 @@ class account_analytic_account(osv.osv):
_columns = {
'name': fields.char('Account Name', size=128, required=True),
'complete_name': fields.function(_complete_name_calc, type='char', string='Full Account Name'),
'code': fields.char('Account Code', size=24, select=True),
'code': fields.char('Code/Reference', size=24, select=True),
'type': fields.selection([('view','View'), ('normal','Normal')], 'Account Type', help='If you select the View Type, it means you won\'t allow to create journal entries using that account.'),
'description': fields.text('Description'),
'parent_id': fields.many2one('account.analytic.account', 'Parent Analytic Account', select=2),
@ -168,7 +168,7 @@ class account_analytic_account(osv.osv):
'date_start': fields.date('Date Start'),
'date': fields.date('Date End', select=True),
'company_id': fields.many2one('res.company', 'Company', required=False), #not required because we want to allow different companies to use the same chart of account, except for leaf accounts.
'state': fields.selection([('draft','New'),('open','Started'), ('pending','Pending'),('cancelled', 'Cancelled'),('close','Closed'),('template', 'Template')], 'State', required=True,
'state': fields.selection([('template', 'Template'),('draft','New'),('open','Open'), ('pending','Pending'),('cancelled', 'Cancelled'),('close','Closed')], 'State', required=True,
help='* When an account is created its in \'Draft\' state.\
\n* If any associated partner is there, it can be in \'Open\' state.\
\n* If any pending balance is there it can be in \'Pending\'. \
@ -217,6 +217,12 @@ class account_analytic_account(osv.osv):
default['line_ids'] = []
return super(account_analytic_account, self).copy(cr, uid, id, default, context=context)
def on_change_partner_id(self, cr, uid, id, partner_id, context={}):
if not partner_id:
return {'value': {'contact_id': False}}
addr = self.pool.get('res.partner').address_get(cr, uid, [partner_id], ['invoice'])
return {'value': {'contact_id': addr.get('invoice', False)}}
def on_change_company(self, cr, uid, id, company_id):
if not company_id:
return {}

View File

@ -63,12 +63,12 @@ class account_analytic_account(osv.osv):
_inherit = "account.analytic.account"
_columns = {
'pricelist_id': fields.many2one('product.pricelist', 'Sale Pricelist',
'pricelist_id': fields.many2one('product.pricelist', 'Customer Pricelist',
help="The product to invoice is defined on the employee form, the price will be deduced by this pricelist on the product."),
'amount_max': fields.float('Max. Invoice Price'),
'amount_invoiced': fields.function(_invoiced_calc, string='Invoiced Amount',
help="Total invoiced"),
'to_invoice': fields.many2one('hr_timesheet_invoice.factor', 'Reinvoice Costs',
'to_invoice': fields.many2one('hr_timesheet_invoice.factor', 'Invoice on Timesheet & Costs',
help="Fill this field if you plan to automatically generate invoices based " \
"on the costs in this analytic account: timesheets, expenses, ..." \
"You can configure an automatic invoice rate on analytic accounts."),
@ -76,7 +76,16 @@ class account_analytic_account(osv.osv):
_defaults = {
'pricelist_id': lambda self, cr, uid, ctx: ctx.get('pricelist_id', False),
}
def on_change_partner_id(self, cr, uid, id, partner_id, context={}):
res = super(account_analytic_account, self).on_change_partner_id(cr, uid, id, partner_id, context)
if (not res.get('value', False)) or not partner_id:
return res
part = self.pool.get('res.partner').browse(cr, uid, partner_id)
pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
if pricelist:
res['value']['pricelist_id'] = pricelist
return res
def set_close(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state':'close'}, context=context)

View File

@ -7,23 +7,27 @@
<field name="type">form</field>
<field name="inherit_id" ref="account.view_account_analytic_account_form"/>
<field name="arch" type="xml">
<field name="user_id" position="after">
<group colspan="4" col="4">
<separator colspan="4" string="Invoicing Data"/>
<field name="pricelist_id" domain="[('type','=','sale')]" widget="selection"/>
<field name="to_invoice" widget="selection" />
<group name="contract" position="after">
<group colspan="2" col="2" name="invoice_data">
<separator colspan="2" string="Invoicing Data"/>
<field name="to_invoice" widget="selection"/>
<field name="pricelist_id" domain="[('type','=','sale')]" attrs="{'required':[('to_invoice','!=',False)]}"/>
<field name="amount_max"/>
<field name="amount_invoiced"/>
<separator colspan="4"/>
<group col="9" colspan="8">
<field name="state" readonly="1"/>
<button name="set_cancel" string="Cancel" type="object" states="open,pending" icon="gtk-cancel"/>
<button name="set_open" string="Reactivate Account" type="object" states="pending,cancelled,close,draft" icon="gtk-ok"/>
<button name="set_pending" string="Pending" type="object" states="open" icon="gtk-media-pause"/>
<button name="set_close" string="Close" type="object" states="open,pending" icon="terp-dialog-close"/>
</group>
</group>
</field>
<group colspan="2" col="2" name="invoice_stats">
<separator colspan="4" string="Invoicing Statistics"/>
<field name="amount_invoiced"/>
</group>
<separator colspan="4"/>
<group col="9" colspan="8">
<field name="state" readonly="1" widget="statusbar"
statusbar_visible="open,pending,close" statusbar_colors='{"pending":"red", "template":"blue"}'/>
<button name="set_cancel" string="Cancel" type="object" states="open,pending" icon="gtk-cancel"/>
<button name="set_open" string="Reactivate Account" type="object" states="pending,cancelled,close,draft" icon="gtk-ok"/>
<button name="set_pending" string="Pending" type="object" states="open" icon="gtk-media-pause"/>
<button name="set_close" string="Close" type="object" states="open,pending" icon="terp-dialog-close"/>
</group>
</group>
</field>
</record>
@ -123,7 +127,7 @@
<field name="view_mode">tree,form</field>
<field name="domain">[]</field>
<field name="context">{'search_default_to_invoice': 1}</field>
<field name="search_view_id" ref="account.view_account_analytic_line_filter"/>
<field name="search_view_id" ref="account.view_account_analytic_line_filter"/>
<field name="help">This list shows you every task you can invoice to the customer. Select the lines and click the Action button to generate the invoices automatically.</field>
</record>
<menuitem

View File

@ -39,145 +39,21 @@ class final_invoice_create(osv.osv_memory):
'time': fields.boolean('Time spent', help='Display time in the history of works'),
'name': fields.boolean('Name of entry', help='Display detail of work in the invoice line.'),
'price': fields.boolean('Cost', help='Display cost of the item you reinvoice'),
'balance_product': fields.many2one('product.product', 'Balance product', help='The product that will be used to invoice the remaining amount'),
}
'product': fields.many2one('product.product', 'Product', help='The product that will be used to invoice the remaining amount'),
}
def do_create(self, cr, uid, ids, context=None):
data = self.read(cr, uid, ids, [], context=context)[0]
ids = self.pool.get('account.analytic.line').search(cr, uid, [('invoice_id','=',False),('to_invoice','<>', False), ('account_id', 'in', context['active_ids'])], context=context)
invs = self.pool.get('account.analytic.line').invoice_cost_create(cr, uid, ids, data, context=context)
mod_obj = self.pool.get('ir.model.data')
analytic_account_obj = self.pool.get('account.analytic.account')
res_partner_obj = self.pool.get('res.partner')
account_payment_term_obj = self.pool.get('account.payment.term')
invoice_obj = self.pool.get('account.invoice')
product_obj = self.pool.get('product.product')
fiscal_pos_obj = self.pool.get('account.fiscal.position')
invoice_line_obj = self.pool.get('account.invoice.line')
invoices = []
if context is None:
context = {}
result = mod_obj._get_id(cr, uid, 'account', 'view_account_invoice_filter')
res = mod_obj.read(cr, uid, result, ['res_id'], context=context)
data = self.browse(cr, uid, ids, context=context)[0]
balance_product = data.balance_product.id
account_ids = 'active_ids' in context and context['active_ids'] or []
for account in analytic_account_obj.browse(cr, uid, account_ids, context=context):
partner = account.partner_id
amount_total=0.0
if (not partner) or not (account.pricelist_id):
raise osv.except_osv(_('Analytic account incomplete'),
_('Please fill in the partner and pricelist field '
'in the analytic account:\n%s') % (account.name,))
date_due = False
if partner.property_payment_term:
pterm_list= account_payment_term_obj.compute(cr, uid,
partner.property_payment_term.id, value=1,
date_ref=time.strftime('%Y-%m-%d'))
if pterm_list:
pterm_list = [line[0] for line in pterm_list]
pterm_list.sort()
date_due = pterm_list[-1]
curr_invoice = {
'name': time.strftime('%d/%m/%Y')+' - '+account.name,
'partner_id': account.partner_id.id,
'address_contact_id': res_partner_obj.address_get(cr, uid, [account.partner_id.id], adr_pref=['contact'])['contact'],
'address_invoice_id': res_partner_obj.address_get(cr, uid, [account.partner_id.id], adr_pref=['invoice'])['invoice'],
'payment_term': partner.property_payment_term.id or False,
'account_id': partner.property_account_receivable.id,
'currency_id': account.pricelist_id.currency_id.id,
'date_due': date_due,
'fiscal_position': account.partner_id.property_account_position.id
}
last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context)
invoices.append(last_invoice)
context2 = context.copy()
context2['lang'] = partner.lang
cr.execute("""SELECT
line.product_id,
sum(line.amount),
line.general_account_id,
line.product_uom_id,
move_line.ref
FROM
account_analytic_line as line
LEFT JOIN account_move_line as move_line on (line.move_id=move_line.id)
LEFT JOIN account_analytic_journal as journal on (line.journal_id=journal.id)
WHERE
line.account_id = %s AND
line.move_id IS NOT NULL AND
journal.type = 'sale'
GROUP BY
line.product_id,
line.general_account_id,
line.product_uom_id,
move_line.ref""", (account.id,))
for product_id, amount, account_id, product_uom_id, ref in cr.fetchall():
product = product_obj.browse(cr, uid, product_id, context2)
if product:
taxes = product.taxes_id
else:
taxes = []
tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes)
if not account_id:
raise osv.except_osv(_("Configuration Error"), _("No income account defined for product '%s'") % product.name)
curr_line = {
'price_unit': -amount,
'quantity': 1.0,
'discount': 0.0,
'invoice_line_tax_id': [(6,0,tax)],
'invoice_id': last_invoice,
'name': ref or '' +(product and ' - '+product.name or ''),
'product_id': product_id,
'uos_id': product_uom_id,
'account_id': account_id,
'account_analytic_id': account.id
}
invoice_line_obj.create(cr, uid, curr_line, context=context)
if not balance_product:
raise osv.except_osv(_('Balance product needed'), _('Please fill a Balance product in the wizard'))
product = product_obj.browse(cr, uid, balance_product, context=context2)
taxes = product.taxes_id
tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes)
account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id
if not account_id:
raise osv.except_osv(_("Configuration Error"), _("No income account defined for product '%s'") % product.name)
curr_line = {
'price_unit': account.amount_max - amount_total,
'quantity': 1.0,
'discount': 0.0,
'invoice_line_tax_id': [(6,0,tax)],
'invoice_id': last_invoice,
'name': product.name,
'product_id': product.id,
'uos_id': product.uom_id.id,
'account_id': account_id,
'account_analytic_id': account.id
}
invoice_line_obj.create(cr, uid, curr_line, context=context)
if account.amount_max < amount_total:
invoice_obj.write(cr, uid, [last_invoice], {'type': 'out_refund',}, context=context)
cr.execute('update account_analytic_line set invoice_id=%s where invoice_id is null and account_id=%s', (last_invoice, account.id))
return {
'domain': "[('id','in', ["+','.join(map(str,invoices))+"])]",
'name': 'Invoices',
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.invoice',
'view_id': False,
'context': "{'type':'out_invoice'}",
'type': 'ir.actions.act_window',
'search_view_id': res['res_id']
}
act_obj = self.pool.get('ir.actions.act_window')
mod_ids = mod_obj.search(cr, uid, [('name', '=', 'action_invoice_tree1')], context=context)[0]
res_id = mod_obj.read(cr, uid, mod_ids, ['res_id'], context=context)['res_id']
act_win = act_obj.read(cr, uid, res_id, [], context=context)
act_win['domain'] = [('id','in',invs),('type','=','out_invoice')]
act_win['name'] = _('Invoices')
return act_win
final_invoice_create()

View File

@ -27,43 +27,18 @@ from tools.translate import _
## Create an invoice based on selected timesheet lines
#
#
# TODO: check unit of measure !!!
#
class hr_timesheet_invoice_create(osv.osv_memory):
class account_analytic_line(osv.osv):
_inherit = "account.analytic.line"
_name = 'hr.timesheet.invoice.create'
_description = 'Create invoice from timesheet'
_columns = {
'date': fields.boolean('Date', help='The real date of each work will be displayed on the invoice'),
'time': fields.boolean('Time spent', help='The time of each work done will be displayed on the invoice'),
'name': fields.boolean('Description', help='The detail of each work done will be displayed on the invoice'),
'price': fields.boolean('Cost', help='The cost of each work done will be displayed on the invoice. You probably don\'t want to check this'),
'product': fields.many2one('product.product', 'Product', help='Complete this field only if you want to force to use a specific product. Keep empty to use the real product that comes from the cost.'),
}
_defaults = {
'date': lambda *args: 1,
'name': lambda *args: 1
}
def view_init(self, cr, uid, fields, context=None):
"""
This function checks for precondition before wizard executes
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param fields: List of fields for default value
@param context: A standard dictionary for contextual values
"""
analytic_obj = self.pool.get('account.analytic.line')
data = context and context.get('active_ids', [])
for analytic in analytic_obj.browse(cr, uid, data, context=context):
if analytic.invoice_id:
raise osv.except_osv(_('Warning !'), _("Invoice is already linked to some of the analytic line(s)!"))
def do_create(self, cr, uid, ids, context=None):
mod_obj = self.pool.get('ir.model.data')
#
# data = {
# 'date': boolean
# 'time': boolean
# 'name': boolean
# 'price': boolean
# 'product': many2one id
# }
def invoice_cost_create(self, cr, uid, ids, data={}, context=None):
analytic_account_obj = self.pool.get('account.analytic.account')
res_partner_obj = self.pool.get('res.partner')
account_payment_term_obj = self.pool.get('account.payment.term')
@ -77,11 +52,9 @@ class hr_timesheet_invoice_create(osv.osv_memory):
invoices = []
if context is None:
context = {}
result = mod_obj._get_id(cr, uid, 'account', 'view_account_invoice_filter')
data = self.read(cr, uid, ids, [], context=context)[0]
account_ids = {}
for line in self.pool.get('account.analytic.line').browse(cr, uid, context['active_ids'], context=context):
for line in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context):
account_ids[line.account_id.id] = True
account_ids = account_ids.keys() #data['accounts']
@ -127,7 +100,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
"FROM account_analytic_line as line " \
"WHERE account_id = %s " \
"AND id IN %s AND to_invoice IS NOT NULL " \
"GROUP BY product_id,to_invoice,product_uom_id", (account.id, tuple(context['active_ids']),))
"GROUP BY product_id,to_invoice,product_uom_id", (account.id, tuple(ids),))
for product_id, factor_id, qty, uom in cr.fetchall():
product = product_obj.browse(cr, uid, product_id, context2)
@ -135,7 +108,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
raise osv.except_osv(_('Error'), _('At least one line has no product !'))
factor_name = ''
factor = invoice_factor_obj.browse(cr, uid, factor_id, context2)
if not data['product']:
if not data.get('product', False):
if factor.customer_name:
factor_name = product.name+' - '+factor.customer_name
else:
@ -148,7 +121,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
ctx.update({'uom':uom})
if account.pricelist_id:
pl = account.pricelist_id.id
price = pro_price_obj.price_get(cr,uid,[pl], data['product'] or product_id, qty or 1.0, account.partner_id.id, context=ctx)[pl]
price = pro_price_obj.price_get(cr,uid,[pl], data.get('product', False) or product_id, qty or 1.0, account.partner_id.id, context=ctx)[pl]
else:
price = 0.0
@ -164,7 +137,7 @@ class hr_timesheet_invoice_create(osv.osv_memory):
'invoice_line_tax_id': [(6,0,tax )],
'invoice_id': last_invoice,
'name': factor_name,
'product_id': data['product'] or product_id,
'product_id': data.get('product',product_id),
'invoice_line_tax_id': [(6,0,tax)],
'uos_id': uom,
'account_id': account_id,
@ -174,37 +147,76 @@ class hr_timesheet_invoice_create(osv.osv_memory):
#
# Compute for lines
#
cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id=%s and to_invoice=%s ORDER BY account_analytic_line.date", (account.id, tuple(context['active_ids']), product_id, factor_id))
cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id=%s and to_invoice=%s ORDER BY account_analytic_line.date", (account.id, tuple(ids), product_id, factor_id))
line_ids = cr.dictfetchall()
note = []
for line in line_ids:
# set invoice_line_note
details = []
if data['date']:
if data.get('date', False):
details.append(line['date'])
if data['time']:
if data.get('time', False):
if line['product_uom_id']:
details.append("%s %s" % (line['unit_amount'], product_uom_obj.browse(cr, uid, [line['product_uom_id']],context2)[0].name))
else:
details.append("%s" % (line['unit_amount'], ))
if data['name']:
if data.get('name', False):
details.append(line['name'])
note.append(u' - '.join(map(lambda x: unicode(x) or '',details)))
curr_line['note'] = "\n".join(map(lambda x: unicode(x) or '',note))
invoice_line_obj.create(cr, uid, curr_line, context=context)
cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(context['active_ids'])))
cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids)))
invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context)
return invoices
#
# TODO: check unit of measure !!!
#
class hr_timesheet_invoice_create(osv.osv_memory):
_name = 'hr.timesheet.invoice.create'
_description = 'Create invoice from timesheet'
_columns = {
'date': fields.boolean('Date', help='The real date of each work will be displayed on the invoice'),
'time': fields.boolean('Time spent', help='The time of each work done will be displayed on the invoice'),
'name': fields.boolean('Description', help='The detail of each work done will be displayed on the invoice'),
'price': fields.boolean('Cost', help='The cost of each work done will be displayed on the invoice. You probably don\'t want to check this'),
'product': fields.many2one('product.product', 'Product', help='Complete this field only if you want to force to use a specific product. Keep empty to use the real product that comes from the cost.'),
}
_defaults = {
'date': lambda *args: 1,
'name': lambda *args: 1
}
def view_init(self, cr, uid, fields, context=None):
"""
This function checks for precondition before wizard executes
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param fields: List of fields for default value
@param context: A standard dictionary for contextual values
"""
analytic_obj = self.pool.get('account.analytic.line')
data = context and context.get('active_ids', [])
for analytic in analytic_obj.browse(cr, uid, data, context=context):
if analytic.invoice_id:
raise osv.except_osv(_('Warning !'), _("Invoice is already linked to some of the analytic line(s)!"))
def do_create(self, cr, uid, ids, context=None):
data = self.read(cr, uid, ids, [], context=context)[0]
invs = self.pool.get('account.analytic.line').invoice_cost_create(cr, uid, context['active_ids'], data, context=context)
mod_obj = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
mod_ids = mod_obj.search(cr, uid, [('name', '=', 'action_invoice_tree1')], context=context)[0]
res_id = mod_obj.read(cr, uid, mod_ids, ['res_id'], context=context)['res_id']
act_win = act_obj.read(cr, uid, res_id, [], context=context)
act_win['domain'] = [('id','in',invoices),('type','=','out_invoice')]
act_win['domain'] = [('id','in',invs),('type','=','out_invoice')]
act_win['name'] = _('Invoices')
return act_win

View File

@ -1,48 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_hr_timesheet_invoice_create_final" model="ir.ui.view">
<field name="name">hr.timesheet.invoice.create.final.form</field>
<field name="model">hr.timesheet.invoice.create.final</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Final invoice for analytic account">
<group col="4" colspan="6">
<separator string="Do you want details for each line of the invoices ?" colspan="4"/>
<field name="date"/>
<field name="time"/>
<field name="name"/>
<field name="price"/>
<separator string="Invoice Balance amount" colspan="4"/>
<field name="balance_product" required="1"/>
</group>
<separator colspan="4"/>
<group col="2" colspan="4">
<button special="cancel" string="Cancel" icon='gtk-cancel'/>
<button name="do_create" string="Create Invoices" colspan="1" type="object" icon="terp-gtk-go-back-rtl"/>
</group>
</form>
</field>
</record>
<record id="action_hr_timesheet_invoice_create_final" model="ir.actions.act_window">
<field name="name">Final Invoice</field>
<field name="name">Create Invoice</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hr.timesheet.invoice.create.final</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="multi" eval="1"/>
</record>
<record model="ir.values" id="hr_timesheet_invoice_create_final_values">
<record model="ir.values" id="hr_timesheet_invoice_create_final_values">
<field name="model_id" ref="model_account_analytic_account" />
<field name="name">Final Invoice</field>
<field name="object" eval="1" />
<field name="name">Invoice</field>
<field name="key2">client_action_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_hr_timesheet_invoice_create_final'))" />
<field name="key">action</field>
<field name="model">account.analytic.account</field>
</record>
</data>
<record id="view_hr_timesheet_invoice_create_final" model="ir.ui.view">
<field name="name">hr.timesheet.invoice.create.final.form</field>
<field name="model">hr.timesheet.invoice.create.final</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Invoice contract">
<group col="4" colspan="6">
<separator string="Do you want to display work details on the invoice ?" colspan="4"/>
<field name="date"/>
<field name="time"/>
<field name="name"/>
<field name="price"/>
<separator string="Force to use a special product" colspan="4" groups="base.group_extended"/>
<field name="product" groups="base.group_extended"/>
</group>
<separator colspan="4"/>
<group col="2" colspan="4">
<button special="cancel" string="Cancel" icon='gtk-cancel'/>
<button name="do_create" string="Create Invoice" colspan="1" type="object" icon="terp-gtk-go-back-rtl"/>
</group>
</form>
</field>
</record>
</data>
</openerp>

View File

@ -45,7 +45,7 @@
<newline/>
<separator colspan="4"/>
<group col="9" colspan="8">
<field name="state" widget="statusbar" statusbar_visible="open,close" statusbar_colors='{"pending":"blue"}' select="1" readonly="1"/>
<field name="state" widget="statusbar" statusbar_visible="open,close" statusbar_colors='{"pending":"blue"}' select="1" readonly="1"/>
<button name="set_cancel" string="Cancel" type="object" states="open,pending" icon="gtk-cancel"/>
<button name="set_template" string="Set as Template" type="object" states="open" icon="gtk-convert" groups="base.group_extended"/>
<button name="set_open" string="Reactivate Project" type="object" states="pending,cancelled,close" icon="gtk-ok"/>

View File

@ -34,7 +34,7 @@ the Timesheet line entries for particular date and particular user with the eff
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'images': ['images/invoice_task_work.jpeg', 'images/my_timesheet.jpeg', 'images/working_hour.jpeg'],
'depends': ['project', 'hr_timesheet_sheet', 'hr_timesheet_invoice'],
'depends': ['project', 'hr_timesheet_sheet', 'hr_timesheet_invoice', 'account_analytic_analysis'],
'init_xml': ['project_timesheet_data.xml'],
'update_xml': ["security/ir.model.access.csv","process/project_timesheet_process.xml", "report/task_report_view.xml", "project_timesheet_view.xml"],
'demo_xml': ["project_timesheet_demo.xml"],

View File

@ -105,8 +105,9 @@ the project form.</field>
<!--
Time Tracking menu in project Management
-->
<menuitem icon="terp-project" id="base.menu_main_pm" name="Project" sequence="10"/>
<menuitem id="menu_project_working_hours" parent="base.menu_project_management_time_tracking" action="hr_timesheet.act_hr_timesheet_line_evry1_all_form"/>
<menuitem id="menu_invoicing" name="Billing" parent="base.menu_main_pm" sequence="4"/>
</data>
</openerp>

View File

@ -489,8 +489,8 @@
src_model="product.product"
groups="base.group_sale_salesman"/>
<menuitem id="menu_invoiced" name="Invoicing" parent="base.menu_base_partner" sequence="5" groups="-base.group_extended"/>
<menuitem id="menu_invoicing_sales_order_lines" parent="menu_invoiced" action="action_order_line_tree2" sequence="2" groups="base.group_no_one"/>
<menuitem id="base.menu_invoiced" name="Invoicing" parent="base.menu_base_partner" sequence="5" groups="-base.group_extended"/>
<menuitem id="menu_invoicing_sales_order_lines" parent="base.menu_invoiced" action="action_order_line_tree2" sequence="2" groups="base.group_no_one"/>
<!-- configartion view -->