class hr_timesheet_report(osv.osv): _name = "hr.timesheet.report" _description = "Timesheet" _auto = False _columns = { 'year': fields.char('Year',size=64,required=False, readonly=True), 'day': fields.char('Day', size=128, readonly=True), 'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')], 'Month',readonly=True), 'date': fields.date('Date', readonly=True), 'name': fields.char('Description', size=64,readonly=True), 'product_id' : fields.many2one('product.product', 'Product',readonly=True), 'journal_id' : fields.many2one('account.analytic.journal', 'Journal',readonly=True), 'general_account_id' : fields.many2one('account.account', 'General Account', readonly=True), 'user_id': fields.many2one('res.users', 'User',readonly=True), 'account_id': fields.many2one('account.analytic.account', 'Analytic Account',readonly=True), 'company_id': fields.many2one('res.company', 'Company',readonly=True), 'cost': fields.float('Cost',readonly=True, digits_compute=dp.get_precision('Account')), 'quantity': fields.float('Quantity',readonly=True), } def init(self, cr): tools.drop_view_if_exists(cr, 'hr_timesheet_report') cr.execute(""" create or replace view hr_timesheet_report as ( select min(t.id) as id, l.date as date, to_char(l.date, 'YYYY-MM-DD') as day, to_char(l.date,'YYYY') as year, to_char(l.date,'MM') as month, sum(l.amount) as cost, sum(l.unit_amount) as quantity, l.account_id as account_id, l.journal_id as journal_id, l.product_id as product_id, l.general_account_id as general_account_id, l.user_id as user_id, l.company_id as company_id, l.currency_id as currency_id from hr_analytic_timesheet as t left join account_analytic_line as l ON (t.line_id=l.id) group by l.date, l.account_id, l.product_id, l.general_account_id, l.journal_id, l.user_id, l.company_id, l.currency_id ) """)
class marketing_campaign(osv.osv): _name = "marketing.campaign" _description = "Marketing Campaign" _columns = { 'name': fields.char('Name', size=64, required=True), 'object_id': fields.many2one('ir.model', 'Resource', required=True, help="Choose the resource on which you want \ this campaign to be run" ), 'partner_field_id': fields.many2one('ir.model.fields', 'Partner Field', domain="[('model_id', '=', object_id), ('ttype', '=', 'many2one'), ('relation', '=', 'res.partner')]", help="The generated workitems will be linked to the partner related to the record. "\ "If the record is the partner itself leave this field empty. "\ "This is useful for reporting purposes, via the Campaign Analysis or Campaign Follow-up views."), 'unique_field_id': fields.many2one('ir.model.fields', 'Unique Field', domain="[('model_id', '=', object_id), ('ttype', 'in', ['char','int','many2one','text','selection'])]", help='If set, this field will help segments that work in "no duplicates" mode to avoid '\ 'selecting similar records twice. Similar records are records that have the same value for '\ 'this unique field. For example by choosing the "email_from" field for CRM Leads you would prevent '\ 'sending the same campaign to the same email address again. If not set, the "no duplicates" segments '\ "will only avoid selecting the same record again if it entered the campaign previously. "\ "Only easily comparable fields like textfields, integers, selections or single relationships may be used."), 'mode': fields.selection([('test', 'Test Directly'), ('test_realtime', 'Test in Realtime'), ('manual', 'With Manual Confirmation'), ('active', 'Normal')], 'Mode', required=True, help= \ """Test - It creates and process all the activities directly (without waiting for the delay on transitions) but does not send emails or produce reports. Test in Realtime - It creates and processes all the activities directly but does not send emails or produce reports. With Manual Confirmation - the campaigns runs normally, but the user has to validate all workitem manually. Normal - the campaign runs normally and automatically sends all emails and reports (be very careful with this mode, you're live!)"""), 'state': fields.selection([('draft', 'New'), ('running', 'Running'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'Status',), 'activity_ids': fields.one2many('marketing.campaign.activity', 'campaign_id', 'Activities'), 'fixed_cost': fields.float('Fixed Cost', help="Fixed cost for running this campaign. You may also specify variable cost and revenue on each campaign activity. Cost and Revenue statistics are included in Campaign Reporting.", digits_compute=dp.get_precision('Purchase Price')), } _defaults = { 'state': lambda *a: 'draft', 'mode': lambda *a: 'test', } def state_running_set(self, cr, uid, ids, *args): # TODO check that all subcampaigns are running campaign = self.browse(cr, uid, ids[0]) if not campaign.activity_ids: raise osv.except_osv( _("Error"), _("The campaign cannot be started: there are no activities in it." )) has_start = False has_signal_without_from = False for activity in campaign.activity_ids: if activity.start: has_start = True if activity.signal and len(activity.from_ids) == 0: has_signal_without_from = True if not has_start and not has_signal_without_from: raise osv.except_osv( _("Error"), _("The campaign cannot be started: it doesn't have any starting activity. Modify campaign's activities to mark one as the starting point." )) return self.write(cr, uid, ids, {'state': 'running'}) def state_done_set(self, cr, uid, ids, *args): # TODO check that this campaign is not a subcampaign in running mode. segment_ids = self.pool.get('marketing.campaign.segment').search( cr, uid, [('campaign_id', 'in', ids), ('state', '=', 'running')]) if segment_ids: raise osv.except_osv( _("Error"), _("The campaign cannot be marked as done before all segments are closed." )) self.write(cr, uid, ids, {'state': 'done'}) return True def state_cancel_set(self, cr, uid, ids, *args): # TODO check that this campaign is not a subcampaign in running mode. self.write(cr, uid, ids, {'state': 'cancelled'}) return True # dead code def signal(self, cr, uid, model, res_id, signal, run_existing=True, context=None): record = self.pool.get(model).browse(cr, uid, res_id, context) return self._signal(cr, uid, record, signal, run_existing, context) #dead code def _signal(self, cr, uid, record, signal, run_existing=True, context=None): if not signal: raise ValueError('signal cannot be False') Workitems = self.pool.get('marketing.campaign.workitem') domain = [('object_id.model', '=', record._table._name), ('state', '=', 'running')] campaign_ids = self.search(cr, uid, domain, context=context) for campaign in self.browse(cr, uid, campaign_ids, context=context): for activity in campaign.activity_ids: if activity.signal != signal: continue data = dict(activity_id=activity.id, res_id=record.id, state='todo') wi_domain = [(k, '=', v) for k, v in data.items()] wi_ids = Workitems.search(cr, uid, wi_domain, context=context) if wi_ids: if not run_existing: continue else: partner = self._get_partner_for(campaign, record) if partner: data['partner_id'] = partner.id wi_id = Workitems.create(cr, uid, data, context=context) wi_ids = [wi_id] Workitems.process(cr, uid, wi_ids, context=context) return True def _get_partner_for(self, campaign, record): partner_field = campaign.partner_field_id.name if partner_field: return getattr(record, partner_field) elif campaign.object_id.model == 'res.partner': return record return None # prevent duplication until the server properly duplicates several levels of nested o2m def copy(self, cr, uid, id, default=None, context=None): raise osv.except_osv( _("Operation not supported"), _("You can not duplicate a campaign, it's not supported yet.")) def _find_duplicate_workitems(self, cr, uid, record, campaign_rec, context=None): """Finds possible duplicates workitems for a record in this campaign, based on a uniqueness field. :param record: browse_record to find duplicates workitems for. :param campaign_rec: browse_record of campaign """ Workitems = self.pool.get('marketing.campaign.workitem') duplicate_workitem_domain = [('res_id', '=', record.id), ('campaign_id', '=', campaign_rec.id)] unique_field = campaign_rec.unique_field_id if unique_field: unique_value = getattr(record, unique_field.name, None) if unique_value: if unique_field.ttype == 'many2one': unique_value = unique_value.id similar_res_ids = self.pool.get( campaign_rec.object_id.model).search( cr, uid, [(unique_field.name, '=', unique_value)], context=context) if similar_res_ids: duplicate_workitem_domain = [ ('res_id', 'in', similar_res_ids), ('campaign_id', '=', campaign_rec.id) ] return Workitems.search(cr, uid, duplicate_workitem_domain, context=context)
class marketing_campaign_activity(osv.osv): _name = "marketing.campaign.activity" _order = "name" _description = "Campaign Activity" _action_types = [ ('email', 'Email'), ('report', 'Report'), ('action', 'Custom Action'), # TODO implement the subcampaigns. # TODO implement the subcampaign out. disallow out transitions from # subcampaign activities ? #('subcampaign', 'Sub-Campaign'), ] _columns = { 'name': fields.char('Name', size=128, required=True), 'campaign_id': fields.many2one('marketing.campaign', 'Campaign', required=True, ondelete='cascade', select=1), 'object_id': fields.related('campaign_id', 'object_id', type='many2one', relation='ir.model', string='Object', readonly=True), 'start': fields.boolean( 'Start', help="This activity is launched when the campaign starts.", select=True), 'condition': fields.text( 'Condition', size=256, required=True, help= "Python expression to decide whether the activity can be executed, otherwise it will be deleted or cancelled." "The expression may use the following [browsable] variables:\n" " - activity: the campaign activity\n" " - workitem: the campaign workitem\n" " - resource: the resource object this campaign item represents\n" " - transitions: list of campaign transitions outgoing from this activity\n" "...- re: Python regular expression module"), 'type': fields.selection( _action_types, 'Type', required=True, help= """The type of action to execute when an item enters this activity, such as: - Email: send an email using a predefined email template - Report: print an existing Report defined on the resource item and save it into a specific directory - Custom Action: execute a predefined action, e.g. to modify the fields of the resource record """), 'email_template_id': fields.many2one( 'email.template', "Email Template", help='The email to send when this activity is activated'), 'report_id': fields.many2one( 'ir.actions.report.xml', "Report", help='The report to generate when this activity is activated', ), 'report_directory_id': fields.many2one( 'document.directory', 'Directory', help="This folder is used to store the generated reports"), 'server_action_id': fields.many2one( 'ir.actions.server', string='Action', help="The action to perform when this activity is activated"), 'to_ids': fields.one2many('marketing.campaign.transition', 'activity_from_id', 'Next Activities'), 'from_ids': fields.one2many('marketing.campaign.transition', 'activity_to_id', 'Previous Activities'), 'variable_cost': fields.float( 'Variable Cost', help= "Set a variable cost if you consider that every campaign item that has reached this point has entailed a certain cost. You can get cost statistics in the Reporting section", digits_compute=dp.get_precision('Purchase Price')), 'revenue': fields.float( 'Revenue', help= "Set an expected revenue if you consider that every campaign item that has reached this point has generated a certain revenue. You can get revenue statistics in the Reporting section", digits_compute=dp.get_precision('Sale Price')), 'signal': fields.char( 'Signal', size=128, help= 'An activity with a signal can be called programmatically. Be careful, the workitem is always created when a signal is sent' ), 'keep_if_condition_not_met': fields.boolean( "Don't Delete Workitems", help= "By activating this option, workitems that aren't executed because the condition is not met are marked as cancelled instead of being deleted." ) } _defaults = { 'type': lambda *a: 'email', 'condition': lambda *a: 'True', } def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): if context == None: context = {} if 'segment_id' in context and context['segment_id']: segment_obj = self.pool.get('marketing.campaign.segment').browse( cr, uid, context['segment_id']) act_ids = [] for activity in segment_obj.campaign_id.activity_ids: act_ids.append(activity.id) return act_ids return super(marketing_campaign_activity, self).search(cr, uid, args, offset, limit, order, context, count) #dead code def _process_wi_report(self, cr, uid, activity, workitem, context=None): service = netsvc.LocalService('report.%s' % activity.report_id.report_name) (report_data, format) = service.create(cr, uid, [], {}, {}) attach_vals = { 'name': '%s_%s_%s' % (activity.report_id.report_name, activity.name, workitem.partner_id.name), 'datas_fname': '%s.%s' % (activity.report_id.report_name, activity.report_id.report_type), 'parent_id': activity.report_directory_id.id, 'datas': base64.encodestring(report_data), 'file_type': format } self.pool.get('ir.attachment').create(cr, uid, attach_vals) return True def _process_wi_email(self, cr, uid, activity, workitem, context=None): return self.pool.get('email.template').send_mail( cr, uid, activity.email_template_id.id, workitem.res_id, context=context) #dead code def _process_wi_action(self, cr, uid, activity, workitem, context=None): if context is None: context = {} server_obj = self.pool.get('ir.actions.server') action_context = dict(context, active_id=workitem.res_id, active_ids=[workitem.res_id], active_model=workitem.object_id.model, workitem=workitem) res = server_obj.run(cr, uid, [activity.server_action_id.id], context=action_context) # server action return False if the action is performed # except client_action, other and python code return res == False and True or res def process(self, cr, uid, act_id, wi_id, context=None): activity = self.browse(cr, uid, act_id, context=context) method = '_process_wi_%s' % (activity.type, ) action = getattr(self, method, None) if not action: raise NotImplementedError( 'method %r in not implemented on %r object' % (method, self)) workitem_obj = self.pool.get('marketing.campaign.workitem') workitem = workitem_obj.browse(cr, uid, wi_id, context=context) return action(cr, uid, activity, workitem, context=context)
class report_stock_move(osv.osv): _name = "report.stock.move" _description = "Moves Statistics" _auto = False _columns = { 'date': fields.date('Date', readonly=True), 'year': fields.char('Year', size=4, readonly=True), 'day': fields.char('Day', size=128, readonly=True), 'month': fields.selection([('01', 'January'), ('02', 'February'), ('03', 'March'), ('04', 'April'), ('05', 'May'), ('06', 'June'), ('07', 'July'), ('08', 'August'), ('09', 'September'), ('10', 'October'), ('11', 'November'), ('12', 'December')], 'Month', readonly=True), 'partner_id': fields.many2one('res.partner.address', 'Partner', readonly=True), 'product_id': fields.many2one('product.product', 'Product', readonly=True), 'company_id': fields.many2one('res.company', 'Company', readonly=True), 'picking_id': fields.many2one('stock.picking', 'Packing', readonly=True), 'type': fields.selection( [('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('other', 'Others')], 'Shipping Type', required=True, select=True, help="Shipping type specify, goods coming in or going out."), 'location_id': fields.many2one( 'stock.location', 'Source Location', readonly=True, select=True, help= "Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations." ), 'location_dest_id': fields.many2one( 'stock.location', 'Dest. Location', readonly=True, select=True, help="Location where the system will stock the finished products." ), 'state': fields.selection([('draft', 'Draft'), ('waiting', 'Waiting'), ('confirmed', 'Confirmed'), ('assigned', 'Available'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', readonly=True, select=True), 'product_qty': fields.integer('Quantity', readonly=True), 'categ_id': fields.many2one( 'product.category', 'Product Category', ), 'product_qty_in': fields.integer('In Qty', readonly=True), 'product_qty_out': fields.integer('Out Qty', readonly=True), 'value': fields.float('Total Value', required=True), 'day_diff2': fields.float('Lag (Days)', readonly=True, digits_compute=dp.get_precision('Shipping Delay'), group_operator="avg"), 'day_diff1': fields.float('Planned Lead Time (Days)', readonly=True, digits_compute=dp.get_precision('Shipping Delay'), group_operator="avg"), 'day_diff': fields.float('Execution Lead Time (Days)', readonly=True, digits_compute=dp.get_precision('Shipping Delay'), group_operator="avg"), 'stock_journal': fields.many2one('stock.journal', 'Stock Journal', select=True), } def init(self, cr): tools.drop_view_if_exists(cr, 'report_stock_move') cr.execute(""" CREATE OR REPLACE view report_stock_move AS ( SELECT min(sm_id) as id, date_trunc('day',al.dp) as date, al.curr_year as year, al.curr_month as month, al.curr_day as day, al.curr_day_diff as day_diff, al.curr_day_diff1 as day_diff1, al.curr_day_diff2 as day_diff2, al.location_id as location_id, al.picking_id as picking_id, al.company_id as company_id, al.location_dest_id as location_dest_id, al.product_qty, al.out_qty as product_qty_out, al.in_qty as product_qty_in, al.address_id as partner_id, al.product_id as product_id, al.state as state , al.product_uom as product_uom, al.categ_id as categ_id, coalesce(al.type, 'other') as type, al.stock_journal as stock_journal, sum(al.in_value - al.out_value) as value FROM (SELECT CASE WHEN sp.type in ('out') THEN sum(sm.product_qty * pu.factor) ELSE 0.0 END AS out_qty, CASE WHEN sp.type in ('in') THEN sum(sm.product_qty * pu.factor) ELSE 0.0 END AS in_qty, CASE WHEN sp.type in ('out') THEN sum(sm.product_qty * pu.factor) * pt.standard_price ELSE 0.0 END AS out_value, CASE WHEN sp.type in ('in') THEN sum(sm.product_qty * pu.factor) * pt.standard_price ELSE 0.0 END AS in_value, min(sm.id) as sm_id, sm.date as dp, to_char(date_trunc('day',sm.date), 'YYYY') as curr_year, to_char(date_trunc('day',sm.date), 'MM') as curr_month, to_char(date_trunc('day',sm.date), 'YYYY-MM-DD') as curr_day, avg(date(sm.date)-date(sm.create_date)) as curr_day_diff, avg(date(sm.date_expected)-date(sm.create_date)) as curr_day_diff1, avg(date(sm.date)-date(sm.date_expected)) as curr_day_diff2, sm.location_id as location_id, sm.location_dest_id as location_dest_id, sum(sm.product_qty) as product_qty, pt.categ_id as categ_id , sm.address_id as address_id, sm.product_id as product_id, sm.picking_id as picking_id, sm.company_id as company_id, sm.state as state, sm.product_uom as product_uom, sp.type as type, sp.stock_journal_id AS stock_journal FROM stock_move sm LEFT JOIN stock_picking sp ON (sm.picking_id=sp.id) LEFT JOIN product_product pp ON (sm.product_id=pp.id) LEFT JOIN product_uom pu ON (sm.product_uom=pu.id) LEFT JOIN product_template pt ON (pp.product_tmpl_id=pt.id) LEFT JOIN stock_location sl ON (sm.location_id = sl.id) GROUP BY sm.id,sp.type, sm.date,sm.address_id, sm.product_id,sm.state,sm.product_uom,sm.date_expected, sm.product_id,pt.standard_price, sm.picking_id, sm.product_qty, sm.company_id,sm.product_qty, sm.location_id,sm.location_dest_id,pu.factor,pt.categ_id, sp.stock_journal_id) AS al GROUP BY al.out_qty,al.in_qty,al.curr_year,al.curr_month, al.curr_day,al.curr_day_diff,al.curr_day_diff1,al.curr_day_diff2,al.dp,al.location_id,al.location_dest_id, al.address_id,al.product_id,al.state,al.product_uom, al.picking_id,al.company_id,al.type,al.product_qty, al.categ_id, al.stock_journal ) """)
class report_stock_inventory(osv.osv): _name = "report.stock.inventory" _description = "Stock Statistics" _auto = False _columns = { 'date': fields.datetime('Date', readonly=True), 'partner_id': fields.many2one('res.partner.address', 'Partner', readonly=True), 'product_id': fields.many2one('product.product', 'Product', readonly=True), 'product_categ_id': fields.many2one('product.category', 'Product Category', readonly=True), 'location_id': fields.many2one('stock.location', 'Location', readonly=True), 'prodlot_id': fields.many2one('stock.production.lot', 'Lot', readonly=True), 'company_id': fields.many2one('res.company', 'Company', readonly=True), 'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product UoM'), readonly=True), 'value': fields.float('Total Value', digits_compute=dp.get_precision('Account'), required=True), 'state': fields.selection( [('draft', 'Draft'), ('waiting', 'Waiting'), ('confirmed', 'Confirmed'), ('assigned', 'Available'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', readonly=True, select=True, help= 'When the stock move is created it is in the \'Draft\' state.\n After that it is set to \'Confirmed\' state.\n If stock is available state is set to \'Avaiable\'.\n When the picking it done the state is \'Done\'.\ \nThe state is \'Waiting\' if the move is waiting for another one.' ), 'location_type': fields.selection( [('supplier', 'Supplier Location'), ('view', 'View'), ('internal', 'Internal Location'), ('customer', 'Customer Location'), ('inventory', 'Inventory'), ('procurement', 'Procurement'), ('production', 'Production'), ('transit', 'Transit Location for Inter-Companies Transfers')], 'Location Type', required=True), } def init(self, cr): tools.drop_view_if_exists(cr, 'report_stock_inventory') cr.execute(""" CREATE OR REPLACE view report_stock_inventory AS ( (SELECT min(m.id) as id, m.date as date, m.address_id as partner_id, m.location_id as location_id, m.product_id as product_id, pt.categ_id as product_categ_id, l.usage as location_type, m.company_id, m.state as state, m.prodlot_id as prodlot_id, coalesce(sum(-pt.standard_price * m.product_qty * pu.factor / u.factor)::decimal, 0.0) as value, CASE when pt.uom_id = m.product_uom THEN coalesce(sum(-m.product_qty)::decimal, 0.0) ELSE coalesce(sum(-m.product_qty * pu.factor / u.factor )::decimal, 0.0) END as product_qty FROM stock_move m LEFT JOIN stock_picking p ON (m.picking_id=p.id) LEFT JOIN product_product pp ON (m.product_id=pp.id) LEFT JOIN product_template pt ON (pp.product_tmpl_id=pt.id) LEFT JOIN product_uom pu ON (pt.uom_id=pu.id) LEFT JOIN product_uom u ON (m.product_uom=u.id) LEFT JOIN stock_location l ON (m.location_id=l.id) GROUP BY m.id, m.product_id, m.product_uom, pt.categ_id, m.address_id, m.location_id, m.location_dest_id, m.prodlot_id, m.date, m.state, l.usage, m.company_id,pt.uom_id ) UNION ALL ( SELECT -m.id as id, m.date as date, m.address_id as partner_id, m.location_dest_id as location_id, m.product_id as product_id, pt.categ_id as product_categ_id, l.usage as location_type, m.company_id, m.state as state, m.prodlot_id as prodlot_id, coalesce(sum(pt.standard_price * m.product_qty * pu.factor / u.factor )::decimal, 0.0) as value, CASE when pt.uom_id = m.product_uom THEN coalesce(sum(m.product_qty)::decimal, 0.0) ELSE coalesce(sum(m.product_qty * pu.factor / u.factor)::decimal, 0.0) END as product_qty FROM stock_move m LEFT JOIN stock_picking p ON (m.picking_id=p.id) LEFT JOIN product_product pp ON (m.product_id=pp.id) LEFT JOIN product_template pt ON (pp.product_tmpl_id=pt.id) LEFT JOIN product_uom pu ON (pt.uom_id=pu.id) LEFT JOIN product_uom u ON (m.product_uom=u.id) LEFT JOIN stock_location l ON (m.location_dest_id=l.id) GROUP BY m.id, m.product_id, m.product_uom, pt.categ_id, m.address_id, m.location_id, m.location_dest_id, m.prodlot_id, m.date, m.state, l.usage, m.company_id,pt.uom_id ) ); """)
class report_account_analytic_line_to_invoice(osv.osv): _name = "report.account.analytic.line.to.invoice" _description = "Analytic lines to invoice report" _auto = False _columns = { 'name': fields.char('Year', size=64, required=False, readonly=True), 'product_id': fields.many2one('product.product', 'Product', readonly=True), 'account_id': fields.many2one('account.analytic.account', 'Analytic account', readonly=True), 'product_uom_id': fields.many2one('product.uom', 'UoM', readonly=True), 'unit_amount': fields.float('Units', readonly=True), 'sale_price': fields.float('Sale price', readonly=True, digits_compute=dp.get_precision('Sale Price')), 'amount': fields.float('Amount', readonly=True, digits_compute=dp.get_precision('Account')), 'month': fields.selection([('01', 'January'), ('02', 'February'), ('03', 'March'), ('04', 'April'), ('05', 'May'), ('06', 'June'), ('07', 'July'), ('08', 'August'), ('09', 'September'), ('10', 'October'), ('11', 'November'), ('12', 'December')], 'Month', readonly=True), } _order = 'name desc, product_id asc, account_id asc' def init(self, cr): tools.drop_view_if_exists(cr, 'report_account_analytic_line_to_invoice') cr.execute(""" CREATE OR REPLACE VIEW report_account_analytic_line_to_invoice AS ( SELECT DISTINCT(to_char(l.date,'MM')) as month, to_char(l.date, 'YYYY') as name, MIN(l.id) AS id, l.product_id, l.account_id, SUM(l.amount) AS amount, SUM(l.unit_amount*t.list_price) AS sale_price, SUM(l.unit_amount) AS unit_amount, l.product_uom_id FROM account_analytic_line l left join product_product p on (l.product_id=p.id) left join product_template t on (p.product_tmpl_id=t.id) WHERE (invoice_id IS NULL) and (to_invoice IS NOT NULL) GROUP BY to_char(l.date, 'YYYY'), to_char(l.date,'MM'), product_id, product_uom_id, account_id ) """)
class partner_event_registration(osv.osv_memory): """ event Registration """ _name = "partner.event.registration" _description = __doc__ _order = 'event_id' _columns = { 'event_id': fields.many2one('event.event', 'Event'), 'event_type': fields.many2one('event.type', 'Type', readonly=True), 'unit_price': fields.float('Registration Cost', digits_compute=dp.get_precision('Sale Price')), 'start_date': fields.datetime('Start date', help="Beginning Date of Event", readonly=True), 'end_date': fields.datetime('Closing date', help="Closing Date of Event", readonly=True), 'nb_register': fields.integer('Number of Registration'), } _defaults = { 'nb_register': 1, } def open_registration(self, cr, uid, ids, context=None): """This Function Open Registration For Given Event id and Partner. """ value = {} res_obj = self.pool.get('res.partner') addr_obj = self.pool.get('res.partner.address') reg_obj = self.pool.get('event.registration') mod_obj = self.pool.get('ir.model.data') record_ids = context and context.get('active_ids', []) or [] addr = res_obj.address_get(cr, uid, record_ids) email = False contact_id = addr.get('default', False) if contact_id: email = addr_obj.browse(cr, uid, contact_id, context=context).email result = mod_obj.get_object_reference(cr, uid, 'event', 'view_registration_search') res = result and result[1] or False # Select the view id2 = mod_obj.get_object_reference(cr, uid, 'event', 'view_event_registration_form') id2 = id2 and id2[1] or False id3 = mod_obj.get_object_reference(cr, uid, 'event', 'view_event_registration_tree') id3 = id3 and id3[1] or False for current in self.browse(cr, uid, ids, context=context): for partner in res_obj.browse(cr, uid, record_ids, context=context): new_case = reg_obj.create( cr, uid, { 'name': 'Registration', 'event_id': current.event_id and current.event_id.id or False, 'unit_price': current.unit_price, 'partner_id': partner.id, 'partner_invoice_id': partner.id, 'event_product': current.event_id.product_id.name, 'contact_id': contact_id, 'email_from': email, 'nb_register': current.nb_register, }, context=context) value = { 'name': _('Event Registration'), 'view_type': 'form', 'view_mode': 'tree,form', 'res_model': 'event.registration', 'res_id': new_case, 'views': [(id2, 'form'), (id3, 'tree'), (False, 'calendar'), (False, 'graph')], 'type': 'ir.actions.act_window', 'search_view_id': res } return value def name_get(self, cr, uid, ids, context=None): """Overrides orm name_get method @param ids: List of partner_event_register ids """ res = [] if not ids: return res reads = self.read(cr, uid, ids, ['event_type', 'event_id'], context=context) for record in reads: event_id = record['event_id'][1] if record['event_type']: event_id = record['event_type'][1] + ' on ' + event_id res.append((record['id'], event_id)) return res def onchange_event_id(self, cr, uid, ids, event_id, context=None): res = {} event_obj = self.pool.get('event.event') product_obj = self.pool.get('product.product') partner_obj = self.pool.get('res.partner') if context is None: context = {} partner_id = context.get('active_id', False) if event_id: event = event_obj.browse(cr, uid, event_id, context=context) pricelist_id = event.pricelist_id and event.pricelist_id.id or False if partner_id: partner = partner_obj.browse(cr, uid, partner_id, context=context) pricelist_id = pricelist_id or partner.property_product_pricelist.id unit_price = product_obj._product_price( cr, uid, [event.product_id.id], False, False, {'pricelist': pricelist_id})[event.product_id.id] res['value'] = { 'event_type': event.type and event.type.id or False, 'start_date': event.date_begin, 'end_date': event.date_end, 'unit_price': unit_price, } return res
class account_analytic_account(osv.osv): _name = "account.analytic.account" _inherit = "account.analytic.account" def _analysis_all(self, cr, uid, ids, fields, arg, context=None): dp = 2 res = dict([(i, {}) for i in ids]) parent_ids = tuple( ids ) #We don't want consolidation for each of these fields because those complex computation is resource-greedy. accounts = self.browse(cr, uid, ids, context=context) for f in fields: if f == 'user_ids': cr.execute('SELECT MAX(id) FROM res_users') max_user = cr.fetchone()[0] if parent_ids: cr.execute('SELECT DISTINCT("user") FROM account_analytic_analysis_summary_user ' \ 'WHERE account_id IN %s AND unit_amount <> 0.0', (parent_ids,)) result = cr.fetchall() else: result = [] for id in ids: res[id][f] = [int((id * max_user) + x[0]) for x in result] elif f == 'month_ids': if parent_ids: cr.execute('SELECT DISTINCT(month_id) FROM account_analytic_analysis_summary_month ' \ 'WHERE account_id IN %s AND unit_amount <> 0.0', (parent_ids,)) result = cr.fetchall() else: result = [] for id in ids: res[id][f] = [ int(id * 1000000 + int(x[0])) for x in result ] elif f == 'last_worked_invoiced_date': for id in ids: res[id][f] = False if parent_ids: cr.execute( "SELECT account_analytic_line.account_id, MAX(date) \ FROM account_analytic_line \ WHERE account_id IN %s \ AND invoice_id IS NOT NULL \ GROUP BY account_analytic_line.account_id;", (parent_ids, )) for account_id, sum in cr.fetchall(): if account_id not in res: res[account_id] = {} res[account_id][f] = sum elif f == 'ca_to_invoice': for id in ids: res[id][f] = 0.0 res2 = {} if parent_ids: # Amount uninvoiced hours to invoice at sale price # Warning # This computation doesn't take care of pricelist ! # Just consider list_price cr.execute( """SELECT account_analytic_account.id, \ COALESCE(SUM (product_template.list_price * \ account_analytic_line.unit_amount * \ ((100-hr_timesheet_invoice_factor.factor)/100)), 0.0) \ AS ca_to_invoice \ FROM product_template \ JOIN product_product \ ON product_template.id = product_product.product_tmpl_id \ JOIN account_analytic_line \ ON account_analytic_line.product_id = product_product.id \ JOIN account_analytic_journal \ ON account_analytic_line.journal_id = account_analytic_journal.id \ JOIN account_analytic_account \ ON account_analytic_account.id = account_analytic_line.account_id \ JOIN hr_timesheet_invoice_factor \ ON hr_timesheet_invoice_factor.id = account_analytic_account.to_invoice \ WHERE account_analytic_account.id IN %s \ AND account_analytic_line.invoice_id IS NULL \ AND account_analytic_line.to_invoice IS NOT NULL \ AND account_analytic_journal.type IN ('purchase','general') \ GROUP BY account_analytic_account.id;""", (parent_ids, )) for account_id, sum in cr.fetchall(): if account_id not in res: res[account_id] = {} res[account_id][f] = round(sum, dp) # sum both result on account_id for id in ids: res[id][f] = round(res.get(id, {}).get(f, 0.0), dp) + round(res2.get(id, 0.0), 2) elif f == 'last_invoice_date': for id in ids: res[id][f] = False if parent_ids: cr.execute( "SELECT account_analytic_line.account_id, \ DATE(MAX(account_invoice.date_invoice)) \ FROM account_analytic_line \ JOIN account_invoice \ ON account_analytic_line.invoice_id = account_invoice.id \ WHERE account_analytic_line.account_id IN %s \ AND account_analytic_line.invoice_id IS NOT NULL \ GROUP BY account_analytic_line.account_id", (parent_ids, )) for account_id, lid in cr.fetchall(): res[account_id][f] = lid elif f == 'last_worked_date': for id in ids: res[id][f] = False if parent_ids: cr.execute( "SELECT account_analytic_line.account_id, MAX(date) \ FROM account_analytic_line \ WHERE account_id IN %s \ AND invoice_id IS NULL \ GROUP BY account_analytic_line.account_id", (parent_ids, )) for account_id, lwd in cr.fetchall(): if account_id not in res: res[account_id] = {} res[account_id][f] = lwd elif f == 'hours_qtt_non_invoiced': for id in ids: res[id][f] = 0.0 if parent_ids: cr.execute( "SELECT account_analytic_line.account_id, COALESCE(SUM(unit_amount), 0.0) \ FROM account_analytic_line \ JOIN account_analytic_journal \ ON account_analytic_line.journal_id = account_analytic_journal.id \ WHERE account_analytic_line.account_id IN %s \ AND account_analytic_journal.type='general' \ AND invoice_id IS NULL \ AND to_invoice IS NOT NULL \ GROUP BY account_analytic_line.account_id;", (parent_ids, )) for account_id, sua in cr.fetchall(): if account_id not in res: res[account_id] = {} res[account_id][f] = round(sua, dp) for id in ids: res[id][f] = round(res[id][f], dp) elif f == 'hours_quantity': for id in ids: res[id][f] = 0.0 if parent_ids: cr.execute( "SELECT account_analytic_line.account_id, COALESCE(SUM(unit_amount), 0.0) \ FROM account_analytic_line \ JOIN account_analytic_journal \ ON account_analytic_line.journal_id = account_analytic_journal.id \ WHERE account_analytic_line.account_id IN %s \ AND account_analytic_journal.type='general' \ GROUP BY account_analytic_line.account_id", (parent_ids, )) ff = cr.fetchall() for account_id, hq in ff: if account_id not in res: res[account_id] = {} res[account_id][f] = round(hq, dp) for id in ids: res[id][f] = round(res[id][f], dp) elif f == 'ca_theorical': # TODO Take care of pricelist and purchase ! for id in ids: res[id][f] = 0.0 # Warning # This computation doesn't take care of pricelist ! # Just consider list_price if parent_ids: cr.execute( """SELECT account_analytic_line.account_id AS account_id, \ COALESCE(SUM((account_analytic_line.unit_amount * pt.list_price) \ - (account_analytic_line.unit_amount * pt.list_price \ * hr.factor)), 0.0) AS somme FROM account_analytic_line \ LEFT JOIN account_analytic_journal \ ON (account_analytic_line.journal_id = account_analytic_journal.id) \ JOIN product_product pp \ ON (account_analytic_line.product_id = pp.id) \ JOIN product_template pt \ ON (pp.product_tmpl_id = pt.id) \ JOIN account_analytic_account a \ ON (a.id=account_analytic_line.account_id) \ JOIN hr_timesheet_invoice_factor hr \ ON (hr.id=a.to_invoice) \ WHERE account_analytic_line.account_id IN %s \ AND a.to_invoice IS NOT NULL \ AND account_analytic_journal.type IN ('purchase', 'general') GROUP BY account_analytic_line.account_id""", (parent_ids, )) for account_id, sum in cr.fetchall(): res[account_id][f] = round(sum, dp) return res def _ca_invoiced_calc(self, cr, uid, ids, name, arg, context=None): res = {} res_final = {} child_ids = tuple( ids ) #We don't want consolidation for each of these fields because those complex computation is resource-greedy. for i in child_ids: res[i] = 0.0 if not child_ids: return res if child_ids: cr.execute( "SELECT account_analytic_line.account_id, COALESCE(SUM(amount), 0.0) \ FROM account_analytic_line \ JOIN account_analytic_journal \ ON account_analytic_line.journal_id = account_analytic_journal.id \ WHERE account_analytic_line.account_id IN %s \ AND account_analytic_journal.type = 'sale' \ GROUP BY account_analytic_line.account_id", (child_ids, )) for account_id, sum in cr.fetchall(): res[account_id] = round(sum, 2) res_final = res return res_final def _total_cost_calc(self, cr, uid, ids, name, arg, context=None): res = {} res_final = {} child_ids = tuple( ids ) #We don't want consolidation for each of these fields because those complex computation is resource-greedy. for i in child_ids: res[i] = 0.0 if not child_ids: return res if child_ids: cr.execute( """SELECT account_analytic_line.account_id, COALESCE(SUM(amount), 0.0) \ FROM account_analytic_line \ JOIN account_analytic_journal \ ON account_analytic_line.journal_id = account_analytic_journal.id \ WHERE account_analytic_line.account_id IN %s \ AND amount<0 \ GROUP BY account_analytic_line.account_id""", (child_ids, )) for account_id, sum in cr.fetchall(): res[account_id] = round(sum, 2) res_final = res return res_final def _remaining_hours_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): if account.quantity_max != 0: res[account.id] = account.quantity_max - account.hours_quantity else: res[account.id] = 0.0 for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _hours_qtt_invoiced_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): res[account. id] = account.hours_quantity - account.hours_qtt_non_invoiced if res[account.id] < 0: res[account.id] = 0.0 for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _revenue_per_hour_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): if account.hours_qtt_invoiced == 0: res[account.id] = 0.0 else: res[account. id] = account.ca_invoiced / account.hours_qtt_invoiced for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _real_margin_rate_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): if account.ca_invoiced == 0: res[account.id] = 0.0 elif account.total_cost != 0.0: res[account. id] = -(account.real_margin / account.total_cost) * 100 else: res[account.id] = 0.0 for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _remaining_ca_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): if account.amount_max != 0: res[account.id] = account.amount_max - account.ca_invoiced else: res[account.id] = 0.0 for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _real_margin_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): res[account.id] = account.ca_invoiced + account.total_cost for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _theorical_margin_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): res[account.id] = account.ca_theorical + account.total_cost for id in ids: res[id] = round(res.get(id, 0.0), 2) return res 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_max > 0.0: result[record.id] = int( record.hours_quantity >= record.quantity_max) else: result[record.id] = 0 return result def _get_analytic_account(self, cr, uid, ids, context=None): result = set() for line in self.pool.get('account.analytic.line').browse( cr, uid, ids, context=context): result.add(line.account_id.id) return list(result) _columns = { 'is_overdue_quantity': fields.function(_is_overdue_quantity, method=True, type='boolean', string='Overdue Quantity', store={ 'account.analytic.line': (_get_analytic_account, None, 20), }), 'ca_invoiced': fields.function( _ca_invoiced_calc, type='float', string='Invoiced Amount', help="Total customer invoiced amount for this account.", digits_compute=dp.get_precision('Account')), 'total_cost': fields.function( _total_cost_calc, type='float', string='Total Costs', help= "Total of costs for this account. It includes real costs (from invoices) and indirect costs, like time spent on timesheets.", digits_compute=dp.get_precision('Account')), 'ca_to_invoice': fields.function( _analysis_all, multi='analytic_analysis', type='float', string='Uninvoiced Amount', help= "If invoice from analytic account, the remaining amount you can invoice to the customer based on the total costs.", digits_compute=dp.get_precision('Account')), 'ca_theorical': fields.function( _analysis_all, multi='analytic_analysis', type='float', string='Theoretical Revenue', help= "Based on the costs you had on the project, what would have been the revenue if all these costs have been invoiced at the normal sale price provided by the pricelist.", digits_compute=dp.get_precision('Account')), 'hours_quantity': fields.function( _analysis_all, multi='analytic_analysis', type='float', string='Total Time', help= "Number of time you spent on the analytic account (from timesheet). It computes quantities on all journal of type 'general'." ), 'last_invoice_date': fields.function( _analysis_all, multi='analytic_analysis', type='date', string='Last Invoice Date', help= "If invoice from the costs, this is the date of the latest invoiced." ), 'last_worked_invoiced_date': fields.function( _analysis_all, multi='analytic_analysis', type='date', string='Date of Last Invoiced Cost', help= "If invoice from the costs, this is the date of the latest work or cost that have been invoiced." ), 'last_worked_date': fields.function(_analysis_all, multi='analytic_analysis', type='date', string='Date of Last Cost/Work', help="Date of the latest work done on this account."), 'hours_qtt_non_invoiced': fields.function( _analysis_all, multi='analytic_analysis', type='float', string='Uninvoiced Time', help= "Number of time (hours/days) (from journal of type 'general') that can be invoiced if you invoice based on analytic account." ), 'hours_qtt_invoiced': fields.function( _hours_qtt_invoiced_calc, type='float', string='Invoiced Time', help= "Number of time (hours/days) that can be invoiced plus those that already have been invoiced." ), 'remaining_hours': fields.function( _remaining_hours_calc, type='float', string='Remaining Time', help="Computed using the formula: Maximum Time - Total Time"), 'remaining_ca': fields.function( _remaining_ca_calc, type='float', string='Remaining Revenue', help= "Computed using the formula: Max Invoice Price - Invoiced Amount.", digits_compute=dp.get_precision('Account')), 'revenue_per_hour': fields.function( _revenue_per_hour_calc, type='float', string='Revenue per Time (real)', help="Computed using the formula: Invoiced Amount / Total Time", digits_compute=dp.get_precision('Account')), 'real_margin': fields.function( _real_margin_calc, type='float', string='Real Margin', help="Computed using the formula: Invoiced Amount - Total Costs.", digits_compute=dp.get_precision('Account')), 'theorical_margin': fields.function( _theorical_margin_calc, type='float', string='Theoretical Margin', help="Computed using the formula: Theorial Revenue - Total Costs", digits_compute=dp.get_precision('Account')), 'real_margin_rate': fields.function( _real_margin_rate_calc, type='float', string='Real Margin Rate (%)', help= "Computes using the formula: (Real Margin / Total Costs) * 100.", digits_compute=dp.get_precision('Account')), 'month_ids': fields.function(_analysis_all, multi='analytic_analysis', type='many2many', relation='account_analytic_analysis.summary.month', string='Month'), 'user_ids': fields.function(_analysis_all, multi='analytic_analysis', type="many2many", relation='account_analytic_analysis.summary.user', string='User'), }
class report_intrastat(osv.osv): _name = "report.intrastat" _description = "Intrastat report" _auto = False _columns = { 'name': fields.char('Year', size=64, required=False, readonly=True), 'month': fields.selection([('01', 'January'), ('02', 'February'), ('03', 'March'), ('04', 'April'), ('05', 'May'), ('06', 'June'), ('07', 'July'), ('08', 'August'), ('09', 'September'), ('10', 'October'), ('11', 'November'), ('12', 'December')], 'Month', readonly=True), 'supply_units': fields.float('Supply Units', readonly=True), 'ref': fields.char('Source document', size=64, readonly=True), 'code': fields.char('Country code', size=2, readonly=True), 'intrastat_id': fields.many2one('report.intrastat.code', 'Intrastat code', readonly=True), 'weight': fields.float('Weight', readonly=True), 'value': fields.float('Value', readonly=True, digits_compute=dp.get_precision('Account')), 'type': fields.selection([('import', 'Import'), ('export', 'Export')], 'Type'), 'currency_id': fields.many2one('res.currency', "Currency", readonly=True), } def init(self, cr): drop_view_if_exists(cr, 'report_intrastat') cr.execute(""" create or replace view report_intrastat as ( select to_char(inv.create_date, 'YYYY') as name, to_char(inv.create_date, 'MM') as month, min(inv_line.id) as id, intrastat.id as intrastat_id, upper(inv_country.code) as code, sum(case when inv_line.price_unit is not null then inv_line.price_unit * inv_line.quantity else 0 end) as value, sum( case when uom.category_id != puom.category_id then (pt.weight_net * inv_line.quantity) else (pt.weight_net * inv_line.quantity * uom.factor) end ) as weight, sum( case when uom.category_id != puom.category_id then inv_line.quantity else (inv_line.quantity * uom.factor) end ) as supply_units, inv.currency_id as currency_id, inv.number as ref, case when inv.type in ('out_invoice','in_refund') then 'export' else 'import' end as type from account_invoice inv left join account_invoice_line inv_line on inv_line.invoice_id=inv.id left join (product_template pt left join product_product pp on (pp.product_tmpl_id = pt.id)) on (inv_line.product_id = pp.id) left join product_uom uom on uom.id=inv_line.uos_id left join product_uom puom on puom.id = pt.uom_id left join report_intrastat_code intrastat on pt.intrastat_id = intrastat.id left join (res_partner_address inv_address left join res_country inv_country on (inv_country.id = inv_address.country_id)) on (inv_address.id = inv.address_invoice_id) where inv.state in ('open','paid') and inv_line.product_id is not null and inv_country.intrastat=true group by to_char(inv.create_date, 'YYYY'), to_char(inv.create_date, 'MM'),intrastat.id,inv.type,pt.intrastat_id, inv_country.code,inv.number, inv.currency_id )""")
class campaign_analysis(osv.osv): _name = "campaign.analysis" _description = "Campaign Analysis" _auto = False _rec_name = 'date' def _total_cost(self, cr, uid, ids, field_name, arg, context=None): """ @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @param ids: List of case and section Data’s IDs @param context: A standard dictionary for contextual values """ result = {} for ca_obj in self.browse(cr, uid, ids, context=context): wi_ids = self.pool.get('marketing.campaign.workitem').search(cr, uid, [('segment_id.campaign_id', '=', ca_obj.campaign_id.id)]) total_cost = ca_obj.activity_id.variable_cost + \ ((ca_obj.campaign_id.fixed_cost or 1.00) / len(wi_ids)) result[ca_obj.id] = total_cost return result _columns = { 'res_id' : fields.integer('Resource', readonly=True), 'year': fields.char('Year', size=4, readonly=True), 'month': fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'),('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')], 'Month', readonly=True), 'day': fields.char('Day', size=10, readonly=True), 'date': fields.date('Date', readonly=True, select=True), 'campaign_id': fields.many2one('marketing.campaign', 'Campaign', readonly=True), 'activity_id': fields.many2one('marketing.campaign.activity', 'Activity', readonly=True), 'segment_id': fields.many2one('marketing.campaign.segment', 'Segment', readonly=True), 'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), 'country_id': fields.related('partner_id', 'country_id', type='many2one', relation='res.country',string='Country'), 'total_cost' : fields.function(_total_cost, string='Cost', type="float", digits_compute=dp.get_precision('Purchase Price')), 'revenue': fields.float('Revenue', readonly=True, digits_compute=dp.get_precision('Sale Price')), 'count' : fields.integer('# of Actions', readonly=True), 'state': fields.selection([('todo', 'To Do'), ('exception', 'Exception'), ('done', 'Done'), ('cancelled', 'Cancelled')], 'Status', readonly=True), } def init(self, cr): tools.drop_view_if_exists(cr, 'campaign_analysis') cr.execute(""" create or replace view campaign_analysis as ( select min(wi.id) as id, min(wi.res_id) as res_id, to_char(wi.date::date, 'YYYY') as year, to_char(wi.date::date, 'MM') as month, to_char(wi.date::date, 'YYYY-MM-DD') as day, wi.date::date as date, s.campaign_id as campaign_id, wi.activity_id as activity_id, wi.segment_id as segment_id, wi.partner_id as partner_id , wi.state as state, sum(act.revenue) as revenue, count(*) as count from marketing_campaign_workitem wi left join res_partner p on (p.id=wi.partner_id) left join marketing_campaign_segment s on (s.id=wi.segment_id) left join marketing_campaign_activity act on (act.id= wi.activity_id) group by s.campaign_id,wi.activity_id,wi.segment_id,wi.partner_id,wi.state, wi.date::date ) """)
class stock_product_moves(osv.osv): _name = "stock.product.moves" _description = "Product Stock Moves" _auto = False _order = "product_id, date" _columns = { 'product_id': fields.many2one('product.product', 'Product', readonly=True), 'picking_id': fields.many2one('stock.picking', 'Picking', readonly=True), 'date': fields.datetime('Date', readonly=True), 'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True), 'qty_received': fields.float( 'Quantity Received', digits_compute=dp.get_precision('Product Unit of Measure'), readonly=True), 'qty_delivered': fields.float( 'Quantity Delivered', digits_compute=dp.get_precision('Product Unit of Measure'), readonly=True), 'qty_stock': fields.float( 'Quantity On Stock', digits_compute=dp.get_precision('Product Unit of Measure'), readonly=True), 'price_unit': fields.float('Unit Price', digits_compute=dp.get_precision('Product Price'), readonly=True), 'amount': fields.float('Amount', digits_compute=dp.get_precision('Account'), readonly=True) } def init(self, cr): drop_view_if_exists(cr, 'stock_product_moves') cr.execute("""CREATE OR REPLACE VIEW stock_product_moves AS (SELECT sm.id AS id, sm.product_id AS product_id, sp.id AS picking_id, sm.date AS date, pa.id AS partner_id, CASE WHEN sp.type = 'in' THEN pai.id WHEN sp.type = 'out' THEN sai.id ELSE NULL END AS invoice_id, CASE WHEN sp.type = 'in' OR sp.id IS NULL THEN sm.product_qty ELSE NULL END AS qty_received, CASE WHEN sp.type = 'out' THEN sm.product_qty ELSE NULL END AS qty_delivered, (SELECT SUM(CASE WHEN sp1.type = 'in' OR sp1.id IS NULL THEN sm1.product_qty ELSE sm1.product_qty * -1 END) FROM stock_move AS sm1 LEFT OUTER JOIN stock_picking AS sp1 ON sp1.id = sm1.picking_id WHERE sm1.product_id = sm.product_id AND sm1.state = 'done' AND sm1.date <= sm.date) AS qty_stock, sm.price_unit AS price_unit, sm.product_qty * sm.price_unit AS amount FROM stock_move AS sm LEFT OUTER JOIN res_partner AS pa ON pa.id = sm.partner_id LEFT OUTER JOIN stock_picking AS sp ON sp.id = sm.picking_id LEFT OUTER JOIN sale_order_line_invoice_rel AS solir ON solir.order_line_id = sm.sale_line_id LEFT OUTER JOIN purchase_order_line_invoice_rel AS polir ON polir.order_line_id = sm.purchase_line_id LEFT OUTER JOIN account_invoice_line AS sail ON sail.id = solir.invoice_id LEFT OUTER JOIN account_invoice AS sai ON sai.id = sail.invoice_id LEFT OUTER JOIN account_invoice_line AS pail ON pail.id = polir.invoice_id LEFT OUTER JOIN account_invoice AS pai ON pai.id = pail.invoice_id WHERE sm.state = 'done');""")
class hr_expense_report(osv.osv): _name = "hr.expense.report" _description = "Expenses Statistics" _auto = False _rec_name = 'date' _columns = { 'date': fields.date('Date ', readonly=True), 'year': fields.char('Year', size=4, readonly=True), 'day': fields.char('Day', size=128, readonly=True), 'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')], 'Month',readonly=True), 'product_id':fields.many2one('product.product', 'Product', readonly=True), 'journal_id': fields.many2one('account.journal', 'Force Journal', readonly=True), 'product_qty':fields.float('Qty', readonly=True), 'invoiced':fields.integer('# of Invoiced Lines', readonly=True), 'employee_id': fields.many2one('hr.employee', "Employee's Name", readonly=True), 'date_confirm': fields.date('Confirmation Date', readonly=True), 'date_valid': fields.date('Validation Date', readonly=True), 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True), 'department_id':fields.many2one('hr.department','Department', readonly=True), 'company_id':fields.many2one('res.company', 'Company', readonly=True), 'user_id':fields.many2one('res.users', 'Validation User', readonly=True), 'currency_id': fields.many2one('res.currency', 'Currency', readonly=True), 'price_total':fields.float('Total Price', readonly=True, digits_compute=dp.get_precision('Account')), 'delay_valid':fields.float('Delay to Valid', readonly=True), 'delay_confirm':fields.float('Delay to Confirm', readonly=True), 'analytic_account': fields.many2one('account.analytic.account','Analytic account',readonly=True), 'price_average':fields.float('Average Price', readonly=True, digits_compute=dp.get_precision('Account')), 'nbr':fields.integer('# of Lines', readonly=True), 'no_of_products':fields.integer('# of Products', readonly=True), 'no_of_account':fields.integer('# of Accounts', readonly=True), 'state': fields.selection([ ('draft', 'Draft'), ('confirm', 'Waiting confirmation'), ('accepted', 'Accepted'), ('invoiced', 'Invoiced'), ('paid', 'Reimbursed'), ('cancelled', 'Cancelled')], 'Status', readonly=True), } _order = 'date desc' def init(self, cr): tools.drop_view_if_exists(cr, 'hr_expense_report') cr.execute(""" create or replace view hr_expense_report as ( select min(l.id) as id, date_trunc('day',s.date) as date, s.employee_id, s.journal_id, s.currency_id, to_date(to_char(s.date_confirm, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_confirm, to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_valid, s.invoice_id, count(s.invoice_id) as invoiced, s.user_valid as user_id, s.department_id, to_char(date_trunc('day',s.create_date), 'YYYY') as year, to_char(date_trunc('day',s.create_date), 'MM') as month, to_char(date_trunc('day',s.create_date), 'YYYY-MM-DD') as day, avg(extract('epoch' from age(s.date_valid,s.date)))/(3600*24) as delay_valid, avg(extract('epoch' from age(s.date_valid,s.date_confirm)))/(3600*24) as delay_confirm, l.product_id as product_id, l.analytic_account as analytic_account, sum(l.unit_quantity * u.factor) as product_qty, s.company_id as company_id, sum(l.unit_quantity*l.unit_amount) as price_total, (sum(l.unit_quantity*l.unit_amount)/sum(case when l.unit_quantity=0 or u.factor=0 then 1 else l.unit_quantity * u.factor end))::decimal(16,2) as price_average, count(*) as nbr, (select unit_quantity from hr_expense_line where id=l.id and product_id is not null) as no_of_products, (select analytic_account from hr_expense_line where id=l.id and analytic_account is not null) as no_of_account, s.state from hr_expense_line l left join hr_expense_expense s on (s.id=l.expense_id) left join product_uom u on (u.id=l.uom_id) group by date_trunc('day',s.date), to_char(date_trunc('day',s.create_date), 'YYYY'), to_char(date_trunc('day',s.create_date), 'MM'), to_char(date_trunc('day',s.create_date), 'YYYY-MM-DD'), to_date(to_char(s.date_confirm, 'dd-MM-YYYY'),'dd-MM-YYYY'), to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY'), l.product_id, l.analytic_account, s.invoice_id, s.currency_id, s.user_valid, s.department_id, l.uom_id, l.id, s.state, s.journal_id, s.company_id, s.employee_id ) """)
class hr_recruitment_report(osv.osv): _name = "hr.recruitment.report" _description = "Recruitments Statistics" _auto = False _rec_name = 'date' _columns = { 'user_id': fields.many2one('res.users', 'User', readonly=True), 'nbr': fields.integer('# of Cases', readonly=True), 'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True), 'month':fields.selection([('01', 'January'), ('02', 'February'), \ ('03', 'March'), ('04', 'April'),\ ('05', 'May'), ('06', 'June'), \ ('07', 'July'), ('08', 'August'),\ ('09', 'September'), ('10', 'October'),\ ('11', 'November'), ('12', 'December')], 'Month', readonly=True), 'company_id': fields.many2one('res.company', 'Company', readonly=True), 'day': fields.char('Day', size=128, readonly=True), 'year': fields.char('Year', size=4, readonly=True), 'date': fields.date('Date', readonly=True), 'date_closed': fields.date('Closed', readonly=True), 'job_id': fields.many2one('hr.job', 'Applied Job',readonly=True), 'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage'), 'type_id': fields.many2one('hr.recruitment.degree', 'Degree'), 'department_id': fields.many2one('hr.department','Department',readonly=True), 'priority': fields.selection(hr_recruitment.AVAILABLE_PRIORITIES, 'Appreciation'), 'salary_prop' : fields.float("Salary Proposed", digits_compute=dp.get_precision('Account')), 'salary_prop_avg' : fields.float("Avg Salary Proposed", group_operator="avg", digits_compute=dp.get_precision('Account')), 'salary_exp' : fields.float("Salary Expected", digits_compute=dp.get_precision('Account')), 'partner_id': fields.many2one('res.partner', 'Partner',readonly=True), 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact Name',readonly=True), 'available': fields.float("Availability"), 'delay_open': fields.float('Avg. Delay to Open', digits=(16,2), readonly=True, group_operator="avg", help="Number of Days to close the project issue"), 'delay_close': fields.float('Avg. Delay to Close', digits=(16,2), readonly=True, group_operator="avg", help="Number of Days to close the project issue"), } _order = 'date desc' def init(self, cr): tools.drop_view_if_exists(cr, 'hr_recruitment_report') cr.execute(""" create or replace view hr_recruitment_report as ( select min(s.id) as id, date_trunc('day',s.create_date) as date, date_trunc('day',s.date_closed) as date_closed, to_char(s.create_date, 'YYYY') as year, to_char(s.create_date, 'MM') as month, to_char(s.create_date, 'YYYY-MM-DD') as day, s.state, s.partner_id, s.company_id, s.partner_address_id, s.user_id, s.job_id, s.type_id, sum(s.availability) as available, s.department_id, s.priority, s.stage_id, sum(salary_proposed) as salary_prop, (sum(salary_proposed)/count(*)) as salary_prop_avg, sum(salary_expected) as salary_exp, extract('epoch' from (s.date_open-s.create_date))/(3600*24) as delay_open, extract('epoch' from (s.date_closed-s.create_date))/(3600*24) as delay_close, count(*) as nbr from hr_applicant s group by to_char(s.create_date, 'YYYY'), to_char(s.create_date, 'MM'), to_char(s.create_date, 'YYYY-MM-DD') , date_trunc('day',s.create_date), date_trunc('day',s.date_closed), s.date_open, s.create_date, s.date_closed, s.state, s.partner_id, s.partner_address_id, s.company_id, s.user_id, s.stage_id, s.type_id, s.priority, s.job_id, s.department_id ) """)
class account_analytic_account(osv.osv): _name = "account.analytic.account" _inherit = "account.analytic.account" def _analysis_all(self, cr, uid, ids, fields, arg, context=None): dp = 2 res = dict([(i, {}) for i in ids]) parent_ids = tuple( ids ) #We don't want consolidation for each of these fields because those complex computation is resource-greedy. accounts = self.browse(cr, uid, ids, context=context) for f in fields: if f == 'user_ids': cr.execute('SELECT MAX(id) FROM res_users') max_user = cr.fetchone()[0] if parent_ids: cr.execute('SELECT DISTINCT("user") FROM account_analytic_analysis_summary_user ' \ 'WHERE account_id IN %s AND unit_amount <> 0.0', (parent_ids,)) result = cr.fetchall() else: result = [] for id in ids: res[id][f] = [int((id * max_user) + x[0]) for x in result] elif f == 'month_ids': if parent_ids: cr.execute('SELECT DISTINCT(month_id) FROM account_analytic_analysis_summary_month ' \ 'WHERE account_id IN %s AND unit_amount <> 0.0', (parent_ids,)) result = cr.fetchall() else: result = [] for id in ids: res[id][f] = [ int(id * 1000000 + int(x[0])) for x in result ] elif f == 'last_worked_invoiced_date': for id in ids: res[id][f] = False if parent_ids: cr.execute( "SELECT account_analytic_line.account_id, MAX(date) \ FROM account_analytic_line \ WHERE account_id IN %s \ AND invoice_id IS NOT NULL \ GROUP BY account_analytic_line.account_id;", (parent_ids, )) for account_id, sum in cr.fetchall(): if account_id not in res: res[account_id] = {} res[account_id][f] = sum elif f == 'ca_to_invoice': for id in ids: res[id][f] = 0.0 res2 = {} if parent_ids: # Amount uninvoiced hours to invoice at sale price # Warning # This computation doesn't take care of pricelist ! # Just consider list_price cr.execute( """SELECT account_analytic_account.id, \ COALESCE(SUM (product_template.list_price * \ account_analytic_line.unit_amount * \ ((100-hr_timesheet_invoice_factor.factor)/100)), 0.0) \ AS ca_to_invoice \ FROM product_template \ JOIN product_product \ ON product_template.id = product_product.product_tmpl_id \ JOIN account_analytic_line \ ON account_analytic_line.product_id = product_product.id \ JOIN account_analytic_journal \ ON account_analytic_line.journal_id = account_analytic_journal.id \ JOIN account_analytic_account \ ON account_analytic_account.id = account_analytic_line.account_id \ JOIN hr_timesheet_invoice_factor \ ON hr_timesheet_invoice_factor.id = account_analytic_account.to_invoice \ WHERE account_analytic_account.id IN %s \ AND account_analytic_line.invoice_id IS NULL \ AND account_analytic_line.to_invoice IS NOT NULL \ AND account_analytic_journal.type = 'general' \ GROUP BY account_analytic_account.id;""", (parent_ids, )) for account_id, sum in cr.fetchall(): if account_id not in res: res[account_id] = {} res[account_id][f] = round(sum, dp) # sum both result on account_id for id in ids: res[id][f] = round(res.get(id, {}).get(f, 0.0), dp) + round(res2.get(id, 0.0), 2) elif f == 'last_invoice_date': for id in ids: res[id][f] = False if parent_ids: cr.execute( "SELECT account_analytic_line.account_id, \ DATE(MAX(account_invoice.date_invoice)) \ FROM account_analytic_line \ JOIN account_invoice \ ON account_analytic_line.invoice_id = account_invoice.id \ WHERE account_analytic_line.account_id IN %s \ AND account_analytic_line.invoice_id IS NOT NULL \ GROUP BY account_analytic_line.account_id", (parent_ids, )) for account_id, lid in cr.fetchall(): res[account_id][f] = lid elif f == 'last_worked_date': for id in ids: res[id][f] = False if parent_ids: cr.execute( "SELECT account_analytic_line.account_id, MAX(date) \ FROM account_analytic_line \ WHERE account_id IN %s \ AND invoice_id IS NULL \ GROUP BY account_analytic_line.account_id", (parent_ids, )) for account_id, lwd in cr.fetchall(): if account_id not in res: res[account_id] = {} res[account_id][f] = lwd elif f == 'hours_qtt_non_invoiced': for id in ids: res[id][f] = 0.0 if parent_ids: cr.execute( "SELECT account_analytic_line.account_id, COALESCE(SUM(unit_amount), 0.0) \ FROM account_analytic_line \ JOIN account_analytic_journal \ ON account_analytic_line.journal_id = account_analytic_journal.id \ WHERE account_analytic_line.account_id IN %s \ AND account_analytic_journal.type='general' \ AND invoice_id IS NULL \ AND to_invoice IS NOT NULL \ GROUP BY account_analytic_line.account_id;", (parent_ids, )) for account_id, sua in cr.fetchall(): if account_id not in res: res[account_id] = {} res[account_id][f] = round(sua, dp) for id in ids: res[id][f] = round(res[id][f], dp) elif f == 'hours_quantity': for id in ids: res[id][f] = 0.0 if parent_ids: cr.execute( "SELECT account_analytic_line.account_id, COALESCE(SUM(unit_amount), 0.0) \ FROM account_analytic_line \ JOIN account_analytic_journal \ ON account_analytic_line.journal_id = account_analytic_journal.id \ WHERE account_analytic_line.account_id IN %s \ AND account_analytic_journal.type='general' \ GROUP BY account_analytic_line.account_id", (parent_ids, )) ff = cr.fetchall() for account_id, hq in ff: if account_id not in res: res[account_id] = {} res[account_id][f] = round(hq, dp) for id in ids: res[id][f] = round(res[id][f], dp) elif f == 'ca_theorical': # TODO Take care of pricelist and purchase ! for id in ids: res[id][f] = 0.0 # Warning # This computation doesn't take care of pricelist ! # Just consider list_price if parent_ids: cr.execute( """SELECT account_analytic_line.account_id AS account_id, \ COALESCE(SUM((account_analytic_line.unit_amount * pt.list_price) \ - (account_analytic_line.unit_amount * pt.list_price \ * hr.factor)), 0.0) AS somme FROM account_analytic_line \ LEFT JOIN account_analytic_journal \ ON (account_analytic_line.journal_id = account_analytic_journal.id) \ JOIN product_product pp \ ON (account_analytic_line.product_id = pp.id) \ JOIN product_template pt \ ON (pp.product_tmpl_id = pt.id) \ JOIN account_analytic_account a \ ON (a.id=account_analytic_line.account_id) \ JOIN hr_timesheet_invoice_factor hr \ ON (hr.id=a.to_invoice) \ WHERE account_analytic_line.account_id IN %s \ AND a.to_invoice IS NOT NULL \ AND account_analytic_journal.type IN ('purchase', 'general') GROUP BY account_analytic_line.account_id""", (parent_ids, )) for account_id, sum in cr.fetchall(): res[account_id][f] = round(sum, dp) return res def _ca_invoiced_calc(self, cr, uid, ids, name, arg, context=None): res = {} res_final = {} child_ids = tuple( ids ) #We don't want consolidation for each of these fields because those complex computation is resource-greedy. for i in child_ids: res[i] = 0.0 if not child_ids: return res if child_ids: cr.execute( "SELECT account_analytic_line.account_id, COALESCE(SUM(amount), 0.0) \ FROM account_analytic_line \ JOIN account_analytic_journal \ ON account_analytic_line.journal_id = account_analytic_journal.id \ WHERE account_analytic_line.account_id IN %s \ AND account_analytic_journal.type = 'sale' \ GROUP BY account_analytic_line.account_id", (child_ids, )) for account_id, sum in cr.fetchall(): res[account_id] = round(sum, 2) res_final = res return res_final def _total_cost_calc(self, cr, uid, ids, name, arg, context=None): res = {} res_final = {} child_ids = tuple( ids ) #We don't want consolidation for each of these fields because those complex computation is resource-greedy. for i in child_ids: res[i] = 0.0 if not child_ids: return res if child_ids: cr.execute( """SELECT account_analytic_line.account_id, COALESCE(SUM(amount), 0.0) \ FROM account_analytic_line \ JOIN account_analytic_journal \ ON account_analytic_line.journal_id = account_analytic_journal.id \ WHERE account_analytic_line.account_id IN %s \ AND amount<0 \ GROUP BY account_analytic_line.account_id""", (child_ids, )) for account_id, sum in cr.fetchall(): res[account_id] = round(sum, 2) res_final = res return res_final def _remaining_hours_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): if account.quantity_max != 0: res[account.id] = account.quantity_max - account.hours_quantity else: res[account.id] = 0.0 for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _remaining_hours_to_invoice_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): res[account.id] = max( account.hours_qtt_est - account.timesheet_ca_invoiced, account.ca_to_invoice) return res def _hours_qtt_invoiced_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): res[account. id] = account.hours_quantity - account.hours_qtt_non_invoiced if res[account.id] < 0: res[account.id] = 0.0 for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _revenue_per_hour_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): if account.hours_qtt_invoiced == 0: res[account.id] = 0.0 else: res[account. id] = account.ca_invoiced / account.hours_qtt_invoiced for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _real_margin_rate_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): if account.ca_invoiced == 0: res[account.id] = 0.0 elif account.total_cost != 0.0: res[account. id] = -(account.real_margin / account.total_cost) * 100 else: res[account.id] = 0.0 for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _fix_price_to_invoice_calc(self, cr, uid, ids, name, arg, context=None): sale_obj = self.pool.get('sale.order') res = {} for account in self.browse(cr, uid, ids, context=context): res[account.id] = 0.0 sale_ids = sale_obj.search( cr, uid, [('project_id', '=', account.id), ('partner_id', '=', account.partner_id.id)], context=context) for sale in sale_obj.browse(cr, uid, sale_ids, context=context): if not sale.invoiced: res[account.id] += sale.amount_untaxed for invoice in sale.invoice_ids: if invoice.state not in ('draft', 'cancel'): res[account.id] -= invoice.amount_untaxed return res def _timesheet_ca_invoiced_calc(self, cr, uid, ids, name, arg, context=None): lines_obj = self.pool.get('account.analytic.line') res = {} for account in self.browse(cr, uid, ids, context=context): res[account.id] = 0.0 line_ids = lines_obj.search(cr, uid, [('account_id', '=', account.id), ('invoice_id', '!=', False), ('to_invoice', '!=', False), ('journal_id.type', '=', 'general')], context=context) for line in lines_obj.browse(cr, uid, line_ids, context=context): res[account.id] += line.invoice_id.amount_untaxed return res def _remaining_ca_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): res[account.id] = max(account.amount_max - account.ca_invoiced, account.fix_price_to_invoice) return res def _real_margin_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): res[account.id] = account.ca_invoiced + account.total_cost for id in ids: res[id] = round(res.get(id, 0.0), 2) return res def _theorical_margin_calc(self, cr, uid, ids, name, arg, context=None): res = {} for account in self.browse(cr, uid, ids, context=context): res[account.id] = account.ca_theorical + account.total_cost for id in ids: res[id] = round(res.get(id, 0.0), 2) return res 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_max > 0.0: result[record.id] = int( record.hours_quantity >= record.quantity_max) else: result[record.id] = 0 return result def _get_analytic_account(self, cr, uid, ids, context=None): result = set() for line in self.pool.get('account.analytic.line').browse( cr, uid, ids, context=context): result.add(line.account_id.id) return list(result) def _get_total_estimation(self, account): tot_est = 0.0 if account.fix_price_invoices: tot_est += account.amount_max if account.invoice_on_timesheets: tot_est += account.hours_qtt_est return tot_est def _get_total_invoiced(self, account): total_invoiced = 0.0 if account.fix_price_invoices: total_invoiced += account.ca_invoiced if account.invoice_on_timesheets: total_invoiced += account.timesheet_ca_invoiced return total_invoiced def _get_total_remaining(self, account): total_remaining = 0.0 if account.fix_price_invoices: total_remaining += account.remaining_ca if account.invoice_on_timesheets: total_remaining += account.remaining_hours_to_invoice return total_remaining def _get_total_toinvoice(self, account): total_toinvoice = 0.0 if account.fix_price_invoices: total_toinvoice += account.fix_price_to_invoice if account.invoice_on_timesheets: total_toinvoice += account.ca_to_invoice return total_toinvoice def _sum_of_fields(self, cr, uid, ids, name, arg, context=None): res = dict([(i, {}) for i in ids]) for account in self.browse(cr, uid, ids, context=context): res[account.id]['est_total'] = self._get_total_estimation(account) res[account.id]['invoiced_total'] = self._get_total_invoiced( account) res[account.id]['remaining_total'] = self._get_total_remaining( account) res[account.id]['toinvoice_total'] = self._get_total_toinvoice( account) return res _columns = { 'is_overdue_quantity': fields.function(_is_overdue_quantity, method=True, type='boolean', string='Overdue Quantity', store={ 'account.analytic.line': (_get_analytic_account, None, 20), }), 'ca_invoiced': fields.function( _ca_invoiced_calc, type='float', string='Invoiced Amount', help="Total customer invoiced amount for this account.", digits_compute=dp.get_precision('Account')), 'total_cost': fields.function( _total_cost_calc, type='float', string='Total Costs', help= "Total of costs for this account. It includes real costs (from invoices) and indirect costs, like time spent on timesheets.", digits_compute=dp.get_precision('Account')), 'ca_to_invoice': fields.function( _analysis_all, multi='analytic_analysis', type='float', string='Uninvoiced Amount', help= "If invoice from analytic account, the remaining amount you can invoice to the customer based on the total costs.", digits_compute=dp.get_precision('Account')), 'ca_theorical': fields.function( _analysis_all, multi='analytic_analysis', type='float', string='Theoretical Revenue', help= "Based on the costs you had on the project, what would have been the revenue if all these costs have been invoiced at the normal sale price provided by the pricelist.", digits_compute=dp.get_precision('Account')), 'hours_quantity': fields.function( _analysis_all, multi='analytic_analysis', type='float', string='Total Worked Time', help= "Number of time you spent on the analytic account (from timesheet). It computes quantities on all journal of type 'general'." ), 'last_invoice_date': fields.function( _analysis_all, multi='analytic_analysis', type='date', string='Last Invoice Date', help= "If invoice from the costs, this is the date of the latest invoiced." ), 'last_worked_invoiced_date': fields.function( _analysis_all, multi='analytic_analysis', type='date', string='Date of Last Invoiced Cost', help= "If invoice from the costs, this is the date of the latest work or cost that have been invoiced." ), 'last_worked_date': fields.function(_analysis_all, multi='analytic_analysis', type='date', string='Date of Last Cost/Work', help="Date of the latest work done on this account."), 'hours_qtt_non_invoiced': fields.function( _analysis_all, multi='analytic_analysis', type='float', string='Uninvoiced Time', help= "Number of time (hours/days) (from journal of type 'general') that can be invoiced if you invoice based on analytic account." ), 'hours_qtt_invoiced': fields.function( _hours_qtt_invoiced_calc, type='float', string='Invoiced Time', help= "Number of time (hours/days) that can be invoiced plus those that already have been invoiced." ), 'remaining_hours': fields.function( _remaining_hours_calc, type='float', string='Remaining Time', help="Computed using the formula: Maximum Time - Total Worked Time" ), 'remaining_hours_to_invoice': fields.function( _remaining_hours_to_invoice_calc, type='float', string='Remaining Time', help= "Computed using the formula: Maximum Time - Total Invoiced Time"), 'fix_price_to_invoice': fields.function(_fix_price_to_invoice_calc, type='float', string='Remaining Time', help="Sum of quotations for this contract."), 'timesheet_ca_invoiced': fields.function( _timesheet_ca_invoiced_calc, type='float', string='Remaining Time', help="Sum of timesheet lines invoiced for this contract."), 'remaining_ca': fields.function( _remaining_ca_calc, type='float', string='Remaining Revenue', help= "Computed using the formula: Max Invoice Price - Invoiced Amount.", digits_compute=dp.get_precision('Account')), 'revenue_per_hour': fields.function( _revenue_per_hour_calc, type='float', string='Revenue per Time (real)', help="Computed using the formula: Invoiced Amount / Total Time", digits_compute=dp.get_precision('Account')), 'real_margin': fields.function( _real_margin_calc, type='float', string='Real Margin', help="Computed using the formula: Invoiced Amount - Total Costs.", digits_compute=dp.get_precision('Account')), 'theorical_margin': fields.function( _theorical_margin_calc, type='float', string='Theoretical Margin', help= "Computed using the formula: Theoretical Revenue - Total Costs", digits_compute=dp.get_precision('Account')), 'real_margin_rate': fields.function( _real_margin_rate_calc, type='float', string='Real Margin Rate (%)', help= "Computes using the formula: (Real Margin / Total Costs) * 100.", digits_compute=dp.get_precision('Account')), 'fix_price_invoices': fields.boolean('Fix Price Invoices'), 'invoice_on_timesheets': fields.boolean("Invoice On Timesheets"), 'month_ids': fields.function(_analysis_all, multi='analytic_analysis', type='many2many', relation='account_analytic_analysis.summary.month', string='Month'), 'user_ids': fields.function(_analysis_all, multi='analytic_analysis', type="many2many", relation='account_analytic_analysis.summary.user', string='User'), 'hours_qtt_est': fields.float('Estimation of Hours to Invoice'), 'est_total': fields.function(_sum_of_fields, type="float", multi="sum_of_all", string="Total Estimation"), 'invoiced_total': fields.function(_sum_of_fields, type="float", multi="sum_of_all", string="Total Invoiced"), 'remaining_total': fields.function( _sum_of_fields, type="float", multi="sum_of_all", string="Total Remaining", help= "Expectation of remaining income for this contract. Computed as the sum of remaining subtotals which, in turn, are computed as the maximum between '(Estimation - Invoiced)' and 'To Invoice' amounts" ), 'toinvoice_total': fields.function( _sum_of_fields, type="float", multi="sum_of_all", string="Total to Invoice", help=" Sum of everything that could be invoiced for this contract." ), } def open_sale_order_lines(self, cr, uid, ids, context=None): if context is None: context = {} sale_ids = self.pool.get('sale.order').search( cr, uid, [('project_id', '=', context.get('search_default_project_id', False)), ('partner_id', '=', context.get('search_default_partner_id', False))]) names = [ record.name for record in self.browse(cr, uid, ids, context=context) ] name = _('Sale Order Lines of %s') % ','.join(names) return { 'type': 'ir.actions.act_window', 'name': name, 'view_type': 'form', 'view_mode': 'tree,form', 'context': context, 'domain': [('order_id', 'in', sale_ids)], 'res_model': 'sale.order.line', 'nodestroy': True, } def on_change_template(self, cr, uid, ids, template_id, context=None): if not template_id: return {} res = super(account_analytic_account, self).on_change_template(cr, uid, ids, template_id, context=context) if template_id and 'value' in res: template = self.browse(cr, uid, template_id, context=context) res['value']['fix_price_invoices'] = template.fix_price_invoices res['value'][ 'invoice_on_timesheets'] = template.invoice_on_timesheets res['value']['hours_qtt_est'] = template.hours_qtt_est res['value']['amount_max'] = template.amount_max res['value']['to_invoice'] = template.to_invoice.id res['value']['pricelist_id'] = template.pricelist_id.id return res
class marketing_campaign(osv.osv): _name = "marketing.campaign" _description = "Marketing Campaign" _columns = { 'name': fields.char('Name', size=64, required=True), 'object_id': fields.many2one('ir.model', 'Resource', required=True, help="Choose the resource on which you want \ this campaign to be run" ), 'partner_field_id': fields.many2one('ir.model.fields', 'Partner Field', domain="[('model_id', '=', object_id), ('ttype', '=', 'many2one'), ('relation', '=', 'res.partner')]", help="The generated workitems will be linked to the partner related to the record. If the record is the partner itself leave this field empty."), 'mode': fields.selection([('test', 'Test Directly'), ('test_realtime', 'Test in Realtime'), ('manual', 'With Manual Confirmation'), ('active', 'Normal')], 'Mode', required=True, help= \ """Test - It creates and process all the activities directly (without waiting for the delay on transitions) but does not send emails or produce reports. Test in Realtime - It creates and processes all the activities directly but does not send emails or produce reports. With Manual Confirmation - the campaigns runs normally, but the user has to validate all workitem manually. Normal - the campaign runs normally and automatically sends all emails and reports (be very careful with this mode, you're live!)"""), 'state': fields.selection([('draft', 'Draft'), ('running', 'Running'), ('done', 'Done'), ('cancelled', 'Cancelled'),], 'State',), 'activity_ids': fields.one2many('marketing.campaign.activity', 'campaign_id', 'Activities'), 'fixed_cost': fields.float('Fixed Cost', help="Fixed cost for running this campaign. You may also specify variable cost and revenue on each campaign activity. Cost and Revenue statistics are included in Campaign Reporting.", digits_compute=dp.get_precision('Purchase Price')), } _defaults = { 'state': lambda *a: 'draft', 'mode': lambda *a: 'test', } def state_running_set(self, cr, uid, ids, *args): # TODO check that all subcampaigns are running campaign = self.browse(cr, uid, ids[0]) if not campaign.activity_ids: raise osv.except_osv( _("Error"), _("The campaign cannot be started: there are no activities in it" )) has_start = False has_signal_without_from = False for activity in campaign.activity_ids: if activity.start: has_start = True if activity.signal and len(activity.from_ids) == 0: has_signal_without_from = True if activity.type != 'email': continue if not activity.email_template_id.from_account: raise osv.except_osv( _("Error"), _("The campaign cannot be started: the email account is missing in email activity '%s'" ) % activity.name) if activity.email_template_id.from_account.state != 'approved': raise osv.except_osv( _("Error"), _("The campaign cannot be started: the email account is not approved in email activity '%s'" ) % activity.name) if not has_start and not has_signal_without_from: raise osv.except_osv( _("Error"), _("The campaign cannot be started: it doesn't have any starting activity (or any activity with a signal and no previous activity)" )) return self.write(cr, uid, ids, {'state': 'running'}) def state_done_set(self, cr, uid, ids, *args): # TODO check that this campaign is not a subcampaign in running mode. segment_ids = self.pool.get('marketing.campaign.segment').search( cr, uid, [('campaign_id', 'in', ids), ('state', '=', 'running')]) if segment_ids: raise osv.except_osv( _("Error"), _("The campaign cannot be marked as done before all segments are done" )) self.write(cr, uid, ids, {'state': 'done'}) return True def state_cancel_set(self, cr, uid, ids, *args): # TODO check that this campaign is not a subcampaign in running mode. self.write(cr, uid, ids, {'state': 'cancelled'}) return True def signal(self, cr, uid, model, res_id, signal, run_existing=True, context=None): record = self.pool.get(model).browse(cr, uid, res_id, context) return self._signal(cr, uid, record, signal, run_existing, context) def _signal(self, cr, uid, record, signal, run_existing=True, context=None): if not signal: raise ValueError('signal cannot be False') Workitems = self.pool.get('marketing.campaign.workitem') domain = [('object_id.model', '=', record._table._name), ('state', '=', 'running')] campaign_ids = self.search(cr, uid, domain, context=context) for campaign in self.browse(cr, uid, campaign_ids, context=context): for activity in campaign.activity_ids: if activity.signal != signal: continue data = dict(activity_id=activity.id, res_id=record.id, state='todo') wi_domain = [(k, '=', v) for k, v in data.items()] wi_ids = Workitems.search(cr, uid, wi_domain, context=context) if wi_ids: if not run_existing: continue else: partner = self._get_partner_for(campaign, record) if partner: data['partner_id'] = partner.id wi_id = Workitems.create(cr, uid, data, context=context) wi_ids = [wi_id] Workitems.process(cr, uid, wi_ids, context=context) return True def _get_partner_for(self, campaign, record): partner_field = campaign.partner_field_id.name if partner_field: return getattr(record, partner_field) elif campaign.object_id.model == 'res.partner': return record return None # prevent duplication until the server properly duplicates several levels of nested o2m def copy(self, cr, uid, id, default=None, context=None): raise osv.except_osv( _("Operation not supported"), _("Sorry, campaign duplication is not supported at the moment."))
class intrastat(osv.osv): _name = "intrastat" _description = "Intrastat report" _auto = False _columns = { 'intrastat_id': fields.many2one('intrastat.code', 'Intrastat code', readonly=True), 'intrastat_name': fields.related('intrastat_id', 'description', type='char', string='Intrastat name'), 'country_supplier': fields.many2one('res.country', 'Country of supplier', readonly=True), 'country_supplier_code': fields.related('country_supplier', 'code', type='char', string='Country of suppl.code'), 'transaction_type_id': fields.many2one('intrastat.transaction.type', 'Transaction type', readonly=True), 'transaction_type_code': fields.related('transaction_type_id', 'code', type='char', string='Trans. type'), 'country_origin': fields.many2one('res.country', 'Country of origin', readonly=True), 'country_origin_code': fields.related('country_origin', 'code', type='char', string='Country of origin'), 'weight': fields.float('Weight', readonly=True), 'value': fields.float('Value', readonly=True, digits_compute=dp.get_precision('Account')), 'date': fields.char('Date done', size=64, required=False, readonly=True), 'month': fields.selection([('01', 'January'), ('02', 'February'), ('03', 'March'), ('04', 'April'), ('05', 'May'), ('06', 'June'), ('07', 'July'), ('08', 'August'), ('09', 'September'), ('10', 'October'), ('11', 'November'), ('12', 'December')], 'Month', readonly=True), 'year': fields.char('Year', size=64, required=False, readonly=True), } def init(self, cr): drop_view_if_exists(cr, 'intrastat') cr.execute(""" create or replace view intrastat as ( select MIN (SM.id) AS id, to_char(SP.date_done, 'YYYY') || '-' || to_char(SP.date_done, 'MM') as date, to_char(SP.date_done, 'MM') as month, to_char(SP.date_done, 'YYYY') as year, SP.transaction_type_id as transaction_type_id, PT.intrastat_id AS intrastat_id, SUM (SM.product_qty * PT.weight_net) AS weight, SUM (SM.product_qty * SM.price_unit) AS value, SM.country_origin_id AS country_origin, PA.country_id AS country_supplier from stock_picking SP left join stock_move SM on SM.picking_id=SP.id left join (product_template PT left join product_product PP on (PP.product_tmpl_id = PT.id)) on (SM.product_id = PP.id) left join res_partner PA on PA.id = SP.partner_id left join res_country C on C.id = PA.country_id where SP.state = 'done' AND PA.country_id != 201 AND SP.type = 'in' AND C.intrastat = true group by to_char(SP.date_done, 'YYYY') || '-' || to_char(SP.date_done, 'MM'), to_char(SP.date_done, 'MM'), to_char(SP.date_done, 'YYYY'), SP.transaction_type_id, PT.intrastat_id, SM.country_origin_id, PA.country_id )""")