def test_2_related_related(self): """ test a related field referring to a related field """ # add a related field on a related field on res.partner old_columns = self.partner._columns self.partner._columns = dict(old_columns) self.partner._columns.update({ 'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'), 'related_related_company_id': fields.related('single_related_company_id', type='many2one', obj='res.company'), }) self.do_test_company_field('related_related_company_id') # restore res.partner fields self.partner._columns = old_columns
def test_3_read_write(self): """ write on a related field """ # add a related field test_related_company_id on res.partner old_columns = self.partner._columns self.partner._columns = dict(old_columns) self.partner._columns.update({ 'related_company_partner_id': fields.related('company_id', 'partner_id', type='many2one', obj='res.partner'), }) # find a company with a non-null partner_id company_ids = self.company.search(self.cr, self.uid, [('partner_id', '!=', False)], limit=1) company = self.company.browse(self.cr, self.uid, company_ids[0]) # find partners that satisfy [('partner_id.company_id', '=', company.id)] partner_ids = self.partner.search(self.cr, self.uid, [('related_company_partner_id', '=', company.id)]) partner = self.partner.browse(self.cr, self.uid, partner_ids[0]) # create a new partner, and assign it to company new_partner_id = self.partner.create(self.cr, self.uid, {'name': 'Foo'}) partner.write({'related_company_partner_id': new_partner_id}) company = self.company.browse(self.cr, self.uid, company_ids[0]) self.assertEqual(company.partner_id.id, new_partner_id) partner = self.partner.browse(self.cr, self.uid, partner_ids[0]) self.assertEqual(partner.related_company_partner_id.id, new_partner_id) # restore res.partner fields self.partner._columns = old_columns
def test_2_related_related(self): """ test a related field referring to a related field """ # add a related field on a related field on res.partner # and simulate a _inherits_reload() to populate _all_columns. old_columns = self.partner._columns old_all_columns = self.partner._all_columns self.partner._columns = dict(old_columns) self.partner._all_columns = dict(old_all_columns) self.partner._columns.update({ 'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'), 'related_related_company_id': fields.related('single_related_company_id', type='many2one', obj='res.company'), }) self.partner._all_columns.update({ 'single_related_company_id': fields.column_info('single_related_company_id', self.partner._columns['single_related_company_id'], None, None, None), 'related_related_company_id': fields.column_info('related_related_company_id', self.partner._columns['related_related_company_id'], None, None, None) }) self.do_test_company_field('related_related_company_id') # restore res.partner fields self.partner._columns = old_columns self.partner._all_columns = old_all_columns
def test_1_single_related(self): """ test a related field with a single indirection like fields.related('foo') """ # add a related field test_related_company_id on res.partner old_columns = self.partner._columns self.partner._columns = dict(old_columns) self.partner._columns.update({ 'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'), }) self.do_test_company_field('single_related_company_id') # restore res.partner fields self.partner._columns = old_columns
def test_1_single_related(self): """ test a related field with a single indirection like fields.related('foo') """ # add a related field test_related_company_id on res.partner # and simulate a _inherits_reload() to populate _all_columns. old_columns = self.partner._columns old_all_columns = self.partner._all_columns self.partner._columns = dict(old_columns) self.partner._all_columns = dict(old_all_columns) self.partner._columns.update({ 'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'), }) self.partner._all_columns.update({ 'single_related_company_id': fields.column_info('single_related_company_id', self.partner._columns['single_related_company_id'], None, None, None) }) self.do_test_company_field('single_related_company_id') # restore res.partner fields self.partner._columns = old_columns self.partner._all_columns = old_all_columns
def test_0_related(self): """ test an usual related field """ # add a related field test_related_company_id on res.partner old_columns = self.partner._columns self.partner._columns = dict(old_columns) self.partner._columns.update({ 'related_company_partner_id': fields.related('company_id', 'partner_id', type='many2one', obj='res.partner'), }) # find a company with a non-null partner_id ids = self.company.search(self.cr, self.uid, [('partner_id', '!=', False)], limit=1) id = ids[0] # find partners that satisfy [('partner_id.company_id', '=', id)] company_ids = self.company.search(self.cr, self.uid, [('partner_id', '=', id)]) partner_ids1 = self.partner.search(self.cr, self.uid, [('company_id', 'in', company_ids)]) partner_ids2 = self.partner.search(self.cr, self.uid, [('related_company_partner_id', '=', id)]) self.assertEqual(partner_ids1, partner_ids2) # restore res.partner fields self.partner._columns = old_columns
def __init__(self, pool, cr): super(AbstractConfigSettings, self).__init__(pool, cr) if self._companyObject: for field_key in self._companyObject._columns: # allows to exclude some field if self._filter_field(field_key): args = ('company_id', field_key) kwargs = { 'string': self._companyObject._columns[field_key].string, 'help': self._companyObject._columns[field_key].help, 'type': self._companyObject._columns[field_key]._type, } if '_obj' in self._companyObject._columns[field_key].__dict__.keys(): kwargs['relation'] = \ self._companyObject._columns[field_key]._obj if '_domain' in \ self._companyObject._columns[field_key].__dict__.keys(): kwargs['domain'] = \ self._companyObject._columns[field_key]._domain field_key = re.sub('^' + self._prefix, '', field_key) self._columns[field_key] = \ fields.related(*args, **kwargs)
def _total_margin(self, cr, uid, ids, name, arg, context=None): res = {} for line in self.browse(cr, uid, ids): res[line.id] = line.product_qty * line.target_margin return res _columns = { #'standard_price': fields.related('product_id','standard_price',type='float',relation='product.product', string='Cost Price', readonly=True), 'standard_price':fields.function( _standard_price, string='Cost Price', type='float', digits=(16,2), ), #Issue 173 'volume_per_pallet': fields.related('product_id','volume_per_pallet',type='integer',relation='product.product', string='Pallet Quantity', readonly=True), 'product_uom': fields.related('product_id', 'uom_id', string='Unit of Measure', type='many2one', relation='product.uom', store=True, readonly=True), 'target_selling': fields.char('Target Selling Price'), #Issue322 'target_margin': fields.char('Target Margin'), #Issue322 'total_margin':fields.function( _total_margin, string='Total Margin', type='float'), #Issue322 } _defaults = { 'product_uom' : _get_uom_id, } def onchange_product_id(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id, partner_id, date_order=False, fiscal_position_id=False, date_planned=False,
class crm_lead(base_stage, format_address, osv.osv): """ CRM Lead Case """ _name = "crm.lead" _description = "Lead/Opportunity" _order = "priority,date_action,id desc" _inherit = ['mail.thread', 'ir.needaction_mixin'] _track = { 'state': { 'crm.mt_lead_create': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'new', 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancel', }, 'stage_id': { 'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['new', 'cancel', 'done'], }, } def create(self, cr, uid, vals, context=None): if context is None: context = {} if not vals.get('stage_id'): ctx = context.copy() if vals.get('section_id'): ctx['default_section_id'] = vals['section_id'] if vals.get('type'): ctx['default_type'] = vals['type'] vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx) return super(crm_lead, self).create(cr, uid, vals, context=context) def _get_default_section_id(self, cr, uid, context=None): """ Gives default section by checking if present in the context """ return self._resolve_section_id_from_context(cr, uid, context=context) or False def _get_default_stage_id(self, cr, uid, context=None): """ Gives default stage_id """ section_id = self._get_default_section_id(cr, uid, context=context) return self.stage_find(cr, uid, [], section_id, [('state', '=', 'draft')], context=context) def _resolve_section_id_from_context(self, cr, uid, context=None): """ Returns ID of section based on the value of 'section_id' context key, or None if it cannot be resolved to a single Sales Team. """ if context is None: context = {} if type(context.get('default_section_id')) in (int, long): return context.get('default_section_id') if isinstance(context.get('default_section_id'), basestring): section_name = context['default_section_id'] section_ids = self.pool.get('crm.case.section').name_search(cr, uid, name=section_name, context=context) if len(section_ids) == 1: return int(section_ids[0][0]) return None def _resolve_type_from_context(self, cr, uid, context=None): """ Returns the type (lead or opportunity) from the type context key. Returns None if it cannot be resolved. """ if context is None: context = {} return context.get('default_type') def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): access_rights_uid = access_rights_uid or uid stage_obj = self.pool.get('crm.case.stage') order = stage_obj._order # lame hack to allow reverting search, should just work in the trivial case if read_group_order == 'stage_id desc': order = "%s desc" % order # retrieve section_id from the context and write the domain # - ('id', 'in', 'ids'): add columns that should be present # - OR ('case_default', '=', True), ('fold', '=', False): add default columns that are not folded # - OR ('section_ids', '=', section_id), ('fold', '=', False) if section_id: add section columns that are not folded search_domain = [] section_id = self._resolve_section_id_from_context(cr, uid, context=context) if section_id: search_domain += ['|', ('section_ids', '=', section_id)] search_domain += [('id', 'in', ids)] else: search_domain += ['|', ('id', 'in', ids), ('case_default', '=', True)] # retrieve type from the context (if set: choose 'type' or 'both') type = self._resolve_type_from_context(cr, uid, context=context) if type: search_domain += ['|', ('type', '=', type), ('type', '=', 'both')] # perform search stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) # restore order of the search result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) fold = {} for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): fold[stage.id] = stage.fold or False return result, fold def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): res = super(crm_lead,self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu) if view_type == 'form': res['arch'] = self.fields_view_get_address(cr, user, res['arch'], context=context) return res _group_by_full = { 'stage_id': _read_group_stage_ids } def _compute_day(self, cr, uid, ids, fields, args, context=None): """ :return dict: difference between current date and log date """ cal_obj = self.pool.get('resource.calendar') res_obj = self.pool.get('resource.resource') res = {} for lead in self.browse(cr, uid, ids, context=context): for field in fields: res[lead.id] = {} duration = 0 ans = False if field == 'day_open': if lead.date_open: date_create = datetime.strptime(lead.create_date, "%Y-%m-%d %H:%M:%S") date_open = datetime.strptime(lead.date_open, "%Y-%m-%d %H:%M:%S") ans = date_open - date_create date_until = lead.date_open elif field == 'day_close': if lead.date_closed: date_create = datetime.strptime(lead.create_date, "%Y-%m-%d %H:%M:%S") date_close = datetime.strptime(lead.date_closed, "%Y-%m-%d %H:%M:%S") date_until = lead.date_closed ans = date_close - date_create if ans: resource_id = False if lead.user_id: resource_ids = res_obj.search(cr, uid, [('user_id','=',lead.user_id.id)]) if len(resource_ids): resource_id = resource_ids[0] duration = float(ans.days) if lead.section_id and lead.section_id.resource_calendar_id: duration = float(ans.days) * 24 new_dates = cal_obj.interval_get(cr, uid, lead.section_id.resource_calendar_id and lead.section_id.resource_calendar_id.id or False, datetime.strptime(lead.create_date, '%Y-%m-%d %H:%M:%S'), duration, resource=resource_id ) no_days = [] date_until = datetime.strptime(date_until, '%Y-%m-%d %H:%M:%S') for in_time, out_time in new_dates: if in_time.date not in no_days: no_days.append(in_time.date) if out_time > date_until: break duration = len(no_days) res[lead.id][field] = abs(int(duration)) return res def _history_search(self, cr, uid, obj, name, args, context=None): res = [] msg_obj = self.pool.get('mail.message') message_ids = msg_obj.search(cr, uid, [('email_from','!=',False), ('subject', args[0][1], args[0][2])], context=context) lead_ids = self.search(cr, uid, [('message_ids', 'in', message_ids)], context=context) if lead_ids: return [('id', 'in', lead_ids)] else: return [('id', '=', '0')] _columns = { 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', track_visibility='onchange', select=True, help="Linked partner (optional). Usually created when converting the lead."), 'id': fields.integer('ID', readonly=True), 'name': fields.char('Subject', size=64, required=True, select=1), 'active': fields.boolean('Active', required=False), 'date_action_last': fields.datetime('Last Action', readonly=1), 'date_action_next': fields.datetime('Next Action', readonly=1), 'email_from': fields.char('Email', size=128, help="Email address of the contact", select=1), 'section_id': fields.many2one('crm.case.section', 'Sales Team', select=True, track_visibility='onchange', help='When sending mails, the default email address is taken from the sales team.'), 'create_date': fields.datetime('Creation Date' , readonly=True), 'email_cc': fields.text('Global CC', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), 'description': fields.text('Notes'), 'write_date': fields.datetime('Update Date' , readonly=True), 'categ_ids': fields.many2many('crm.case.categ', 'crm_lead_category_rel', 'lead_id', 'category_id', 'Categories', \ domain="['|',('section_id','=',section_id),('section_id','=',False), ('object_id.model', '=', 'crm.lead')]"), 'type_id': fields.many2one('crm.case.resource.type', 'Campaign', \ domain="['|',('section_id','=',section_id),('section_id','=',False)]", help="From which campaign (seminar, marketing campaign, mass mailing, ...) did this contact come from?"), 'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel (mail, direct, phone, ...)"), 'contact_name': fields.char('Contact Name', size=64), 'partner_name': fields.char("Customer Name", size=64,help='The name of the future partner company that will be created while converting the lead into opportunity', select=1), 'opt_out': fields.boolean('Opt-Out', oldname='optout', help="If opt-out is checked, this contact has refused to receive emails or unsubscribed to a campaign."), 'type':fields.selection([ ('lead','Lead'), ('opportunity','Opportunity'), ],'Type', help="Type is used to separate Leads and Opportunities"), 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), 'date_closed': fields.datetime('Closed', readonly=True), 'stage_id': fields.many2one('crm.case.stage', 'Stage', track_visibility='onchange', domain="['&', '&', ('fold', '=', False), ('section_ids', '=', section_id), '|', ('type', '=', type), ('type', '=', 'both')]"), 'user_id': fields.many2one('res.users', 'Salesperson', select=True, track_visibility='onchange'), 'referred': fields.char('Referred By', size=64), 'date_open': fields.datetime('Opened', readonly=True), 'day_open': fields.function(_compute_day, string='Days to Open', \ multi='day_open', type="float", store=True), 'day_close': fields.function(_compute_day, string='Days to Close', \ multi='day_close', type="float", store=True), 'state': fields.related('stage_id', 'state', type="selection", store=True, selection=crm.AVAILABLE_STATES, string="Status", readonly=True, help='The Status is set to \'Draft\', when a case is created. If the case is in progress the Status is set to \'Open\'. When the case is over, the Status is set to \'Done\'. If the case needs to be reviewed then the Status is set to \'Pending\'.'), # Only used for type opportunity 'probability': fields.float('Success Rate (%)',group_operator="avg"), 'planned_revenue': fields.float('Expected Revenue', track_visibility='always'), 'ref': fields.reference('Reference', selection=crm._links_get, size=128), 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), 'phone': fields.char("Phone", size=64), 'date_deadline': fields.date('Expected Closing', help="Estimate of the date on which the opportunity will be won."), 'date_action': fields.date('Next Action Date', select=True), 'title_action': fields.char('Next Action', size=64), 'color': fields.integer('Color Index'), 'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True), 'partner_address_email': fields.related('partner_id', 'email', type='char', string='Partner Contact Email', readonly=True), 'company_currency': fields.related('company_id', 'currency_id', type='many2one', string='Currency', readonly=True, relation="res.currency"), 'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True), 'user_login': fields.related('user_id', 'login', type='char', string='User Login', readonly=True), # Fields for address, due to separation from crm and res.partner 'street': fields.char('Street', size=128), 'street2': fields.char('Street2', size=128), 'zip': fields.char('Zip', change_default=True, size=24), 'city': fields.char('City', size=128), 'state_id': fields.many2one("res.country.state", 'State'), 'country_id': fields.many2one('res.country', 'Country'), 'phone': fields.char('Phone', size=64), 'fax': fields.char('Fax', size=64), 'mobile': fields.char('Mobile', size=64), 'function': fields.char('Function', size=128), 'title': fields.many2one('res.partner.title', 'Title'), 'company_id': fields.many2one('res.company', 'Company', select=1), 'payment_mode': fields.many2one('crm.payment.mode', 'Payment Mode', \ domain="[('section_id','=',section_id)]"), 'planned_cost': fields.float('Planned Costs'), } _defaults = { 'active': 1, 'type': 'lead', 'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c), 'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c), 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c), 'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c), 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c), 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], 'color': 0, } _sql_constraints = [ ('check_probability', 'check(probability >= 0 and probability <= 100)', 'The probability of closing the deal should be between 0% and 100%!') ] def onchange_stage_id(self, cr, uid, ids, stage_id, context=None): if not stage_id: return {'value':{}} stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context) if not stage.on_change: return {'value':{}} return {'value':{'probability': stage.probability}} def on_change_partner(self, cr, uid, ids, partner_id, context=None): result = {} values = {} if partner_id: partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) values = { 'partner_name' : partner.name, 'street' : partner.street, 'street2' : partner.street2, 'city' : partner.city, 'state_id' : partner.state_id and partner.state_id.id or False, 'country_id' : partner.country_id and partner.country_id.id or False, 'email_from' : partner.email, 'phone' : partner.phone, 'mobile' : partner.mobile, 'fax' : partner.fax, } return {'value' : values} def _check(self, cr, uid, ids=False, context=None): """ Override of the base.stage method. Function called by the scheduler to process cases for date actions Only works on not done and cancelled cases """ cr.execute('select * from crm_case \ where (date_action_last<%s or date_action_last is null) \ and (date_action_next<=%s or date_action_next is null) \ and state not in (\'cancel\',\'done\')', (time.strftime("%Y-%m-%d %H:%M:%S"), time.strftime('%Y-%m-%d %H:%M:%S'))) ids2 = map(lambda x: x[0], cr.fetchall() or []) cases = self.browse(cr, uid, ids2, context=context) return self._action(cr, uid, cases, False, context=context) def stage_find(self, cr, uid, cases, section_id, domain=None, order='sequence', context=None): """ Override of the base.stage method Parameter of the stage search taken from the lead: - type: stage type must be the same or 'both' - section_id: if set, stages must belong to this section or be a default stage; if not set, stages must be default stages """ if isinstance(cases, (int, long)): cases = self.browse(cr, uid, cases, context=context) # collect all section_ids section_ids = [] types = ['both'] if not cases : type = context.get('default_type') types += [type] if section_id: section_ids.append(section_id) for lead in cases: if lead.section_id: section_ids.append(lead.section_id.id) if lead.type not in types: types.append(lead.type) # OR all section_ids and OR with case_default search_domain = [] if section_ids: search_domain += [('|')] * len(section_ids) for section_id in section_ids: search_domain.append(('section_ids', '=', section_id)) else: search_domain.append(('case_default', '=', True)) # AND with cases types search_domain.append(('type', 'in', types)) # AND with the domain in parameter search_domain += list(domain) # perform search, return the first found stage_ids = self.pool.get('crm.case.stage').search(cr, uid, search_domain, order=order, context=context) if stage_ids: return stage_ids[0] return False def case_cancel(self, cr, uid, ids, context=None): """ Overrides case_cancel from base_stage to set probability """ res = super(crm_lead, self).case_cancel(cr, uid, ids, context=context) self.write(cr, uid, ids, {'probability' : 0.0}, context=context) return res def case_reset(self, cr, uid, ids, context=None): """ Overrides case_reset from base_stage to set probability """ res = super(crm_lead, self).case_reset(cr, uid, ids, context=context) self.write(cr, uid, ids, {'probability': 0.0}, context=context) return res def case_mark_lost(self, cr, uid, ids, context=None): """ Mark the case as lost: state=cancel and probability=0 """ for lead in self.browse(cr, uid, ids): stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 0.0),('on_change','=',True)], context=context) if stage_id: self.case_set(cr, uid, [lead.id], values_to_update={'probability': 0.0}, new_stage_id=stage_id, context=context) return True def case_mark_won(self, cr, uid, ids, context=None): """ Mark the case as won: state=done and probability=100 """ for lead in self.browse(cr, uid, ids): stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 100.0),('on_change','=',True)], context=context) if stage_id: self.case_set(cr, uid, [lead.id], values_to_update={'probability': 100.0}, new_stage_id=stage_id, context=context) return True def set_priority(self, cr, uid, ids, priority): """ Set lead priority """ return self.write(cr, uid, ids, {'priority' : priority}) def set_high_priority(self, cr, uid, ids, context=None): """ Set lead priority to high """ return self.set_priority(cr, uid, ids, '1') def set_normal_priority(self, cr, uid, ids, context=None): """ Set lead priority to normal """ return self.set_priority(cr, uid, ids, '3') def _merge_get_result_type(self, cr, uid, opps, context=None): """ Define the type of the result of the merge. If at least one of the element to merge is an opp, the resulting new element will be an opp. Otherwise it will be a lead. We'll directly use a list of browse records instead of a list of ids for performances' sake: it will spare a second browse of the leads/opps. :param list opps: list of browse records containing the leads/opps to process :return string type: the type of the final element """ for opp in opps: if (opp.type == 'opportunity'): return 'opportunity' return 'lead' def _merge_data(self, cr, uid, ids, oldest, fields, context=None): """ Prepare lead/opp data into a dictionary for merging. Different types of fields are processed in different ways: - text: all the values are concatenated - m2m and o2m: those fields aren't processed - m2o: the first not null value prevails (the other are dropped) - any other type of field: same as m2o :param list ids: list of ids of the leads to process :param list fields: list of leads' fields to process :return dict data: contains the merged values """ opportunities = self.browse(cr, uid, ids, context=context) def _get_first_not_null(attr): for opp in opportunities: if hasattr(opp, attr) and bool(getattr(opp, attr)): return getattr(opp, attr) return False def _get_first_not_null_id(attr): res = _get_first_not_null(attr) return res and res.id or False def _concat_all(attr): return '\n\n'.join(filter(lambda x: x, [getattr(opp, attr) or '' for opp in opportunities if hasattr(opp, attr)])) # Process the fields' values data = {} for field_name in fields: field_info = self._all_columns.get(field_name) if field_info is None: continue field = field_info.column if field._type in ('many2many', 'one2many'): continue elif field._type == 'many2one': data[field_name] = _get_first_not_null_id(field_name) # !! elif field._type == 'text': data[field_name] = _concat_all(field_name) #not lost else: data[field_name] = _get_first_not_null(field_name) #not lost # Define the resulting type ('lead' or 'opportunity') data['type'] = self._merge_get_result_type(cr, uid, opportunities, context) return data def _mail_body(self, cr, uid, lead, fields, title=False, context=None): body = [] if title: body.append("%s\n" % (title)) for field_name in fields: field_info = self._all_columns.get(field_name) if field_info is None: continue field = field_info.column value = '' if field._type == 'selection': if hasattr(field.selection, '__call__'): key = field.selection(self, cr, uid, context=context) else: key = field.selection value = dict(key).get(lead[field_name], lead[field_name]) elif field._type == 'many2one': if lead[field_name]: value = lead[field_name].name_get()[0][1] elif field._type == 'many2many': if lead[field_name]: for val in lead[field_name]: field_value = val.name_get()[0][1] value += field_value + "," else: value = lead[field_name] body.append("%s: %s" % (field.string, value or '')) return "<br/>".join(body + ['<br/>']) def _merge_notify(self, cr, uid, opportunity_id, opportunities, context=None): """ Create a message gathering merged leads/opps information. """ #TOFIX: mail template should be used instead of fix body, subject text details = [] result_type = self._merge_get_result_type(cr, uid, opportunities, context) if result_type == 'lead': merge_message = _('Merged leads') else: merge_message = _('Merged opportunities') subject = [merge_message] for opportunity in opportunities: subject.append(opportunity.name) title = "%s : %s" % (opportunity.type == 'opportunity' and _('Merged opportunity') or _('Merged lead'), opportunity.name) fields = list(CRM_LEAD_FIELDS_TO_MERGE) details.append(self._mail_body(cr, uid, opportunity, fields, title=title, context=context)) # Chatter message's subject subject = subject[0] + ": " + ", ".join(subject[1:]) details = "\n\n".join(details) return self.message_post(cr, uid, [opportunity_id], body=details, subject=subject, context=context) def _merge_opportunity_history(self, cr, uid, opportunity_id, opportunities, context=None): message = self.pool.get('mail.message') for opportunity in opportunities: for history in opportunity.message_ids: message.write(cr, uid, history.id, { 'res_id': opportunity_id, 'subject' : _("From %s : %s") % (opportunity.name, history.subject) }, context=context) return True def _merge_opportunity_attachments(self, cr, uid, opportunity_id, opportunities, context=None): attachment = self.pool.get('ir.attachment') # return attachments of opportunity def _get_attachments(opportunity_id): attachment_ids = attachment.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', opportunity_id)], context=context) return attachment.browse(cr, uid, attachment_ids, context=context) count = 1 first_attachments = _get_attachments(opportunity_id) for opportunity in opportunities: attachments = _get_attachments(opportunity.id) for first in first_attachments: for attachment in attachments: if attachment.name == first.name: values = dict( name = "%s (%s)" % (attachment.name, count,), res_id = opportunity_id, ) attachment.write(values) count+=1 return True def merge_opportunity(self, cr, uid, ids, context=None): """ Different cases of merge: - merge leads together = 1 new lead - merge at least 1 opp with anything else (lead or opp) = 1 new opp :param list ids: leads/opportunities ids to merge :return int id: id of the resulting lead/opp """ if context is None: context = {} if len(ids) <= 1: raise osv.except_osv(_('Warning!'), _('Please select more than one element (lead or opportunity) from the list view.')) opportunities = self.browse(cr, uid, ids, context=context) sequenced_opps = [] for opportunity in opportunities: sequenced_opps.append((opportunity.stage_id and opportunity.stage_id.state != 'cancel' and opportunity.stage_id.sequence or 0, opportunity)) sequenced_opps.sort(key=lambda tup: tup[0], reverse=True) opportunities = [opportunity for sequence, opportunity in sequenced_opps] ids = [opportunity.id for opportunity in opportunities] highest = opportunities[0] opportunities_rest = opportunities[1:] tail_opportunities = opportunities_rest fields = list(CRM_LEAD_FIELDS_TO_MERGE) merged_data = self._merge_data(cr, uid, ids, highest, fields, context=context) # Merge messages and attachements into the first opportunity self._merge_opportunity_history(cr, uid, highest.id, tail_opportunities, context=context) self._merge_opportunity_attachments(cr, uid, highest.id, tail_opportunities, context=context) # Merge notifications about loss of information opportunities = [highest] opportunities.extend(opportunities_rest) self._merge_notify(cr, uid, highest, opportunities, context=context) # Check if the stage is in the stages of the sales team. If not, assign the stage with the lowest sequence if merged_data.get('type') == 'opportunity' and merged_data.get('section_id'): section_stages = self.pool.get('crm.case.section').read(cr, uid, merged_data['section_id'], ['stage_ids'], context=context) if merged_data.get('stage_id') not in section_stages['stage_ids']: stages_sequences = self.pool.get('crm.case.stage').search(cr, uid, [('id','in',section_stages['stage_ids'])], order='sequence', limit=1, context=context) merged_data['stage_id'] = stages_sequences[0] # Write merged data into first opportunity self.write(cr, uid, [highest.id], merged_data, context=context) # Delete tail opportunities self.unlink(cr, uid, [x.id for x in tail_opportunities], context=context) return highest.id def _convert_opportunity_data(self, cr, uid, lead, customer, section_id=False, context=None): crm_stage = self.pool.get('crm.case.stage') contact_id = False if customer: contact_id = self.pool.get('res.partner').address_get(cr, uid, [customer.id])['default'] if not section_id: section_id = lead.section_id and lead.section_id.id or False val = { 'planned_revenue': lead.planned_revenue, 'probability': lead.probability, 'name': lead.name, 'partner_id': customer and customer.id or False, 'user_id': (lead.user_id and lead.user_id.id), 'type': 'opportunity', 'date_action': fields.datetime.now(), 'date_open': fields.datetime.now(), 'email_from': customer and customer.email or lead.email_from, 'phone': customer and customer.phone or lead.phone, } if not lead.stage_id or lead.stage_id.type=='lead': val['stage_id'] = self.stage_find(cr, uid, [lead], section_id, [('state', '=', 'draft'),('type', 'in', ('opportunity','both'))], context=context) return val def convert_opportunity(self, cr, uid, ids, partner_id, user_ids=False, section_id=False, context=None): customer = False if partner_id: partner = self.pool.get('res.partner') customer = partner.browse(cr, uid, partner_id, context=context) for lead in self.browse(cr, uid, ids, context=context): if lead.state in ('done', 'cancel'): continue vals = self._convert_opportunity_data(cr, uid, lead, customer, section_id, context=context) self.write(cr, uid, [lead.id], vals, context=context) self.message_post(cr, uid, ids, body=_("Lead <b>converted into an Opportunity</b>"), subtype="crm.mt_lead_convert_to_opportunity", context=context) if user_ids or section_id: self.allocate_salesman(cr, uid, ids, user_ids, section_id, context=context) return True def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): partner = self.pool.get('res.partner') vals = {'name': name, 'user_id': lead.user_id.id, 'comment': lead.description, 'section_id': lead.section_id.id or False, 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, 'email': lead.email_from and tools.email_split(lead.email_from)[0], 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, 'street': lead.street, 'street2': lead.street2, 'zip': lead.zip, 'city': lead.city, 'country_id': lead.country_id and lead.country_id.id or False, 'state_id': lead.state_id and lead.state_id.id or False, 'is_company': is_company, 'type': 'contact' } partner = partner.create(cr, uid, vals, context=context) return partner def _create_lead_partner(self, cr, uid, lead, context=None): partner_id = False if lead.partner_name and lead.contact_name: partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context) elif lead.partner_name and not lead.contact_name: partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) elif not lead.partner_name and lead.contact_name: partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, context=context) elif lead.email_from and self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)[0]: contact_name = self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)[0] partner_id = self._lead_create_contact(cr, uid, lead, contact_name, False, context=context) else: raise osv.except_osv( _('Warning!'), _('No customer name defined. Please fill one of the following fields: Company Name, Contact Name or Email ("Name <email@address>")') ) return partner_id def _lead_set_partner(self, cr, uid, lead, partner_id, context=None): """ Assign a partner to a lead. :param object lead: browse record of the lead to process :param int partner_id: identifier of the partner to assign :return bool: True if the partner has properly been assigned """ res = False res_partner = self.pool.get('res.partner') if partner_id: res_partner.write(cr, uid, partner_id, {'section_id': lead.section_id and lead.section_id.id or False}) contact_id = res_partner.address_get(cr, uid, [partner_id])['default'] res = lead.write({'partner_id': partner_id}, context=context) message = _("<b>Partner</b> set to <em>%s</em>." % (lead.partner_id.name)) self.message_post(cr, uid, [lead.id], body=message, context=context) return res def handle_partner_assignation(self, cr, uid, ids, action='create', partner_id=False, context=None): """ Handle partner assignation during a lead conversion. if action is 'create', create new partner with contact and assign lead to new partner_id. otherwise assign lead to the specified partner_id :param list ids: leads/opportunities ids to process :param string action: what has to be done regarding partners (create it, assign an existing one, or nothing) :param int partner_id: partner to assign if any :return dict: dictionary organized as followed: {lead_id: partner_assigned_id} """ #TODO this is a duplication of the handle_partner_assignation method of crm_phonecall partner_ids = {} # If a partner_id is given, force this partner for all elements force_partner_id = partner_id for lead in self.browse(cr, uid, ids, context=context): # If the action is set to 'create' and no partner_id is set, create a new one if action == 'create': partner_id = force_partner_id or self._create_lead_partner(cr, uid, lead, context) self._lead_set_partner(cr, uid, lead, partner_id, context=context) partner_ids[lead.id] = partner_id return partner_ids def allocate_salesman(self, cr, uid, ids, user_ids=None, team_id=False, context=None): """ Assign salesmen and salesteam to a batch of leads. If there are more leads than salesmen, these salesmen will be assigned in round-robin. E.g.: 4 salesmen (S1, S2, S3, S4) for 6 leads (L1, L2, ... L6). They will be assigned as followed: L1 - S1, L2 - S2, L3 - S3, L4 - S4, L5 - S1, L6 - S2. :param list ids: leads/opportunities ids to process :param list user_ids: salesmen to assign :param int team_id: salesteam to assign :return bool """ index = 0 for lead_id in ids: value = {} if team_id: value['section_id'] = team_id if user_ids: value['user_id'] = user_ids[index] # Cycle through user_ids index = (index + 1) % len(user_ids) if value: self.write(cr, uid, [lead_id], value, context=context) return True def schedule_phonecall(self, cr, uid, ids, schedule_time, call_summary, desc, phone, contact_name, user_id=False, section_id=False, categ_id=False, action='schedule', context=None): """ :param string action: ('schedule','Schedule a call'), ('log','Log a call') """ phonecall = self.pool.get('crm.phonecall') model_data = self.pool.get('ir.model.data') phonecall_dict = {} if not categ_id: res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2') if res_id: categ_id = model_data.browse(cr, uid, res_id, context=context).res_id for lead in self.browse(cr, uid, ids, context=context): if not section_id: section_id = lead.section_id and lead.section_id.id or False if not user_id: user_id = lead.user_id and lead.user_id.id or False vals = { 'name': call_summary, 'opportunity_id': lead.id, 'user_id': user_id or False, 'categ_id': categ_id or False, 'description': desc or '', 'date': schedule_time, 'section_id': section_id or False, 'partner_id': lead.partner_id and lead.partner_id.id or False, 'partner_phone': phone or lead.phone or (lead.partner_id and lead.partner_id.phone or False), 'partner_mobile': lead.partner_id and lead.partner_id.mobile or False, 'priority': lead.priority, } new_id = phonecall.create(cr, uid, vals, context=context) phonecall.case_open(cr, uid, [new_id], context=context) if action == 'log': phonecall.case_close(cr, uid, [new_id], context=context) phonecall_dict[lead.id] = new_id self.schedule_phonecall_send_note(cr, uid, [lead.id], new_id, action, context=context) return phonecall_dict def redirect_opportunity_view(self, cr, uid, opportunity_id, context=None): models_data = self.pool.get('ir.model.data') # Get opportunity views dummy, form_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_form_view_oppor') dummy, tree_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_tree_view_oppor') return { 'name': _('Opportunity'), 'view_type': 'form', 'view_mode': 'tree, form', 'res_model': 'crm.lead', 'domain': [('type', '=', 'opportunity')], 'res_id': int(opportunity_id), 'view_id': False, 'views': [(form_view or False, 'form'), (tree_view or False, 'tree'), (False, 'calendar'), (False, 'graph')], 'type': 'ir.actions.act_window', } def redirect_lead_view(self, cr, uid, lead_id, context=None): models_data = self.pool.get('ir.model.data') # Get lead views dummy, form_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_form_view_leads') dummy, tree_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_tree_view_leads') return { 'name': _('Lead'), 'view_type': 'form', 'view_mode': 'tree, form', 'res_model': 'crm.lead', 'domain': [('type', '=', 'lead')], 'res_id': int(lead_id), 'view_id': False, 'views': [(form_view or False, 'form'), (tree_view or False, 'tree'), (False, 'calendar'), (False, 'graph')], 'type': 'ir.actions.act_window', } def action_makeMeeting(self, cr, uid, ids, context=None): """ Open meeting's calendar view to schedule meeting on current opportunity. :return dict: dictionary value for created Meeting view """ opportunity = self.browse(cr, uid, ids[0], context) res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'base_calendar', 'action_crm_meeting', context) res['context'] = { 'default_opportunity_id': opportunity.id, 'default_partner_id': opportunity.partner_id and opportunity.partner_id.id or False, 'default_partner_ids' : opportunity.partner_id and [opportunity.partner_id.id] or False, 'default_user_id': uid, 'default_section_id': opportunity.section_id and opportunity.section_id.id or False, 'default_email_from': opportunity.email_from, 'default_name': opportunity.name, } return res def write(self, cr, uid, ids, vals, context=None): if vals.get('stage_id') and not vals.get('probability'): # change probability of lead(s) if required by stage stage = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context) if stage.on_change: vals['probability'] = stage.probability return super(crm_lead, self).write(cr, uid, ids, vals, context=context) def new_mail_send(self, cr, uid, ids, context=None): ''' This function opens a window to compose an email, with the edi sale template message loaded by default ''' assert len(ids) == 1, 'This option should only be used for a single id at a time.' ir_model_data = self.pool.get('ir.model.data') try: template_id = ir_model_data.get_object_reference(cr, uid, 'crm', 'email_template_opportunity_mail')[1] except ValueError: template_id = False try: compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1] except ValueError: compose_form_id = False if context is None: context = {} ctx = context.copy() ctx.update({ 'default_model': 'crm.lead', 'default_res_id': ids[0], 'default_use_template': bool(template_id), 'default_template_id': template_id, 'default_composition_mode': 'comment', }) return { 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'form', 'res_model': 'mail.compose.message', 'views': [(compose_form_id, 'form')], 'view_id': compose_form_id, 'target': 'new', 'context': ctx, } # ---------------------------------------- # Mail Gateway # ---------------------------------------- def message_get_reply_to(self, cr, uid, ids, context=None): """ Override to get the reply_to of the parent project. """ return [lead.section_id.message_get_reply_to()[0] if lead.section_id else False for lead in self.browse(cr, uid, ids, context=context)] def message_new(self, cr, uid, msg, custom_values=None, context=None): """ Overrides mail_thread message_new that is called by the mailgateway through message_process. This override updates the document according to the email. """ if custom_values is None: custom_values = {} desc = html2plaintext(msg.get('body')) if msg.get('body') else '' defaults = { 'name': msg.get('subject') or _("No Subject"), 'description': desc, 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), 'partner_id': msg.get('author_id', False), 'user_id': False, } if msg.get('author_id'): defaults.update(self.on_change_partner(cr, uid, None, msg.get('author_id'), context=context)['value']) if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): defaults['priority'] = msg.get('priority') defaults.update(custom_values) return super(crm_lead, self).message_new(cr, uid, msg, custom_values=defaults, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): """ Overrides mail_thread message_update that is called by the mailgateway through message_process. This method updates the document according to the email. """ if isinstance(ids, (str, int, long)): ids = [ids] if update_vals is None: update_vals = {} if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): update_vals['priority'] = msg.get('priority') maps = { 'cost':'planned_cost', 'revenue': 'planned_revenue', 'probability':'probability', } for line in msg.get('body', '').split('\n'): line = line.strip() res = tools.command_re.match(line) if res and maps.get(res.group(1).lower()): key = maps.get(res.group(1).lower()) update_vals[key] = res.group(2).lower() return super(crm_lead, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) # ---------------------------------------- # OpenChatter methods and notifications # ---------------------------------------- def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None): phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0] if action == 'log': prefix = 'Logged' else: prefix = 'Scheduled' message = _("<b>%s a call</b> for the <em>%s</em>.") % (prefix, phonecall.date) return self.message_post(cr, uid, ids, body=message, context=context) def onchange_state(self, cr, uid, ids, state_id, context=None): if state_id: country_id=self.pool.get('res.country.state').browse(cr, uid, state_id, context).country_id.id return {'value':{'country_id':country_id}} return {}
class base_action_rule(osv.osv): """ Base Action Rules """ _name = 'base.action.rule' _description = 'Action Rules' _order = 'sequence' _columns = { 'name': fields.char('Rule Name', required=True), 'model_id': fields.many2one('ir.model', 'Related Document Model', required=True, domain=[('transient', '=', False)]), 'model': fields.related('model_id', 'model', type="char", string='Model'), 'create_date': fields.datetime('Create Date', readonly=1), 'active': fields.boolean('Active', help="When unchecked, the rule is hidden and will not be executed."), 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of rules."), 'kind': fields.selection( [('on_create', 'On Creation'), ('on_write', 'On Update'), ('on_create_or_write', 'On Creation & Update'), ('on_unlink', 'On Deletion'), ('on_change', 'Based on Form Modification'), ('on_time', 'Based on Timed Condition')], string='When to Run'), 'trg_date_id': fields.many2one('ir.model.fields', string='Trigger Date', help="When should the condition be triggered. If present, will be checked by the scheduler. If empty, will be checked at creation and update.", domain="[('model_id', '=', model_id), ('ttype', 'in', ('date', 'datetime'))]"), 'trg_date_range': fields.integer('Delay after trigger date', help="Delay after the trigger date." \ "You can put a negative number if you need a delay before the" \ "trigger date, like sending a reminder 15 minutes before a meeting."), 'trg_date_range_type': fields.selection([('minutes', 'Minutes'), ('hour', 'Hours'), ('day', 'Days'), ('month', 'Months')], 'Delay type'), 'trg_date_calendar_id': fields.many2one( 'resource.calendar', 'Use Calendar', help='When calculating a day-based timed condition, it is possible to use a calendar to compute the date based on working days.', ondelete='set null', ), 'act_user_id': fields.many2one('res.users', 'Set Responsible'), 'act_followers': fields.many2many("res.partner", string="Add Followers"), 'server_action_ids': fields.many2many('ir.actions.server', string='Server Actions', domain="[('model_id', '=', model_id)]", help="Examples: email reminders, call object service, etc."), 'filter_pre_id': fields.many2one( 'ir.filters', string='Before Update Filter', ondelete='restrict', domain="[('model_id', '=', model_id.model)]", help="If present, this condition must be satisfied before the update of the record."), 'filter_pre_domain': fields.char(string='Before Update Domain', help="If present, this condition must be satisfied before the update of the record."), 'filter_id': fields.many2one( 'ir.filters', string='Filter', ondelete='restrict', domain="[('model_id', '=', model_id.model)]", help="If present, this condition must be satisfied before executing the action rule."), 'filter_domain': fields.char(string='Domain', help="If present, this condition must be satisfied before executing the action rule."), 'last_run': fields.datetime('Last Run', readonly=1, copy=False), 'on_change_fields': fields.char(string="On Change Fields Trigger", help="Comma-separated list of field names that triggers the onchange."), } # which fields have an impact on the registry CRITICAL_FIELDS = ['model_id', 'active', 'kind', 'on_change_fields'] _defaults = { 'active': True, 'trg_date_range_type': 'day', } def onchange_kind(self, cr, uid, ids, kind, context=None): clear_fields = [] if kind in ['on_create', 'on_create_or_write', 'on_unlink']: clear_fields = [ 'filter_pre_id', 'filter_pre_domain', 'trg_date_id', 'trg_date_range', 'trg_date_range_type' ] elif kind in ['on_write', 'on_create_or_write']: clear_fields = [ 'trg_date_id', 'trg_date_range', 'trg_date_range_type' ] elif kind == 'on_time': clear_fields = ['filter_pre_id', 'filter_pre_domain'] return {'value': dict.fromkeys(clear_fields, False)} def onchange_filter_pre_id(self, cr, uid, ids, filter_pre_id, context=None): ir_filter = self.pool['ir.filters'].browse(cr, uid, filter_pre_id, context=context) return {'value': {'filter_pre_domain': ir_filter.domain}} def onchange_filter_id(self, cr, uid, ids, filter_id, context=None): ir_filter = self.pool['ir.filters'].browse(cr, uid, filter_id, context=context) return {'value': {'filter_domain': ir_filter.domain}} @openerp.api.model def _get_actions(self, records, kinds): """ Return the actions of the given kinds for records' model. The returned actions' context contain an object to manage processing. """ if '__action_done' not in self._context: self = self.with_context(__action_done={}) domain = [('model', '=', records._name), ('kind', 'in', kinds)] actions = self.with_context(active_test=True).search(domain) return actions.with_env(self.env) @openerp.api.model def _get_eval_context(self): """ Prepare the context used when evaluating python code :returns: dict -- evaluation context given to (safe_)eval """ return { 'datetime': DT, 'dateutil': dateutil, 'time': time, 'uid': self.env.uid, 'user': self.env.user, } @openerp.api.model def _filter_pre(self, records): """ Filter the records that satisfy the precondition of action ``self``. """ if self.filter_pre_id and records: eval_context = self._get_eval_context() domain = [('id', 'in', records.ids)] + eval( self.filter_pre_id.domain, eval_context) ctx = eval(self.filter_pre_id.context) return records.with_context(**ctx).search(domain).with_env( records.env) elif self.filter_pre_domain and records: eval_context = self._get_eval_context() domain = [('id', 'in', records.ids)] + eval( self.filter_pre_domain, eval_context) return records.search(domain) else: return records @openerp.api.model def _filter_post(self, records): """ Filter the records that satisfy the postcondition of action ``self``. """ if self.filter_id and records: eval_context = self._get_eval_context() domain = [('id', 'in', records.ids)] + eval( self.filter_id.domain, eval_context) ctx = eval(self.filter_id.context) return records.with_context(**ctx).search(domain).with_env( records.env) elif self.filter_domain and records: eval_context = self._get_eval_context() domain = [('id', 'in', records.ids)] + eval( self.filter_domain, eval_context) return records.search(domain) else: return records @openerp.api.multi def _process(self, records): """ Process action ``self`` on the ``records`` that have not been done yet. """ # filter out the records on which self has already been done, then mark # remaining records as done (to avoid recursive processing) action_done = self._context['__action_done'] records -= action_done.setdefault(self, records.browse()) if not records: return action_done[self] |= records # modify records values = {} if 'date_action_last' in records._fields: values['date_action_last'] = openerp.fields.Datetime.now() if self.act_user_id and 'user_id' in records._fields: values['user_id'] = self.act_user_id.id if values: records.write(values) # subscribe followers if self.act_followers and hasattr(records, 'message_subscribe'): records.message_subscribe(self.act_followers.ids) # execute server actions if self.server_action_ids: for record in records: ctx = { 'active_model': record._name, 'active_ids': record.ids, 'active_id': record.id } self.server_action_ids.with_context(**ctx).run() def _register_hook(self, cr): """ Patch models that should trigger action rules based on creation, modification, deletion of records and form onchanges. """ # # Note: the patched methods must be defined inside another function, # otherwise their closure may be wrong. For instance, the function # create refers to the outer variable 'create', which you expect to be # bound to create itself. But that expectation is wrong if create is # defined inside a loop; in that case, the variable 'create' is bound to # the last function defined by the loop. # def make_create(): """ Instanciate a create method that processes action rules. """ @openerp.api.model def create(self, vals, **kw): # retrieve the action rules to possibly execute actions = self.env['base.action.rule']._get_actions( self, ['on_create', 'on_create_or_write']) # call original method record = create.origin(self.with_env(actions.env), vals, **kw) # check postconditions, and execute actions on the records that satisfy them for action in actions.with_context(old_values=None): action._process(action._filter_post(record)) return record.with_env(self.env) return create def make_write(): """ Instanciate a _write method that processes action rules. """ # # Note: we patch method _write() instead of write() in order to # catch updates made by field recomputations. # @openerp.api.multi def _write(self, vals, **kw): # retrieve the action rules to possibly execute actions = self.env['base.action.rule']._get_actions( self, ['on_write', 'on_create_or_write']) records = self.with_env(actions.env) # check preconditions on records pre = { action: action._filter_pre(records) for action in actions } # read old values before the update old_values = { old_vals.pop('id'): old_vals for old_vals in records.read(list(vals)) } # call original method _write.origin(records, vals, **kw) # check postconditions, and execute actions on the records that satisfy them for action in actions.with_context(old_values=old_values): action._process(action._filter_post(pre[action])) return True return _write def make_unlink(): """ Instanciate an unlink method that processes action rules. """ @openerp.api.multi def unlink(self, **kwargs): # retrieve the action rules to possibly execute actions = self.env['base.action.rule']._get_actions( self, ['on_unlink']) records = self.with_env(actions.env) # check conditions, and execute actions on the records that satisfy them for action in actions: action._process(action._filter_post(pre[action])) # call original method return unlink.origin(self, **kwargs) return unlink def make_onchange(action_rule_id): """ Instanciate an onchange method for the given action rule. """ def base_action_rule_onchange(self): action_rule = self.env['base.action.rule'].browse( action_rule_id) server_actions = action_rule.server_action_ids.with_context( active_model=self._name, onchange_self=self) result = {} for server_action in server_actions: res = server_action.run() if res and 'value' in res: res['value'].pop('id', None) self.update( self._convert_to_cache(res['value'], validate=False)) if res and 'domain' in res: result.setdefault('domain', {}).update(res['domain']) if res and 'warning' in res: result['warning'] = res['warning'] return result return base_action_rule_onchange patched_models = defaultdict(set) def patch(model, name, method): """ Patch method `name` on `model`, unless it has been patched already. """ if model not in patched_models[name]: patched_models[name].add(model) model._patch_method(name, method) # retrieve all actions, and patch their corresponding model ids = self.search(cr, SUPERUSER_ID, []) for action_rule in self.browse(cr, SUPERUSER_ID, ids): model = action_rule.model_id.model model_obj = self.pool.get(model) if not model_obj: continue if action_rule.kind == 'on_create': patch(model_obj, 'create', make_create()) elif action_rule.kind == 'on_create_or_write': patch(model_obj, 'create', make_create()) patch(model_obj, '_write', make_write()) elif action_rule.kind == 'on_write': patch(model_obj, '_write', make_write()) elif action_rule.kind == 'on_unlink': patch(model_obj, 'unlink', make_unlink()) elif action_rule.kind == 'on_change': # register an onchange method for the action_rule method = make_onchange(action_rule.id) for field_name in action_rule.on_change_fields.split(","): field_name = field_name.strip() model_obj._onchange_methods[field_name].append(method) def _update_cron(self, cr, uid, context=None): """ Activate the cron job depending on whether there exists action rules based on time conditions. """ try: cron = self.pool['ir.model.data'].get_object(cr, uid, 'base_action_rule', 'ir_cron_crm_action', context=context) except ValueError: return False return cron.toggle(model=self._name, domain=[('kind', '=', 'on_time')]) def _update_registry(self, cr, uid, context=None): """ Update the registry after a modification on action rules. """ if self.pool.ready: # for the sake of simplicity, simply force the registry to reload cr.commit() openerp.api.Environment.reset() RegistryManager.new(cr.dbname) RegistryManager.signal_registry_change(cr.dbname) def create(self, cr, uid, vals, context=None): res_id = super(base_action_rule, self).create(cr, uid, vals, context=context) self._update_cron(cr, uid, context=context) self._update_registry(cr, uid, context=context) return res_id def write(self, cr, uid, ids, vals, context=None): super(base_action_rule, self).write(cr, uid, ids, vals, context=context) if set(vals) & set(self.CRITICAL_FIELDS): self._update_cron(cr, uid, context=context) self._update_registry(cr, uid, context=context) return True def unlink(self, cr, uid, ids, context=None): res = super(base_action_rule, self).unlink(cr, uid, ids, context=context) self._update_cron(cr, uid, context=context) self._update_registry(cr, uid, context=context) return res def onchange_model_id(self, cr, uid, ids, model_id, context=None): data = {'model': False, 'filter_pre_id': False, 'filter_id': False} if model_id: model = self.pool.get('ir.model').browse(cr, uid, model_id, context=context) data.update({'model': model.model}) return {'value': data} def _check_delay(self, cr, uid, action, record, record_dt, context=None): if action.trg_date_calendar_id and action.trg_date_range_type == 'day': start_dt = get_datetime(record_dt) action_dt = self.pool['resource.calendar'].schedule_days_get_date( cr, uid, action.trg_date_calendar_id.id, action.trg_date_range, day_date=start_dt, compute_leaves=True, context=context) else: delay = DATE_RANGE_FUNCTION[action.trg_date_range_type]( action.trg_date_range) action_dt = get_datetime(record_dt) + delay return action_dt def _check(self, cr, uid, automatic=False, use_new_cursor=False, context=None): """ This Function is called by scheduler. """ context = context or {} if '__action_done' not in context: context = dict(context, __action_done={}) # retrieve all the action rules to run based on a timed condition action_dom = [('kind', '=', 'on_time')] action_ids = self.search(cr, uid, action_dom, context=dict(context, active_test=True)) eval_context = self._get_eval_context(cr, uid, context=context) for action in self.browse(cr, uid, action_ids, context=context): now = datetime.now() if action.last_run: last_run = get_datetime(action.last_run) else: last_run = datetime.utcfromtimestamp(0) # retrieve all the records that satisfy the action's condition model = self.pool[action.model_id.model] domain = [] ctx = dict(context) if action.filter_domain is not False: domain = eval(action.filter_domain, eval_context) elif action.filter_id: domain = eval(action.filter_id.domain, eval_context) ctx.update(eval(action.filter_id.context)) if 'lang' not in ctx: # Filters might be language-sensitive, attempt to reuse creator lang # as we are usually running this as super-user in background [filter_meta] = action.filter_id.get_metadata() user_id = filter_meta['write_uid'] and filter_meta['write_uid'][0] or \ filter_meta['create_uid'][0] ctx['lang'] = self.pool['res.users'].browse( cr, uid, user_id).lang record_ids = model.search(cr, uid, domain, context=ctx) # determine when action should occur for the records date_field = action.trg_date_id.name if date_field == 'date_action_last' and 'create_date' in model._fields: get_record_dt = lambda record: record[date_field ] or record.create_date else: get_record_dt = lambda record: record[date_field] # process action on the records that should be executed for record in model.browse(cr, uid, record_ids, context=context): record_dt = get_record_dt(record) if not record_dt: continue action_dt = self._check_delay(cr, uid, action, record, record_dt, context=context) if last_run <= action_dt < now: try: action._process(record) except Exception: import traceback _logger.error(traceback.format_exc()) action.write( {'last_run': now.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) if automatic: # auto-commit for batch processing cr.commit()
except Exception, e: stack = traceback.format_exc() self.pool.get('checklist.exception').create(cr, uid, {'checklist_task_id': instance.checklist_task_id.id, 'exception_type': 'field', 'res_id': instance.res_id, 'field_id': field.id, 'exception': e, 'stack': stack}) continue return res def _get_checklist_task_instance_ids(self, cr, uid, ids, context={}): if isinstance(ids, (int, long)): ids = [ids] return self.pool.get('checklist.task.instance').search(cr, uid, [('checklist_task_id', 'in', ids)]) _columns = { 'checklist_task_id': fields.many2one('checklist.task', 'Checklist Task', required=True, ondelete='cascade'), 'checklist_id': fields.related('checklist_task_id', 'checklist_id', type='many2one', relation='checklist', string='Checklist'), 'model_id': fields.related('checklist_id', 'model_id', type='many2one', relation='ir.model', string='Model'), 'name': fields.related('checklist_task_id', 'name', type='char', size=128, string='Name'), 'mandatory': fields.related('checklist_task_id', 'mandatory', type='boolean', string='Mandatory', help='Required to make active object'), 'res_id': fields.integer('Resource ID', select=True, required=True), 'active': fields.function(_get_activity, type='boolean', string='Active', store=False, multi='activity'), 'sequence': fields.function(_get_activity, type='integer', string='Priority', store={ 'checklist.task': (_get_checklist_task_instance_ids, ['sequence'], 10), }, multi='activity'), 'field_ids_to_fill': fields.function(_get_activity, type='one2many', relation='checklist.task.field', string='Fields to fill', store=False, multi='activity'), 'field_ids_filled': fields.function(_get_activity, type='one2many', relation='checklist.task.field', string='Filled fields', store=False, multi='activity'), 'progress_rate': fields.float('Progress Rate', digits=(16, 2)), }
class hon_issue(orm.Model): _name = "hon.issue" def _invoiced_rate(self, cursor, user, ids, name, arg, context=None): res = {} for issue in self.browse(cursor, user, ids, context=context): if issue.invoiced: res[issue.id] = 100.0 continue tot = 0.0 for invoice in issue.invoice_ids: if invoice.state not in ('draft', 'cancel'): tot += invoice.amount_untaxed if tot: res[issue.id] = min(100.0, tot * 100.0 / (issue.amount_untaxed or 1.00)) else: res[issue.id] = 0.0 return res def _invoice_exists(self, cursor, user, ids, name, arg, context=None): res = {} for issue in self.browse(cursor, user, ids, context=context): res[issue.id] = False if issue.invoice_ids: res[issue.id] = True return res def _invoiced(self, cursor, user, ids, name, arg, context=None): res = {} for issue in self.browse(cursor, user, ids, context=context): res[issue.id] = True for line in issue.hon_issue_line: if not line.invoice_line_id and not line.employee and not line.gratis: res[issue.id] = False return res def _invoiced_search(self, cursor, user, obj, name, args, context=None): if not len(args): return [] clause = '' issue_clause = '' no_invoiced = False for arg in args: if (arg[1] == '=' and arg[2]) or (arg[1] == '!=' and not arg[2]): clause += 'AND inv.state = \'paid\'' else: clause += 'AND inv.state != \'cancel\' AND issue.state != \'cancel\' AND inv.state <> \'paid\' AND rel.issue_id = issue.id ' issue_clause = ', hon_issue AS issue ' no_invoiced = True cursor.execute('SELECT rel.issue_id ' \ 'FROM hon_issue_invoice_rel AS rel, account_invoice AS inv '+ issue_clause + \ 'WHERE rel.invoice_id = inv.id ' + clause) res = cursor.fetchall() if no_invoiced: cursor.execute('SELECT issue.id ' \ 'FROM hon_issue AS issue ' \ 'WHERE issue.id NOT IN ' \ '(SELECT rel.issue_id ' \ 'FROM hon_issue_invoice_rel AS rel) and issue.state != \'cancel\'') res.extend(cursor.fetchall()) if not res: return [('id', '=', 0)] return [('id', 'in', [x[0] for x in res])] _columns = { # 'account_analytic_id': fields.many2one('account.analytic.account', 'Title/Issue', # required=True, readonly=True, states = {'draft': [('readonly', False)]}, # domain=[('type','!=','view'), ('portal_sub', '=', True), ('parent_id.is_hon', '=', True)]), 'account_analytic_id': fields.many2one('account.analytic.account', 'Title/Issue', required=True, readonly=True, states = {'draft': [('readonly', False)]}, domain=[('portal_sub', '=', True)]), 'date_publish': fields.related('account_analytic_id','date_publish',type='date', relation='account.analytic.account',string='Publishing Date', store=True, readonly=True), 'name': fields.related('account_analytic_id', 'name', type='char', relation='account.analytic.account', string='Name', store=True, readonly=True), 'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}), 'hon_issue_line': fields.one2many('hon.issue.line', 'issue_id', 'Hon Lines', readonly=False, states={'draft':[('readonly',False)]}), 'state': fields.selection([ ('cancel', 'Cancelled'), ('draft','Draft'), ('open','Open'), ('done', 'Done'), ],'Status', select=True, readonly=True, help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Honorarium Issue. \ \n* The \'Open\' status is used when user create invoice.\ \n* The \'Cancelled\' status is used when user cancel Honorarium Issue.'), 'comment': fields.text('Additional Information'), 'invoice_ids': fields.many2many('account.invoice', 'hon_issue_invoice_rel', 'issue_id', 'invoice_id', 'Invoices', readonly=True, help="This is the list of invoices that have been generated for this issue. " "The same issue may have been invoiced several times (by line for example)."), 'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced Ratio', type='float'), 'invoiced': fields.function(_invoiced, string='Invoiced', fnct_search=_invoiced_search, type='boolean', help="It indicates that all issue lines have been invoiced."), 'invoice_exists': fields.function(_invoice_exists, string='Invoiced', fnct_search=_invoiced_search, type='boolean', help="It indicates that hon issue has at least one invoice."), } _defaults = { 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'hon.issue', context=c), 'state': 'draft', 'account_analytic_id': False, } _sql_constraints = [ ('account_analytic_company_uniq', 'unique (account_analytic_id, company_id)', 'The Issue must be unique per company !'), ] def onchange_analytic_ac(self, cr, uid, ids, analytic, context={}): res = {} if not ids and not analytic : return res war = {} analytic_account = self.pool['account.analytic.account'].browse(cr, uid, analytic, context) res['name'] = analytic_account.name res['date_publish'] = analytic_account.date_publish llist = [] if ids: iss_obj = self.browse(cr,uid,ids) if iss_obj[0].hon_issue_line: for line in iss_obj[0].hon_issue_line: if line.activity_id: llist.append((1, line.id, {'activity_id': [],})) res['hon_issue_line'] = llist war['title'] = 'Let op!' war['message'] = 'U heeft de Titel/Nummer aangepast. Nu moet u opnieuw Redacties selecteren in de HONregel(s)' return {'value': res, 'warning': war} def manual_invoice(self, cr, uid, ids, context=None): ''' create invoices for the given hon issues (ids), and open the form view of one of the newly created invoices ''' mod_obj = self.pool.get('ir.model.data') inv_ids0 = set(inv.id for issue in self.browse(cr, uid, ids, context) for inv in issue.invoice_ids) self.action_invoice_create(cr, uid, ids, date_invoice=False, context=None) inv_ids1 = set(inv.id for issue in self.browse(cr, uid, ids, context) for inv in issue.invoice_ids) # determine newly created invoices new_inv_ids = list(inv_ids1 - inv_ids0) res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_supplier_form') res_id = res and res[1] or False, return { 'name': _('Supplier Invoices'), 'view_type': 'form', 'view_mode': 'form', 'view_id': [res_id], 'res_model': 'account.invoice', 'context': "{'type':'in_invoice'}", 'type': 'ir.actions.act_window', 'nodestroy': True, 'target': 'current', 'res_id': new_inv_ids and new_inv_ids[0] or False, } def action_view_invoice(self, cr, uid, ids, context=None): ''' This function returns an action that display existing invoices of given hon issue ids. It can either be a in a list or in a form view, if there is only one invoice to show. ''' mod_obj = self.pool.get('ir.model.data') act_obj = self.pool.get('ir.actions.act_window') result = mod_obj.get_object_reference(cr, uid, 'account', 'action_invoice_tree2') id = result and result[1] or False result = act_obj.read(cr, uid, [id], context=context)[0] #compute the number of invoices to display inv_ids = [] for so in self.browse(cr, uid, ids, context=context): inv_ids += [invoice.id for invoice in so.invoice_ids] #choose the view_mode accordingly if len(inv_ids)>1: result['domain'] = "[('id','in',["+','.join(map(str, inv_ids))+"])]" else: res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_supplier_form') result['views'] = [(res and res[1] or False, 'form')] result['res_id'] = inv_ids and inv_ids[0] or False return result def action_invoice_create(self, cr, uid, ids, date_invoice=False, context=None): if context is None: context = {} # If date was specified, use it as date invoiced, useful when invoices are generated this month and put the # last day of the last month as invoice date if date_invoice: context['date_invoice'] = date_invoice if ids: context['active_ids'] = ids himi = self.pool['hon.issue.make.invoice'] himi.make_invoices_from_issues(cr, uid, ids, context=context) return True def action_button_confirm(self, cr, uid, ids, context=None): assert len(ids) == 1, 'This option should only be used for a single id at a time.' wf_service = netsvc.LocalService('workflow') wf_service.trg_validate(uid, 'hon.issue', ids[0], 'issue_confirm', cr) return True def unlink(self, cr, uid, ids, context=None): issues = self.read(cr, uid, ids, ['state'], context=context) unlink_ids = [] for s in issues: if s['state'] in ['draft', 'cancel']: unlink_ids.append(s['id']) else: raise osv.except_osv(_('Invalid Action!'), _('In order to delete a confirmed issue, you must cancel it before!')) return osv.osv.unlink(self, cr, uid, unlink_ids, context=context) def action_wait(self, cr, uid, ids, context=None): context = context or {} for o in self.browse(cr, uid, ids): if not o.hon_issue_line: raise osv.except_osv(_('Error!'),_('You cannot confirm a hon issue which has no line.')) self.write(cr, uid, [o.id], {'state': 'open', }) self.pool.get('hon.issue.line').button_confirm(cr, uid, [x.id for x in o.hon_issue_line]) return True def action_unwait(self, cr, uid, ids, context=None): context = context or {} for o in self.browse(cr, uid, ids): if not o.hon_issue_line: raise osv.except_osv(_('Error!'),_('You cannot unconfirm a hon issue which has no line.')) self.write(cr, uid, [o.id], {'state': 'draft', }) self.pool.get('hon.issue.line').button_unconfirm(cr, uid, [x.id for x in o.hon_issue_line]) return True def action_cancel(self, cr, uid, ids, context=None): wf_service = netsvc.LocalService("workflow") if context is None: context = {} hon_issue_line_obj = self.pool.get('hon.issue.line') for issue in self.browse(cr, uid, ids, context=context): for inv in issue.invoice_ids: if inv.state not in ('draft', 'cancel'): raise osv.except_osv( _('Cannot cancel this issue!'), _('First cancel all invoices attached to this issue.')) for r in self.read(cr, uid, ids, ['invoice_ids']): for inv in r['invoice_ids']: wf_service.trg_validate(uid, 'account.invoice', inv, 'invoice_cancel', cr) cr.execute('delete from hon_issue_invoice_rel where issue_id=%s and invoice_id=%s', (issue.id, inv)) hon_issue_line_obj.write(cr, uid, [l.id for l in issue.hon_issue_line], {'state': 'cancel','invoice_line_id': False}) self.write(cr, uid, ids, {'state': 'cancel'}) return True # go from canceled state to draft state def action_cancel_draft(self, cr, uid, ids, *args): self.write(cr, uid, ids, {'state':'draft'}) wf_service = netsvc.LocalService("workflow") for issue_id in ids: wf_service.trg_delete(uid, 'hon.issue', issue_id, cr) wf_service.trg_create(uid, 'hon.issue', issue_id, cr) return True def action_done(self, cr, uid, ids, context=None): data_hon = self.browse(cr, uid, ids, context=context) a = [] for issue in data_hon: if issue.invoiced: a.append(issue.id) else: raise osv.except_osv( _('Cannot finalise this issue!'), _('First invoice all hon_lines attached to this issue.')) return self.write(cr, uid, a, {'state': 'done'}, context=context)
#but WITHOUT ANY WARRANTY; without even the implied warranty of # #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # #GNU Affero General Public License for more details. # # # #You should have received a copy of the GNU Affero General Public License # #along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################### from openerp.osv import orm, fields FISCAL_POSITION_COLUMNS = { 'name': fields.char('Fiscal Position', size=128, required=True), 'fiscal_category_id': fields.many2one( 'l10n_br_account.fiscal.category', 'Categoria Fiscal'), 'fiscal_category_fiscal_type': fields.related( 'fiscal_category_id', 'fiscal_type', type='char', readonly=True, relation='l10n_br_account.fiscal.category', store=True, string='Fiscal Type'), 'type': fields.selection([('input', 'Entrada'), ('output', 'Saida')], 'Tipo'), 'type_tax_use': fields.selection( [('sale', 'Sale'), ('purchase', 'Purchase'), ('all', 'All')], 'Tax Application'), 'inv_copy_note': fields.boolean(u'Copiar Observação na Nota Fiscal'), 'asset_operation': fields.boolean(u'Operação de Aquisição de Ativo', help=u"""Caso seja marcada essa opção, será incluido o IPI na base de calculo do ICMS."""), 'state': fields.selection([('draft', u'Rascunho'), ('review', u'Revisão'), ('approved', u'Aprovada'), ('unapproved', u'Não Aprovada')], 'Status', readonly=True, track_visibility='onchange', select=True), }
'ship_message': fields.text('Message'), 'address_validate': fields.selection([ ('validate', 'Validate'), ('nonvalidate', 'No Validation') ], 'Address Validation', help=''' No Validation = No address validation. Validate = Fail on failed address validation. Defaults to validate. Note: Full address validation is not performed. Therefore, it is the responsibility of the Shipping Tool User to ensure the address entered is correct to avoid an address correction fee.'''), 'ship_description': fields.text('Description'), 'ship_from': fields.boolean('Ship From', help='Required if pickup location is different from the shipper\'s address..'), 'ship_from_tax_id_no': fields.char('Identification Number', size=30 , select=1), 'shipcharge': fields.float('Shipping Cost', readonly=True), 'ship_from_address': fields.many2one('res.partner', 'Ship From Address', size=30), # 'address': fields.many2one('res.partner', 'Ship From Address'), 'tot_order_weight': fields.related('sale_id', 'total_weight_net', type='float', relation='sale.order', string='Total Order Weight'), 'comm_inv': fields.boolean('Commercial Invoice'), 'cer_orig': fields.boolean('U.S. Certificate of Origin'), 'nafta_cer_orig': fields.boolean('NAFTA Certificate of Origin'), 'sed': fields.boolean('Shipper Export Declaration (SED)'), 'prod_option': fields.selection([ ('01', 'AVAILABLE TO CUSTOMS UPON REQUEST'), ('02', 'SAME AS EXPORTER'), ('03', 'ATTACHED LIST'), ('04', 'UNKNOWN'), (' ', ' ') ], 'Option'), 'prod_company': fields.char('CompanyName', size=256, help='Only applicable when producer option is empty or not present.'), 'prod_tax_id_no': fields.char('TaxIdentificationNumber', size=256, help='Only applicable when producer option is empty or not present.'), 'prod_address_id': fields.many2one('res.partner', 'Producer Address', help='Only applicable when producer option is empty or not present.'), 'inv_option': fields.selection([
class stock_move(osv.osv): _inherit = "stock.move" def _get_rec_info(self, cr, uid, ids, fields, args, context=None): result = {} for id in ids: result[id] = {'return_qty':0,} for m in self.browse(cr,uid,ids,context=context): return_qty = 0 if m.state == 'done': for rec in m.move_history_ids2: # only take into account 'product return' moves, ignoring any other # kind of upstream moves, such as internal procurements, etc. # a valid return move will be the exact opposite of ours: # (src location, dest location) <=> (dest location, src location)) if rec.state != 'cancel' \ and rec.location_dest_id.id == m.location_id.id \ and rec.location_id.id == m.location_dest_id.id: return_qty += self.pool.get('product.uom')._compute_qty(cr, uid, m.product_uom.id, rec.product_qty, rec.product_uom.id) #calculate the product base uom quantity product_uom_base_qty = m.product_qty if m.product_uom.id != m.product_id.uom_id.id: product_uom_base_qty = self.pool.get('product.uom')._compute_qty_obj(cr, uid, m.product_uom, m.product_qty, m.product_id.uom_id) result[m.id].update({'return_qty':return_qty,'product_uom_base_qty':product_uom_base_qty}) return result _columns = { 'type': fields.related('picking_id', 'type', type='selection', selection=[('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('mr', 'Material Request'), ('mrr', 'Material Return')], string='Shipping Type'), 'create_uid': fields.many2one('res.users', 'Creator',readonly=True), 'supplier_prod_name': fields.related('purchase_line_id', 'supplier_prod_name',string='Supplier Product Name',type="char",readonly=True,store=True), 'return_qty': fields.function(_get_rec_info, type='float', string='Return Quantity', multi="rec_info", digits_compute=dp.get_precision('Product Price')), #make the price's decimal precision as the 'Product Price' 'price_unit': fields.float('Unit Price', digits_compute= dp.get_precision('Product Price'), help="Technical field used to record the product cost set by the user during a picking confirmation (when average price costing method is used)"), 'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Price'), required=True,states={'done': [('readonly', True)]}, help="This is the quantity of products from an inventory " "point of view. For moves in the state 'done', this is the " "quantity of products that were actually moved. For other " "moves, this is the quantity of product that is planned to " "be moved. Lowering this quantity does not generate a " "backorder. Changing this quantity on assigned moves affects " "the product reservation, and should be done with care." ), 'product_uom_base': fields.related('product_id','uom_id',type='many2one',relation='product.uom', string='Base UOM',readonly=True), 'product_uom_base_qty': fields.function(_get_rec_info, type='float', string='Base Quantity', multi="rec_info", digits_compute=dp.get_precision('Product Unit of Measure'),readonly=True), 'mfg_ids': fields.related('purchase_line_id','mfg_ids',type='many2many',relation='sale.product',string="MFG IDs"), } def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): #deal the 'date' datetime field query new_args = deal_args(self,args) return super(stock_move,self).search(cr, user, new_args, offset, limit, order, context, count) def action_done(self, cr, uid, ids, context=None): resu = super(stock_move,self).action_done(cr, uid, ids, context) move_ids = [] for move in self.browse(cr, uid, ids, context=context): if move.state == 'done': move_ids.append(move.id) self.write(cr, uid, move_ids, {'date': datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}, context=context) return resu def _create_account_move_line(self, cr, uid, move, matches, src_account_id, dest_account_id, reference_amount, reference_currency_id, type='', context=None): val = super(stock_move,self)._create_account_move_line(cr, uid, move, matches, src_account_id, dest_account_id, reference_amount, reference_currency_id, type, context) #check if this move is a material request line move mr_line = self.pool.get('material.request.line').browse(cr, uid, move.id, context=context) if mr_line.mr_sale_prod_id: #set the analytic_account_id to debit_line, the detail data ref _create_account_move_line() in product_fifo_lifo.stock.py if mr_line.mr_sale_prod_id.analytic_account_id and val and val[0]: if mr_line.pick_type == 'mr': #debit dict data mline_data = val[0][2] else: #credit dict data mline_data = val[1][2] mline_data.update({'analytic_account_id':mr_line.mr_sale_prod_id.analytic_account_id.id}) return val
class address_address(orm.Model): _name = 'address.address' _inherit = ['mozaik.abstract.model'] _description = 'Address' # private methods def _get_technical_name(self, cr, uid, values, context=None): """ This method produces a technical name with the content of values. :type values: dictionary :param values: used to create a technical address name ``country_id`` ``address_local_zip`` ``zip_man`` ``town_man`` ``address_local_street_id`` ``street_man`` ``number`` ``box`` :rparam: formated values of ``values`` join wit a `#`. 0 if value is null """ technical_value = [] for field in values.keys(): value = values[field] or u'0' technical_value.append(format_value(value)) return '#'.join(technical_value) def _get_linked_coordinates(self, cr, uid, ids, context=None): return self.pool['postal.coordinate'].search( cr, uid, [('address_id', 'in', ids)], context=context) def _get_integral_address(self, cr, uid, ids, name, args, context=None): result = { i: {key: False for key in ['name', 'technical_name']} for i in ids } adrs_recs = self.browse(cr, uid, ids, context=context) for adrs in adrs_recs: elts = [ adrs.street or False, adrs.sequence and '[%s]' % adrs.sequence or False, adrs.street and '-' or adrs.sequence and '-' or False, (adrs.country_code == 'BE') and adrs.zip or False, adrs.city or False, (adrs.country_code != 'BE') and '-' or False, (adrs.country_code != 'BE') and adrs.country_id.name or False, ] adr = ' '.join([el for el in elts if el]) values = KEY_FIELDS.copy() for field in KEY_FIELDS.keys(): to_evaluate = field if not KEY_FIELDS[field] else '%s.%s' % ( field, KEY_FIELDS[field]) real_value = eval('adrs.%s' % to_evaluate) values[field] = real_value technical_name = self._get_technical_name(cr, uid, values, context=context) result[adrs.id] = { 'name': adr or False, 'technical_name': technical_name or False, } return result def _get_street(self, cr, uid, ids, name, args, context=None): result = {i: False for i in ids} adrs_recs = self.browse(cr, uid, ids, context=context) for adrs in adrs_recs: number = adrs.number or '-' number = adrs.box and '%s/%s' % (number, adrs.box) or \ adrs.number or False if adrs.address_local_street_id: street = adrs.select_alternative_address_local_street and \ adrs.address_local_street_id.local_street_alternative or \ adrs.address_local_street_id.local_street else: street = adrs.street_man or False result[adrs.id] = ' '.join([el for el in [street, number] if el]) return result def _get_zip(self, cr, uid, ids, name, args, context=None): result = {i: {key: False for key in [ 'zip', 'city', ]} for i in ids} adrs_recs = self.browse(cr, uid, ids, context=context) for adrs in adrs_recs: result[adrs.id] = { 'zip': adrs.address_local_zip_id and adrs.address_local_zip_id.local_zip or adrs.zip_man or False, 'city': adrs.address_local_zip_id and adrs.address_local_zip_id.town or adrs.town_man or False, } return result _address_store_triggers = { # this MUST be executed in last for consistency: sequence is greater # than other 'address.address': (lambda self, cr, uid, ids, context=None: ids, TRIGGER_FIELDS, 20), 'address.local.zip': (lambda self, cr, uid, ids, context=None: self.pool[ 'address.local.zip']._get_linked_addresses( cr, uid, ids, context=context).ids, ['local_zip', 'town'], 15), 'address.local.street': (lambda self, cr, uid, ids, context=None: self.pool[ 'address.local.street']._get_linked_addresses( cr, uid, ids, context=context), ['local_street', 'local_street_alternative'], 15), 'res.country': (lambda self, cr, uid, ids, context=None: self.pool['res.country']. _get_linked_addresses(cr, uid, ids, context=context), ['name'], 15), } _zip_store_triggers = { 'address.address': (lambda self, cr, uid, ids, context=None: ids, ['address_local_zip_id', 'zip_man', 'town_man'], 10), 'address.local.zip': (lambda self, cr, uid, ids, context=None: self.pool[ 'address.local.zip']._get_linked_addresses( cr, uid, ids, context=context).ids, ['local_zip', 'town'], 10), } _street_store_triggers = { 'address.address': (lambda self, cr, uid, ids, context=None: ids, [ 'address_local_street_id', 'select_alternative_address_local_street', 'street_man', 'number', 'box' ], 10), 'address.local.street': (lambda self, cr, uid, ids, context=None: self.pool[ 'address.local.street']._get_linked_addresses( cr, uid, ids, context=context), ['local_street', 'local_street_alternative'], 10), } _columns = { 'id': fields.integer('ID', readonly=True), 'name': fields.function(_get_integral_address, string='Address', type='char', select=True, multi='display_and_technical', store=_address_store_triggers), 'technical_name': fields.function(_get_integral_address, string='Technical Name', type='char', select=True, multi='display_and_technical', store=_address_store_triggers), 'country_id': fields.many2one('res.country', 'Country', required=True, select=True, track_visibility='onchange'), 'country_code': fields.related('country_id', 'code', string='Country Code', type='char'), 'zip': fields.function(_get_zip, string='Zip', type='char', multi='ZipAndCity', store=_zip_store_triggers), 'address_local_zip_id': fields.many2one('address.local.zip', string='City', track_visibility='onchange'), 'zip_man': fields.char(string='Zip', track_visibility='onchange'), 'city': fields.function(_get_zip, string='City', type='char', multi='ZipAndCity', store=_zip_store_triggers), 'town_man': fields.char(string='Town', track_visibility='onchange'), 'street': fields.function(_get_street, string='Street', type='char', store=_street_store_triggers), 'address_local_street_id': fields.many2one('address.local.street', string='Reference Street', track_visibility='onchange'), 'select_alternative_address_local_street': fields.boolean('Use Alternative Reference Street', track_visibility='onchange'), 'street_man': fields.char(string='Street', track_visibility='onchange'), 'street2': fields.char(string='Street2', track_visibility='onchange'), 'number': fields.char(string='Number', track_visibility='onchange'), 'box': fields.char(string='Box', track_visibility='onchange'), 'sequence': fields.integer('Sequence', track_visibility='onchange', group_operator='min'), 'postal_coordinate_ids': fields.one2many('postal.coordinate', 'address_id', string='Postal Coordinates', domain=[('active', '=', True)], context={'force_recompute': True}), 'postal_coordinate_inactive_ids': fields.one2many('postal.coordinate', 'address_id', string='Postal Coordinates', domain=[('active', '=', False)]), } _defaults = { 'country_id': lambda self, cr, uid, c: self.pool.get('res.country'). _country_default_get(cr, uid, COUNTRY_CODE, context=c), 'country_code': COUNTRY_CODE, 'sequence': 0, } _order = 'country_id, zip, name' # constraints _unicity_keys = 'technical_name, sequence' # orm methods def copy_data(self, cr, uid, ids, default=None, context=None): """ Increase sequence value when duplicating address """ adr_id = isinstance(ids, (long, int)) and [ids] or ids technical_name = self.read(cr, uid, adr_id[0], ['technical_name'], context=context)['technical_name'] cr.execute( 'SELECT MAX(sequence) FROM %s WHERE technical_name=%%s' % (self._table, ), (technical_name, )) sequence = cr.fetchone() sequence = sequence and sequence[0] or False if not sequence: raise orm.except_orm( _('Error'), _('An Address without sequence number cannot be duplicated!')) default = dict(default or {}) default.update({ 'sequence': sequence + 1, 'postal_coordinate_ids': [], 'postal_coordinate_inactive_ids': [], }) res = super(address_address, self).copy_data(cr, uid, ids, default=default, context=context) return res # view methods: onchange, button def onchange_country_id(self, cr, uid, ids, country_id, context=None): return { 'value': { 'country_code': self.pool.get('res.country').read( cr, uid, [country_id], ['code'], context=context)[0]['code'] if country_id else False, 'address_local_zip_id': False, } } def onchange_local_zip_id(self, cr, uid, ids, local_zip_id, context=None): _zip, city = False, False if local_zip_id: zip_city = self.pool.get('address.local.zip').read( cr, uid, [local_zip_id], [], context=context)[0] _zip, city = zip_city['local_zip'], zip_city['town'] return { 'value': { 'zip': _zip, 'city': city, 'zip_man': False, 'town_man': False, } } def onchange_zip(self, cr, uid, ids, _zip, context=None): return { 'value': { 'address_local_street_id': False, } } def onchange_local_street_id(self, cr, uid, ids, local_street_id, context=None): vals = {} if local_street_id else { 'select_alternative_address_local_street': False } vals.update({'street_man': False}) return {'value': vals} # public methods def get_linked_partners(self, cr, uid, ids, context=None): """ Return all partners ids linked to addresses ids :param: ids :type: list of addresses ids :rparam: partner_ids :rtype: list of ids """ coord_ids = self._get_linked_coordinates(cr, uid, ids, context=context) return self.pool['postal.coordinate'].get_linked_partners( cr, uid, coord_ids, context=context)
class wiz_modificacion_regularizacion_siif(osv.osv_memory): _name = 'wiz.modificacion_regularizacion_siif' _description = "Wizard modificacion de regularizacion clearing SIIF" _columns = { 'regularizacion_id': fields.integer('regularizacion id', invisible=False), 'tipo': fields.selection( (('A', 'A - Aumento'), ('R', u'R - Reducción')), # ('C', u'C - Corrección'), # ('N', u'N - Anulación'), # ('D', u'D - Devolución')), 'Tipo', required=True), 'fecha': fields.date('Fecha', required=True), 'importe': fields.integer('Importe', required=True), 'motivo': fields.char('Motivo', required=True), 'financiamiento': fields.related('fin_id', 'ff', type='char', string='Fin related', store=True, readonly=True), 'programa': fields.related('programa_id', 'programa', type='char', string='Programa related', store=True, readonly=True), 'proyecto': fields.related('proyecto_id', 'proyecto', type='char', string='Proyecto related', store=True, readonly=True), 'objeto_gasto': fields.related('odg_id', 'odg', type='char', string='ODG related', store=True, readonly=True), 'auxiliar': fields.related('auxiliar_id', 'aux', type='char', string='Auxiliar related', store=True, readonly=True), 'moneda': fields.related('mon_id', 'moneda', type='char', string='Mon related', store=True, readonly=True), 'tipo_credito': fields.related('tc_id', 'tc', type='char', string='TC related', store=True, readonly=True), 'ue_id': fields.many2one('grp.estruc_pres.ue', 'Unidad ejecutora'), 'fin_id': fields.many2one('grp.estruc_pres.ff', 'Fin', required=True), 'programa_id': fields.many2one('grp.estruc_pres.programa', 'Programa', required=True), 'proyecto_id': fields.many2one('grp.estruc_pres.proyecto', 'Proyecto', required=True), 'odg_id': fields.many2one('grp.estruc_pres.odg', 'ODG', required=True), 'auxiliar_id': fields.many2one('grp.estruc_pres.aux', 'Auxiliar', required=True), 'mon_id': fields.many2one('grp.estruc_pres.moneda', 'Mon', required=True), 'tc_id': fields.many2one('grp.estruc_pres.tc', 'TC', required=True), } _defaults = { 'fecha': fields.date.context_today, } #Consumir SIIF aca def send_modif(self, cr, uid, ids, context=None): data = self.read(cr, uid, ids, [], context=context)[0] regularizacion_obj = self.pool.get("regularizacion.clearing") ctx = dict(context) ctx.update({ 'es_modif': True, 'regularizacion_id': data['regularizacion_id'], 'tipo_modificacion': data['tipo'], 'fecha': data['fecha'], 'programa': data['programa'], 'proyecto': data['proyecto'], 'moneda': data['moneda'], 'tipo_credito': data['tipo_credito'], 'financiamiento': data['financiamiento'], 'objeto_gasto': data['objeto_gasto'], 'auxiliar': data['auxiliar'], 'importe': data['importe'] if data['tipo'] == 'A' else data['importe'] * -1, 'motivo': data['motivo'], }) raise Warning(_('Integración con SIIF'))
class material_request_line(osv.osv): _name = "material.request.line" _inherit = "stock.move" _table = "stock_move" _description = "Material Request Line" def _amount_line(self, cr, uid, ids, prop, arg, context=None): res = {} for line in self.browse(cr, uid, ids, context=context): res[line.id] = line.product_qty * line.price_unit return res _columns = { 'picking_id': fields.many2one('material.request', 'MR#', select=True,states={'done': [('readonly', True)]}), 'mr_emp_id': fields.many2one('hr.employee','Employee'), 'mr_sale_prod_id': fields.many2one('sale.product','Sale Product ID', ondelete="restrict"), 'mr_notes': fields.text('Reason and use'), 'mr_dept_id': fields.related('picking_id','mr_dept_id',string='Department',type='many2one',relation='hr.department',select=True), 'mr_date_order': fields.related('picking_id','date',string='Order Date',type='datetime'), 'pick_type': fields.related('picking_id','type',string='Picking Type',type='char'), 'create_uid': fields.many2one('res.users', 'Creator',readonly=True), 'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Account')), #make the price's decimal precision as the 'Product Price' 'price_unit': fields.float('Unit Price', digits_compute= dp.get_precision('Product Price'), help="Technical field used to record the product cost set by the user during a picking confirmation (when average price costing method is used)"), 'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Price'), required=True,states={'done': [('readonly', True)]}, help="This is the quantity of products from an inventory " "point of view. For moves in the state 'done', this is the " "quantity of products that were actually moved. For other " "moves, this is the quantity of product that is planned to " "be moved. Lowering this quantity does not generate a " "backorder. Changing this quantity on assigned moves affects " "the product reservation, and should be done with care." ), 'prod_categ_id': fields.related('product_id','categ_id',string='Product Category Type',type='many2one',relation="product.category",select=True), } _order = 'id' def default_mr_loc(self, cr, uid, context=None): if context is None: context = {} #material_request.type: mr or mrr req_type = context.get('req_type') if not req_type: req_type = 'mr' loc_stock_id = None loc_prod_id = None #get the default stock location cr.execute('select c.id \n'+ 'from res_users a \n'+ 'left join stock_warehouse b on a.company_id = b.company_id \n'+ 'left join stock_location c on b.lot_stock_id = c.id \n' 'where a.id = %s', (uid,)) loc_src = cr.fetchone() if loc_src: loc_stock_id = loc_src[0] #get the default production location loc_obj = self.pool.get('stock.location') prod_loc_ids = loc_obj.search(cr,uid,[('usage','=','production')],context=context) if prod_loc_ids and prod_loc_ids[0]: prod_loc = loc_obj.browse(cr,uid,prod_loc_ids[0],context=context) loc_prod_id = prod_loc.id #set the locations by the request type loc_from_id = 0 loc_to_id = 0 if req_type == 'mr': if loc_stock_id: loc_from_id = loc_stock_id if loc_prod_id: loc_to_id = loc_prod_id if req_type == 'mrr': if loc_prod_id: loc_from_id = loc_prod_id if loc_stock_id: loc_to_id = loc_stock_id return loc_from_id, loc_to_id def default_get(self, cr, uid, fields_list, context=None): resu = super(material_request_line,self).default_get(cr, uid, fields_list, context) loc_from_id, loc_to_id = self.default_mr_loc(cr, uid, context=context) resu.update({'location_id':loc_from_id, 'location_dest_id':loc_to_id}) return resu def onchange_product_id(self, cr, uid, ids, prod_id=False, loc_id=False, loc_dest_id=False, ): """ On change of product id, if finds UoM, UoS, quantity and UoS quantity. @param prod_id: Changed Product id @param loc_id: Source location id @param loc_dest_id: Destination location id @param partner_id: Address id of partner @return: Dictionary of values """ if not prod_id: return {} user = self.pool.get("res.users").browse(cr,uid,uid) ctx = {'lang': user.lang,'location':loc_id} product = self.pool.get('product.product').browse(cr, uid, [prod_id], context=ctx)[0] uos_id = product.uos_id and product.uos_id.id or False result = { 'product_uom': product.uom_id.id, 'product_uos': uos_id, 'product_qty': product.qty_available, 'product_uos_qty' : self.pool.get('stock.move').onchange_quantity(cr, uid, ids, prod_id, 1.00, product.uom_id.id, uos_id)['value']['product_uos_qty'], 'prodlot_id' : False, } if not ids: result['name'] = product.partner_ref if loc_id: result['location_id'] = loc_id if loc_dest_id: result['location_dest_id'] = loc_dest_id #update the price_unit the and price_currency_id #default is the product's cost price price_unit = product.standard_price price_currency_id = None #get the final purchase price move_obj = self.pool.get('stock.move') #get the final purchase price move_ids = move_obj.search(cr,uid,[('product_id','=',prod_id),('state','=','done'),('type','=','in')],limit=1,order='create_date desc') if move_ids: move_price = move_obj.read(cr,uid,move_ids[0],['price_unit','price_currency_id'],context=ctx) price_unit = move_price['price_unit'] price_currency_id = move_price['price_currency_id'] result['price_unit'] = price_unit result['price_currency_id'] = price_currency_id return {'value': result} def check_access_rights(self, cr, uid, operation, raise_exception=True): #override in order to redirect the check of acces rights on the stock.picking object return self.pool.get('stock.move').check_access_rights(cr, uid, operation, raise_exception=raise_exception) def check_access_rule(self, cr, uid, ids, operation, context=None): #override in order to redirect the check of acces rules on the stock.picking object return self.pool.get('stock.move').check_access_rule(cr, uid, ids, operation, context=context) def _workflow_trigger(self, cr, uid, ids, trigger, context=None): #override in order to trigger the workflow of stock.picking at the end of create, write and unlink operation #instead of it's own workflow (which is not existing) return self.pool.get('stock.move')._workflow_trigger(cr, uid, ids, trigger, context=context) def _workflow_signal(self, cr, uid, ids, signal, context=None): #override in order to fire the workflow signal on given stock.picking workflow instance #instead of it's own workflow (which is not existing) return self.pool.get('stock.move')._workflow_signal(cr, uid, ids, signal, context=context) def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): #deal the 'date' datetime field query new_args = deal_args(self,args) return super(material_request_line,self).search(cr, user, new_args, offset, limit, order, context, count) def create(self, cr, user, vals, context=None): #add the procut_uom set by product's purchase uom if 'product_uom' not in vals: prod = self.pool.get('product.product').browse(cr, user, vals['product_id'], context=context) vals.update({'product_uom':prod.uom_id.id}) resu = super(material_request_line,self).create(cr, user, vals, context=context) return resu
class marketing_campaign_workitem(osv.osv): _name = "marketing.campaign.workitem" _description = "Campaign Workitem" def _res_name_get(self, cr, uid, ids, field_name, arg, context=None): res = dict.fromkeys(ids, '/') for wi in self.browse(cr, uid, ids, context=context): if not wi.res_id: continue proxy = self.pool[wi.object_id.model] if not proxy.exists(cr, uid, [wi.res_id]): continue ng = proxy.name_get(cr, uid, [wi.res_id], context=context) if ng: res[wi.id] = ng[0][1] return res def _resource_search(self, cr, uid, obj, name, args, domain=None, context=None): """Returns id of workitem whose resource_name matches with the given name""" if not len(args): return [] condition_name = None for domain_item in args: # we only use the first domain criterion and ignore all the rest including operators if isinstance(domain_item, (list, tuple)) and len( domain_item) == 3 and domain_item[0] == 'res_name': condition_name = [None, domain_item[1], domain_item[2]] break assert condition_name, "Invalid search domain for marketing_campaign_workitem.res_name. It should use 'res_name'" cr.execute("""select w.id, w.res_id, m.model \ from marketing_campaign_workitem w \ left join marketing_campaign_activity a on (a.id=w.activity_id)\ left join marketing_campaign c on (c.id=a.campaign_id)\ left join ir_model m on (m.id=c.object_id) """) res = cr.fetchall() workitem_map = {} matching_workitems = [] for id, res_id, model in res: workitem_map.setdefault(model, {}).setdefault(res_id, set()).add(id) for model, id_map in workitem_map.iteritems(): model_pool = self.pool[model] condition_name[0] = model_pool._rec_name condition = [('id', 'in', id_map.keys()), condition_name] for res_id in model_pool.search(cr, uid, condition, context=context): matching_workitems.extend(id_map[res_id]) return [('id', 'in', list(set(matching_workitems)))] _columns = { 'segment_id': fields.many2one('marketing.campaign.segment', 'Segment', readonly=True), 'activity_id': fields.many2one('marketing.campaign.activity', 'Activity', required=True, readonly=True), 'campaign_id': fields.related('activity_id', 'campaign_id', type='many2one', relation='marketing.campaign', string='Campaign', readonly=True, store=True), 'object_id': fields.related('activity_id', 'campaign_id', 'object_id', type='many2one', relation='ir.model', string='Resource', select=1, readonly=True, store=True), 'res_id': fields.integer('Resource ID', select=1, readonly=True), 'res_name': fields.function(_res_name_get, string='Resource Name', fnct_search=_resource_search, type="char", size=64), 'date': fields.datetime( 'Execution Date', help='If date is not set, this workitem has to be run manually', readonly=True), 'partner_id': fields.many2one('res.partner', 'Partner', select=1, readonly=True), 'state': fields.selection([ ('todo', 'To Do'), ('cancelled', 'Cancelled'), ('exception', 'Exception'), ('done', 'Done'), ], 'Status', readonly=True, copy=False), 'error_msg': fields.text('Error Message', readonly=True) } _defaults = { 'state': lambda *a: 'todo', 'date': False, } @api.cr_uid_ids_context def button_draft(self, cr, uid, workitem_ids, context=None): for wi in self.browse(cr, uid, workitem_ids, context=context): if wi.state in ('exception', 'cancelled'): self.write(cr, uid, [wi.id], {'state': 'todo'}, context=context) return True @api.cr_uid_ids_context def button_cancel(self, cr, uid, workitem_ids, context=None): for wi in self.browse(cr, uid, workitem_ids, context=context): if wi.state in ('todo', 'exception'): self.write(cr, uid, [wi.id], {'state': 'cancelled'}, context=context) return True def _process_one(self, cr, uid, workitem, context=None): if workitem.state != 'todo': return False activity = workitem.activity_id proxy = self.pool[workitem.object_id.model] object_id = proxy.browse(cr, uid, workitem.res_id, context=context) eval_context = { 'activity': activity, 'workitem': workitem, 'object': object_id, 'resource': object_id, 'transitions': activity.to_ids, 're': re, } try: condition = activity.condition campaign_mode = workitem.campaign_id.mode if condition: if not eval(condition, eval_context): if activity.keep_if_condition_not_met: workitem.write({'state': 'cancelled'}) else: workitem.unlink() return result = True if campaign_mode in ('manual', 'active'): Activities = self.pool.get('marketing.campaign.activity') result = Activities.process(cr, uid, activity.id, workitem.id, context=context) values = dict(state='done') if not workitem.date: values['date'] = datetime.now().strftime(DT_FMT) workitem.write(values) if result: # process _chain workitem.refresh() # reload date = datetime.strptime(workitem.date, DT_FMT) for transition in activity.to_ids: if transition.trigger == 'cosmetic': continue launch_date = False if transition.trigger == 'auto': launch_date = date elif transition.trigger == 'time': launch_date = date + transition._delta() if launch_date: launch_date = launch_date.strftime(DT_FMT) values = { 'date': launch_date, 'segment_id': workitem.segment_id.id, 'activity_id': transition.activity_to_id.id, 'partner_id': workitem.partner_id.id, 'res_id': workitem.res_id, 'state': 'todo', } wi_id = self.create(cr, uid, values, context=context) # Now, depending on the trigger and the campaign mode # we know whether we must run the newly created workitem. # # rows = transition trigger \ colums = campaign mode # # test test_realtime manual normal (active) # time Y N N N # cosmetic N N N N # auto Y Y N Y # run = (transition.trigger == 'auto' \ and campaign_mode != 'manual') \ or (transition.trigger == 'time' \ and campaign_mode == 'test') if run: new_wi = self.browse(cr, uid, wi_id, context) self._process_one(cr, uid, new_wi, context) except Exception: tb = "".join(format_exception(*exc_info())) workitem.write({'state': 'exception', 'error_msg': tb}) @api.cr_uid_ids_context def process(self, cr, uid, workitem_ids, context=None): for wi in self.browse(cr, uid, workitem_ids, context=context): self._process_one(cr, uid, wi, context=context) return True def process_all(self, cr, uid, camp_ids=None, context=None): camp_obj = self.pool.get('marketing.campaign') if camp_ids is None: camp_ids = camp_obj.search(cr, uid, [('state', '=', 'running')], context=context) for camp in camp_obj.browse(cr, uid, camp_ids, context=context): if camp.mode == 'manual': # manual states are not processed automatically continue while True: domain = [('campaign_id', '=', camp.id), ('state', '=', 'todo'), ('date', '!=', False)] if camp.mode in ('test_realtime', 'active'): domain += [('date', '<=', time.strftime('%Y-%m-%d %H:%M:%S'))] workitem_ids = self.search(cr, uid, domain, context=context) if not workitem_ids: break self.process(cr, uid, workitem_ids, context=context) return True def preview(self, cr, uid, ids, context=None): res = {} wi_obj = self.browse(cr, uid, ids[0], context=context) if wi_obj.activity_id.type == 'email': view_id = self.pool.get('ir.model.data').get_object_reference( cr, uid, 'email_template', 'email_template_preview_form') res = { 'name': _('Email Preview'), 'view_type': 'form', 'view_mode': 'form,tree', 'res_model': 'email_template.preview', 'view_id': False, 'context': context, 'views': [(view_id and view_id[1] or 0, 'form')], 'type': 'ir.actions.act_window', 'target': 'new', 'nodestroy': True, 'context': "{'template_id':%d,'default_res_id':%d}" % (wi_obj.activity_id.email_template_id.id, wi_obj.res_id) } elif wi_obj.activity_id.type == 'report': datas = {'ids': [wi_obj.res_id], 'model': wi_obj.object_id.model} res = { 'type': 'ir.actions.report.xml', 'report_name': wi_obj.activity_id.report_id.report_name, 'datas': datas, } else: raise osv.except_osv( _('No preview'), _('The current step for this item has no email or report to preview.' )) return res
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', 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('Product 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('Account')), 'signal': fields.char( 'Signal', 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): report_data, format = render_report(cr, uid, [], activity.report_id.report_name, {}, context=context) 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) server_obj.run(cr, uid, [activity.server_action_id.id], context=action_context) return True 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 is 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 marketing_campaign_segment(osv.osv): _name = "marketing.campaign.segment" _description = "Campaign Segment" _order = "name" def _get_next_sync(self, cr, uid, ids, fn, args, context=None): # next auto sync date is same for all segments sync_job = self.pool.get('ir.model.data').get_object( cr, uid, 'marketing_campaign', 'ir_cron_marketing_campaign_every_day', context=context) next_sync = sync_job and sync_job.nextcall or False return dict.fromkeys(ids, next_sync) _columns = { 'name': fields.char('Name', required=True), 'campaign_id': fields.many2one('marketing.campaign', 'Campaign', required=True, select=1, ondelete="cascade"), 'object_id': fields.related('campaign_id','object_id', type='many2one', relation='ir.model', string='Resource'), 'ir_filter_id': fields.many2one('ir.filters', 'Filter', ondelete="restrict", help="Filter to select the matching resource records that belong to this segment. "\ "New filters can be created and saved using the advanced search on the list view of the Resource. "\ "If no filter is set, all records are selected without filtering. "\ "The synchronization mode may also add a criterion to the filter."), 'sync_last_date': fields.datetime('Last Synchronization', help="Date on which this segment was synchronized last time (automatically or manually)"), 'sync_mode': fields.selection([('create_date', 'Only records created after last sync'), ('write_date', 'Only records modified after last sync (no duplicates)'), ('all', 'All records (no duplicates)')], 'Synchronization mode', help="Determines an additional criterion to add to the filter when selecting new records to inject in the campaign. "\ '"No duplicates" prevents selecting records which have already entered the campaign previously.'\ 'If the campaign has a "unique field" set, "no duplicates" will also prevent selecting records which have '\ 'the same value for the unique field as other records that already entered the campaign.'), 'state': fields.selection([('draft', 'New'), ('cancelled', 'Cancelled'), ('running', 'Running'), ('done', 'Done')], 'Status', copy=False), 'date_run': fields.datetime('Launch Date', help="Initial start date of this segment."), 'date_done': fields.datetime('End Date', help="Date this segment was last closed or cancelled."), 'date_next_sync': fields.function(_get_next_sync, string='Next Synchronization', type='datetime', help="Next time the synchronization job is scheduled to run automatically"), } _defaults = { 'state': lambda *a: 'draft', 'sync_mode': lambda *a: 'create_date', } def _check_model(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): if not obj.ir_filter_id: return True if obj.campaign_id.object_id.model != obj.ir_filter_id.model_id: return False return True _constraints = [ (_check_model, 'Model of filter must be same as resource model of Campaign ', ['ir_filter_id,campaign_id']), ] def onchange_campaign_id(self, cr, uid, ids, campaign_id): res = {'domain': {'ir_filter_id': []}} campaign_pool = self.pool.get('marketing.campaign') if campaign_id: campaign = campaign_pool.browse(cr, uid, campaign_id) model_name = self.pool.get('ir.model').read( cr, uid, [campaign.object_id.id], ['model']) if model_name: mod_name = model_name[0]['model'] res['domain'] = {'ir_filter_id': [('model_id', '=', mod_name)]} else: res['value'] = {'ir_filter_id': False} return res def state_running_set(self, cr, uid, ids, *args): segment = self.browse(cr, uid, ids[0]) vals = {'state': 'running'} if not segment.date_run: vals['date_run'] = time.strftime('%Y-%m-%d %H:%M:%S') self.write(cr, uid, ids, vals) return True def state_done_set(self, cr, uid, ids, *args): wi_ids = self.pool.get("marketing.campaign.workitem").search( cr, uid, [('state', '=', 'todo'), ('segment_id', 'in', ids)]) self.pool.get("marketing.campaign.workitem").write( cr, uid, wi_ids, {'state': 'cancelled'}) self.write(cr, uid, ids, { 'state': 'done', 'date_done': time.strftime('%Y-%m-%d %H:%M:%S') }) return True def state_cancel_set(self, cr, uid, ids, *args): wi_ids = self.pool.get("marketing.campaign.workitem").search( cr, uid, [('state', '=', 'todo'), ('segment_id', 'in', ids)]) self.pool.get("marketing.campaign.workitem").write( cr, uid, wi_ids, {'state': 'cancelled'}) self.write(cr, uid, ids, { 'state': 'cancelled', 'date_done': time.strftime('%Y-%m-%d %H:%M:%S') }) return True def synchroniz(self, cr, uid, ids, *args): self.process_segment(cr, uid, ids) return True @api.cr_uid_ids_context def process_segment(self, cr, uid, segment_ids=None, context=None): Workitems = self.pool.get('marketing.campaign.workitem') Campaigns = self.pool.get('marketing.campaign') if not segment_ids: segment_ids = self.search(cr, uid, [('state', '=', 'running')], context=context) action_date = time.strftime('%Y-%m-%d %H:%M:%S') campaigns = set() for segment in self.browse(cr, uid, segment_ids, context=context): if segment.campaign_id.state != 'running': continue campaigns.add(segment.campaign_id.id) act_ids = self.pool.get('marketing.campaign.activity').search( cr, uid, [('start', '=', True), ('campaign_id', '=', segment.campaign_id.id)], context=context) model_obj = self.pool[segment.object_id.model] criteria = [] if segment.sync_last_date and segment.sync_mode != 'all': criteria += [(segment.sync_mode, '>', segment.sync_last_date)] if segment.ir_filter_id: criteria += eval(segment.ir_filter_id.domain) object_ids = model_obj.search(cr, uid, criteria, context=context) # XXX TODO: rewrite this loop more efficiently without doing 1 search per record! for record in model_obj.browse(cr, uid, object_ids, context=context): # avoid duplicate workitem for the same resource if segment.sync_mode in ('write_date', 'all'): if Campaigns._find_duplicate_workitems(cr, uid, record, segment.campaign_id, context=context): continue wi_vals = { 'segment_id': segment.id, 'date': action_date, 'state': 'todo', 'res_id': record.id } partner = self.pool.get('marketing.campaign')._get_partner_for( segment.campaign_id, record) if partner: wi_vals['partner_id'] = partner.id for act_id in act_ids: wi_vals['activity_id'] = act_id Workitems.create(cr, uid, wi_vals, context=context) self.write(cr, uid, segment.id, {'sync_last_date': action_date}, context=context) Workitems.process_all(cr, uid, list(campaigns), context=context) return True
clock_ids_exist = [] try: for order in self.browse(cr, uid, ids, context=context): emp_code = order.employee_id.emp_code for clock_id in clock_ids: emps_clock = clock_sync_obj.clock_emps_get(cr, uid, clock_id, [emp_code], context=context) if emps_clock: clock_ids_exist.append(clock_id) resu[order.id] = clock_ids_exist except Exception,e: traceback.print_exc() pass return resu _columns = { 'employee_id': fields.many2one('hr.employee', 'Employee', required=True, select=True), 'department_id':fields.related('employee_id','department_id', type='many2one', relation='hr.department', string='Department', store=True), 'job_id':fields.related('employee_id','job_id', type='many2one', relation='hr.job', string='Title', store=True), 'emp_code':fields.related('employee_id','emp_code', type='char', string='Employee Code', store=True), 'mobile_phone':fields.related('employee_id','mobile_phone', type='char', string='Work Mobile', store=True), 'borrow_money_residual':fields.related('employee_id','money_residual', type='float', string='Borrowed residual', readonly=True), 'dimmission_reason':fields.text('Dimission Reason', required=True), 'advice_to_company':fields.text('Advice to company'), 'employment_start':fields.date('Employment Started'), 'date_request':fields.date('Request Date', required=True), 'date_done':fields.date('Done Date', required=False, readonly=True), 'approve_ids': fields.one2many('hr.dimission.item', 'dimission_id', 'Approvals', domain=[('type','=','approve')]), 'transfer_ids': fields.one2many('hr.dimission.item', 'dimission_id', 'Transfers', domain=[('type','=','transfer')]), 'payslip_id': fields.many2many('hr.emppay', string='Payslip'),
class social_programs_direction(osv.osv): _description = "Direcciones de programas sociales" _name = 'social.programs.direction' def onchange_state(self, cr, uid, ids, state_id, context=None): """ Obtiene la informacion del pais en base a el estado seleccioando """ if state_id: country_id = self.pool.get('res.country.state').browse( cr, uid, state_id, context).country_id.id return {'value': {'country_id': country_id}} return {} def onchange_settlement(self, cr, uid, ids, settlement_id, context=None): """ Obtiene la informacion de ciudad, estado, pais, CP, etc... en base a la colonia seleccionada """ if settlement_id: settlement = self.pool.get( 'social.programs.res.settlement').browse( cr, uid, settlement_id, context) if settlement.id: state = self.pool.get('res.country.state').browse( cr, uid, settlement.city_id.state_id.id, context) return { 'value': { 'country_id': state.country_id.id, 'state_id': state.id, 'city_id': settlement.city_id.id, 'city': settlement.city_id.name, 'area_id': settlement.area_id.id, 'sector_id': settlement.sector_id.id, 'zip': settlement.zip } } return {} _columns = { 'name': fields.char('Nombre', size=128, required=True, select=True), # Datos de ubicacion 'state_id': fields.many2one('res.country.state', 'Estado'), 'country_id': fields.related('state_id', 'country_id', type="many2one", relation="res.country", string="Country", store=True), 'city_id': fields.many2one("social.programs.res.city", 'Ciudad'), 'settlement_id': fields.many2one('social.programs.res.settlement', 'Colonia'), 'area_id': fields.related('settlement_id', 'area_id', type="many2one", relation="social.programs.res.area", string="Area", readonly=True), 'sector_id': fields.related('settlement_id', 'sector_id', type="many2one", relation="social.programs.res.sector", string="Sector", readonly=True), 'street': fields.char('Calle', size=128), 'zip': fields.char('C.P.', change_default=True, size=24), 'email': fields.char('Email', size=240), 'phone': fields.char('Phone', size=64), 'fax': fields.char('Fax', size=64), 'mobile': fields.char('Mobile', size=64), # Datos complemento 'user_id': fields.many2one('res.users', 'Director', help='Encargado de la direccion.'), 'comment': fields.text('Notas'), 'active': fields.boolean('Active'), } _order = 'name'
'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Account')), 'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes', readonly=True, states={'draft': [('readonly', False)]}), 'address_allotment_id': fields.many2one('res.partner', 'Allotment Partner',help="A partner to whom the particular product needs to be allotted."), 'product_uom_qty': fields.float('Quantity', digits_compute= dp.get_precision('Product UoS'), required=True, readonly=True, states={'draft': [('readonly', False)]}), 'product_uom': fields.many2one('product.uom', 'Unit of Measure ', required=True, readonly=True, states={'draft': [('readonly', False)]}), 'product_uos_qty': fields.float('Quantity (UoS)' ,digits_compute= dp.get_precision('Product UoS'), readonly=True, states={'draft': [('readonly', False)]}), 'product_uos': fields.many2one('product.uom', 'Product UoS'), 'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount'), readonly=True, states={'draft': [('readonly', False)]}), 'th_weight': fields.float('Weight', readonly=True, states={'draft': [('readonly', False)]}), 'state': fields.selection([('cancel', 'Cancelled'),('draft', 'Draft'),('confirmed', 'Confirmed'),('exception', 'Exception'),('done', 'Done')], 'Status', required=True, readonly=True, help='* The \'Draft\' status is set when the related sales order in draft status. \ \n* The \'Confirmed\' status is set when the related sales order is confirmed. \ \n* The \'Exception\' status is set when the related sales order is set as exception. \ \n* The \'Done\' status is set when the sales order line has been picked. \ \n* The \'Cancelled\' status is set when a user cancel the sales order related.'), 'order_partner_id': fields.related('order_id', 'partner_id', type='many2one', relation='res.partner', store=True, string='Customer'), 'salesman_id':fields.related('order_id', 'user_id', type='many2one', relation='res.users', store=True, string='Salesperson'), 'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), } _order = 'order_id desc, sequence, id' _defaults = { 'product_uom' : _get_uom_id, 'discount': 0.0, 'product_uom_qty': 1, 'product_uos_qty': 1, 'sequence': 10, 'state': 'draft', 'type': 'make_to_stock', 'price_unit': 0.0, }
class social_programs_program_delivery(osv.osv, format_address): _description = "Entrega Programas sociales" _name = 'social.programs.program.delivery' def action_delivery_product(self, cr, uid, ids, context=None): """ Muestra una ventana con la informacion del documento a entregar """ partner_id = self.browse(cr, uid, ids[0], context=context).partner_id.id mod_obj = self.pool.get('ir.model.data') res = mod_obj.get_object_reference( cr, uid, 'social_programs', 'view_social_programs_program_delivery_form') res_id = res and res[1] or False #~ Redirecciona al formulario de Entrega return { 'name': "Entrega", 'view_type': 'form', 'view_mode': 'form', 'view_id': [res_id], 'res_model': 'social.programs.program.delivery', # object name 'type': 'ir.actions.act_window', 'nodestroy': True, 'target': 'new', 'res_id': ids[0], # id of the object to which to redirected } def action_delivery(self, cr, uid, ids, context=None): """ Pone el documento como entregado """ date = fields.date.context_today(self, cr, uid, context=context) self.write(cr, uid, ids, { 'delivery': True, 'date': date }, context=context) print "*************** cambio de estado **************** " #~ Cierra la ventana return {'type': 'ir.actions.act_window_close'} def action_delivery_and_print(self, cr, uid, ids, context=None): """ Entrega el documento e imprime el reporte """ self.action_delivery(cr, uid, ids, context=context) res = self.action_print_report(cr, uid, ids, context=context) return res def action_print_report(self, cr, uid, ids, context=None): datas = {} if context is None: context = {} print "************** active_ids ************* ", context.get( 'active_ids', []) data = self.read(cr, uid, ids)[0] datas = { 'ids': ids, 'model': 'social.programs.program.delivery', 'form': data } return { 'type': 'ir.actions.report.xml', 'report_name': 'report.social.programs.program.delivery.webkit', 'datas': datas, } def onchange_delivery(self, cr, uid, ids, delivery, context=None): """ Cierra la ventana si cambia el estado a entregado """ if delivery == True: return {'type': 'ir.actions.act_window_close'} return True _columns = { 'program_id': fields.many2one('social.programs.program', 'Programa', readonly=True), 'partner_id': fields.many2one('res.partner', 'Beneficiario', readonly=True), 'product_id': fields.many2one('product.product', 'Producto', readonly=True), 'qty': fields.float('Cantidad a entregar', readonly=True), 'date': fields.date('Entregado', readonly=True), 'delivery': fields.boolean('Entregado', readonly=True), # Campos relacionados con el beneficiario 'name': fields.related('partner_id', 'name', type="char", string="Nombre", readonly=True), 'curp': fields.related('partner_id', 'curp', type="char", string="curp", readonly=True), 'image': fields.related('partner_id', 'image', type="binary", string="Image", readonly=True), 'image_medium': fields.related('partner_id', 'image_medium', type="binary", string="Image medium", readonly=True), 'category_id': fields.many2many("res.partner.category", 'partner_id', 'category_id', string="Etiquetas", readonly=True), 'street': fields.related('partner_id', 'street', type="char", string="Calle", readonly=True), 'settlement_id': fields.related('partner_id', 'settlement_id', type='many2one', relation='social.programs.res.settlement', string='Colonia', readonly=True), 'city_id': fields.related('partner_id', 'city_id', type='many2one', relation='social.programs.res.city', string='Ciudad', readonly=True), 'state_id': fields.related('partner_id', 'state_id', type='many2one', relation='res.country.state', string='Estado', readonly=True), 'zip': fields.related('partner_id', 'zip', type="char", string="C.P.", readonly=True), 'sector_id': fields.related('partner_id', 'sector_id', type='many2one', relation='social.programs.res.sector', string='Sector', readonly=True), 'area_id': fields.related('partner_id', 'area_id', type='many2one', relation='social.programs.res.area', string='Area', readonly=True), 'country_id': fields.related('partner_id', 'country_id', type='many2one', relation='res.country', string='Estado', readonly=True), 'phone': fields.related('partner_id', 'phone', type="char", string="Telefono", readonly=True), 'mobile': fields.related('partner_id', 'mobile', type="char", string="Celular", readonly=True), 'email': fields.related('partner_id', 'email', type="char", string="Correo", readonly=True), } _defaults = {'delivery': False} _order = 'delivery,program_id,partner_id,product_id,qty'
# # #else: # # logging.getLogger(self._name).info("Este activo ya esta asentado!") _columns = { 'department_id' : fields.many2one('hr.department', 'Departament'), 'life_expectancy' : fields.float('Life Expectancy', help="""Life expectancy of the asset in years."""), 'life_expectancy_percentage': fields.float('Life Expectancy Percentage'), 'stage_id': fields.many2one('account.asset.stage', 'Stage', help="""The stage of the asset indicate in wich point of his life is at the moment. You can add new stages in configuration."""), 'code': fields.char("Code", size=9, required=True), 'model_id': fields.many2one('account.asset.model', 'Model'), 'brand_id': fields.related('model_id', 'brand_id', string='Brand', type="many2one", relation="account.asset.model", readonly=True), 'category': fields.selection(( ('administrative', 'Administrative'), ('scholarly', 'Scholarly'), ('none', 'None')), 'Type of asset', help="""The type of asset, scholarly or administrative."""), 'asset_move_ids': fields.one2many('account.asset.move', 'asset_id', 'Movements') } _defaults = { 'category': 'administrative', } class AccountCategory(orm.Model): _name = 'account.asset.category'
class account_followup_print(osv.osv_memory): _name = 'account_followup.print' _description = 'Print Follow-up & Send Mail to Customers' _columns = { 'date': fields.date( 'Follow-up Sending Date', required=True, help= "This field allow you to select a forecast date to plan your follow-ups" ), 'followup_id': fields.many2one('account_followup.followup', 'Follow-Up', required=True, readonly=True), 'partner_ids': fields.many2many('account_followup.stat.by.partner', 'partner_stat_rel', 'osv_memory_id', 'partner_id', 'Partners', required=True), 'company_id': fields.related('followup_id', 'company_id', type='many2one', relation='res.company', store=True, readonly=True), 'email_conf': fields.boolean('Send Email Confirmation'), 'email_subject': fields.char('Email Subject', size=64), 'partner_lang': fields.boolean( 'Send Email in Partner Language', help= 'Do not change message text, if you want to send email in partner language, or configure from company' ), 'email_body': fields.text('Email Body'), 'summary': fields.text('Summary', readonly=True), 'test_print': fields.boolean( 'Test Print', help= 'Check if you want to print follow-ups without changing follow-up level.' ), } def _get_followup(self, cr, uid, context=None): if context is None: context = {} if context.get('active_model', 'ir.ui.menu') == 'account_followup.followup': return context.get('active_id', False) company_id = self.pool.get('res.users').browse( cr, uid, uid, context=context).company_id.id followp_id = self.pool.get('account_followup.followup').search( cr, uid, [('company_id', '=', company_id)], context=context) return followp_id and followp_id[0] or False def process_partners(self, cr, uid, partner_ids, data, context=None): partner_obj = self.pool.get('res.partner') partner_ids_to_print = [] nbmanuals = 0 manuals = {} nbmails = 0 nbunknownmails = 0 nbprints = 0 resulttext = " " for partner in self.pool.get( 'account_followup.stat.by.partner').browse(cr, uid, partner_ids, context=context): if partner.max_followup_id.manual_action: partner_obj.do_partner_manual_action(cr, uid, [partner.partner_id.id], context=context) nbmanuals = nbmanuals + 1 key = partner.partner_id.payment_responsible_id.name or _( "Anybody") if not key in manuals.keys(): manuals[key] = 1 else: manuals[key] = manuals[key] + 1 if partner.max_followup_id.send_email: nbunknownmails += partner_obj.do_partner_mail( cr, uid, [partner.partner_id.id], context=context) nbmails += 1 if partner.max_followup_id.send_letter: partner_ids_to_print.append(partner.id) nbprints += 1 message = _( "Follow-up letter of " ) + "<I> " + partner.partner_id.latest_followup_level_id_without_lit.name + "</I>" + _( " will be sent") partner_obj.message_post(cr, uid, [partner.partner_id.id], body=message, context=context) if nbunknownmails == 0: resulttext += str(nbmails) + _(" email(s) sent") else: resulttext += str(nbmails) + _( " email(s) should have been sent, but ") + str( nbunknownmails) + _( " had unknown email address(es)") + "\n <BR/> " resulttext += "<BR/>" + str(nbprints) + _( " letter(s) in report") + " \n <BR/>" + str(nbmanuals) + _( " manual action(s) assigned:") needprinting = False if nbprints > 0: needprinting = True resulttext += "<p align=\"center\">" for item in manuals: resulttext = resulttext + "<li>" + item + ":" + str( manuals[item]) + "\n </li>" resulttext += "</p>" result = {} action = partner_obj.do_partner_print(cr, uid, partner_ids_to_print, data, context=context) result['needprinting'] = needprinting result['resulttext'] = resulttext result['action'] = action or {} return result def do_update_followup_level(self, cr, uid, to_update, partner_list, date, context=None): #update the follow-up level on account.move.line for id in to_update.keys(): if to_update[id]['partner_id'] in partner_list: self.pool.get('account.move.line').write( cr, uid, [int(id)], { 'followup_line_id': to_update[id]['level'], 'followup_date': date }) def clear_manual_actions(self, cr, uid, partner_list, context=None): # Partnerlist is list to exclude # Will clear the actions of partners that have no due payments anymore partner_list_ids = [ partner.partner_id.id for partner in self.pool.get('account_followup.stat.by.partner'). browse(cr, uid, partner_list, context=context) ] ids = self.pool.get('res.partner').search( cr, uid, [ '&', ('id', 'not in', partner_list_ids), '|', ('payment_responsible_id', '!=', False), ('payment_next_action_date', '!=', False) ], context=context) partners_to_clear = [] for part in self.pool.get('res.partner').browse(cr, uid, ids, context=context): if not part.unreconciled_aml_ids: partners_to_clear.append(part.id) self.pool.get('res.partner').action_done(cr, uid, partners_to_clear, context=context) return len(partners_to_clear) def do_process(self, cr, uid, ids, context=None): if context is None: context = {} #Get partners tmp = self._get_partners_followp(cr, uid, ids, context=context) partner_list = tmp['partner_ids'] to_update = tmp['to_update'] date = self.browse(cr, uid, ids, context=context)[0].date data = self.read(cr, uid, ids, [], context=context)[0] data['followup_id'] = data['followup_id'][0] #Update partners self.do_update_followup_level(cr, uid, to_update, partner_list, date, context=context) #process the partners (send mails...) restot = self.process_partners(cr, uid, partner_list, data, context=context) #clear the manual actions if nothing is due anymore nbactionscleared = self.clear_manual_actions(cr, uid, partner_list, context=context) if nbactionscleared > 0: restot['resulttext'] = restot['resulttext'] + "<li>" + _( "%s partners have no credits and as such the action is cleared" ) % (str(nbactionscleared)) + "</li>" res = restot['action'] #return the next action mod_obj = self.pool.get('ir.model.data') model_data_ids = mod_obj.search( cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_account_followup_sending_results')], context=context) resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] context.update({ 'description': restot['resulttext'], 'needprinting': restot['needprinting'], 'report_data': res }) return { 'name': _('Send Letters and Emails: Actions Summary'), 'view_type': 'form', 'context': context, 'view_mode': 'tree,form', 'res_model': 'account_followup.sending.results', 'views': [(resource_id, 'form')], 'type': 'ir.actions.act_window', 'target': 'new', } def _get_msg(self, cr, uid, context=None): return self.pool.get('res.users').browse( cr, uid, uid, context=context).company_id.follow_up_msg _defaults = { 'date': lambda *a: time.strftime('%Y-%m-%d'), 'followup_id': _get_followup, 'email_body': "", 'email_subject': _('Invoices Reminder'), 'partner_lang': True, } def _get_partners_followp(self, cr, uid, ids, context=None): data = {} data = self.browse(cr, uid, ids, context=context)[0] company_id = data.company_id.id cr.execute( "SELECT l.partner_id, l.followup_line_id,l.date_maturity, l.date, l.id "\ "FROM account_move_line AS l "\ "LEFT JOIN account_account AS a "\ "ON (l.account_id=a.id) "\ "WHERE (l.reconcile_id IS NULL) "\ "AND (a.type='receivable') "\ "AND (l.state<>'draft') "\ "AND (l.partner_id is NOT NULL) "\ "AND (a.active) "\ "AND (l.debit > 0) "\ "AND (l.company_id = %s) " \ "AND (l.blocked = False)" \ "ORDER BY l.date", (company_id,)) #l.blocked added to take litigation into account and it is not necessary to change follow-up level of account move lines without debit move_lines = cr.fetchall() old = None fups = {} fup_id = 'followup_id' in context and context[ 'followup_id'] or data.followup_id.id date = 'date' in context and context['date'] or data.date current_date = datetime.date(*time.strptime(date, '%Y-%m-%d')[:3]) cr.execute( "SELECT * "\ "FROM account_followup_followup_line "\ "WHERE followup_id=%s "\ "ORDER BY delay", (fup_id,)) #Create dictionary of tuples where first element is the date to compare with the due date and second element is the id of the next level for result in cr.dictfetchall(): delay = datetime.timedelta(days=result['delay']) fups[old] = (current_date - delay, result['id']) old = result['id'] partner_list = [] to_update = {} #Fill dictionary of accountmovelines to_update with the partners that need to be updated for partner_id, followup_line_id, date_maturity, date, id in move_lines: if not partner_id: continue if followup_line_id not in fups: continue stat_line_id = partner_id * 10000 + company_id if date_maturity: if date_maturity <= fups[followup_line_id][0].strftime( '%Y-%m-%d'): if stat_line_id not in partner_list: partner_list.append(stat_line_id) to_update[str(id)] = { 'level': fups[followup_line_id][1], 'partner_id': stat_line_id } elif date and date <= fups[followup_line_id][0].strftime( '%Y-%m-%d'): if stat_line_id not in partner_list: partner_list.append(stat_line_id) to_update[str(id)] = { 'level': fups[followup_line_id][1], 'partner_id': stat_line_id } return {'partner_ids': partner_list, 'to_update': to_update}
class hr_evaluation_interview(osv.Model): _name = 'hr.evaluation.interview' _inherit = ['mail.thread'] _rec_name = 'user_to_review_id' _description = 'Appraisal Interview' _columns = { 'request_id': fields.many2one('survey.user_input', 'Survey Request', ondelete='cascade', readonly=True), 'evaluation_id': fields.many2one('hr_evaluation.evaluation', 'Appraisal Plan', required=True), 'phase_id': fields.many2one('hr_evaluation.plan.phase', 'Appraisal Phase', required=True), 'user_to_review_id': fields.related('evaluation_id', 'employee_id', type="many2one", relation="hr.employee", string="Employee to evaluate"), 'user_id': fields.many2one('res.users', 'Interviewer'), 'state': fields.selection([('draft', "Draft"), ('waiting_answer', "In progress"), ('done', "Done"), ('cancel', "Cancelled")], string="State", required=True, copy=False), 'survey_id': fields.related('phase_id', 'survey_id', string="Appraisal Form", type="many2one", relation="survey.survey"), 'deadline': fields.related('request_id', 'deadline', type="datetime", string="Deadline"), } _defaults = { 'state': 'draft' } def create(self, cr, uid, vals, context=None): phase_obj = self.pool.get('hr_evaluation.plan.phase') survey_id = phase_obj.read(cr, uid, vals.get('phase_id'), fields=['survey_id'], context=context)['survey_id'][0] if vals.get('user_id'): user_obj = self.pool.get('res.users') partner_id = user_obj.read(cr, uid, vals.get('user_id'), fields=['partner_id'], context=context)['partner_id'][0] else: partner_id = None user_input_obj = self.pool.get('survey.user_input') if not vals.get('deadline'): vals['deadline'] = (datetime.now() + timedelta(days=28)).strftime(DF) ret = user_input_obj.create(cr, uid, {'survey_id': survey_id, 'deadline': vals.get('deadline'), 'type': 'link', 'partner_id': partner_id}, context=context) vals['request_id'] = ret return super(hr_evaluation_interview, self).create(cr, uid, vals, context=context) def name_get(self, cr, uid, ids, context=None): if not ids: return [] reads = self.browse(cr, uid, ids, context=context) res = [] for record in reads: name = record.survey_id.title res.append((record['id'], name)) return res def survey_req_waiting_answer(self, cr, uid, ids, context=None): request_obj = self.pool.get('survey.user_input') for interview in self.browse(cr, uid, ids, context=context): if interview.request_id: request_obj.action_survey_resent(cr, uid, [interview.request_id.id], context=context) self.write(cr, uid, interview.id, {'state': 'waiting_answer'}, context=context) return True def survey_req_done(self, cr, uid, ids, context=None): for id in self.browse(cr, uid, ids, context=context): flag = False wating_id = 0 if not id.evaluation_id.id: raise UserError(_("You cannot start evaluation without Appraisal.")) records = id.evaluation_id.survey_request_ids for child in records: if child.state == "draft": wating_id = child.id continue if child.state != "done": flag = True if not flag and wating_id: self.survey_req_waiting_answer(cr, uid, [wating_id], context=context) self.write(cr, uid, ids, {'state': 'done'}, context=context) return True def survey_req_cancel(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state': 'cancel'}, context=context) return True def action_print_survey(self, cr, uid, ids, context=None): """ If response is available then print this response otherwise print survey form (print template of the survey) """ context = dict(context or {}) interview = self.browse(cr, uid, ids, context=context)[0] survey_obj = self.pool.get('survey.survey') response_obj = self.pool.get('survey.user_input') response = response_obj.browse(cr, uid, interview.request_id.id, context=context) context.update({'survey_token': response.token}) return survey_obj.action_print_survey(cr, uid, [interview.survey_id.id], context=context) def action_start_survey(self, cr, uid, ids, context=None): context = dict(context or {}) interview = self.browse(cr, uid, ids, context=context)[0] survey_obj = self.pool.get('survey.survey') response_obj = self.pool.get('survey.user_input') # grab the token of the response and start surveying response = response_obj.browse(cr, uid, interview.request_id.id, context=context) context.update({'survey_token': response.token}) return survey_obj.action_start_survey(cr, uid, [interview.survey_id.id], context=context)
class IrAttachment(osv.Model): """ Update partner to add a field about notification preferences """ _name = "ir.attachment" _inherit = 'ir.attachment' _fileext_to_type = { '7z': 'archive', 'aac': 'audio', 'ace': 'archive', 'ai': 'vector', 'aiff': 'audio', 'apk': 'archive', 'app': 'binary', 'as': 'script', 'asf': 'video', 'ass': 'text', 'avi': 'video', 'bat': 'script', 'bin': 'binary', 'bmp': 'image', 'bzip2': 'archive', 'c': 'script', 'cab': 'archive', 'cc': 'script', 'ccd': 'disk', 'cdi': 'disk', 'cdr': 'vector', 'cer': 'certificate', 'cgm': 'vector', 'cmd': 'script', 'coffee': 'script', 'com': 'binary', 'cpp': 'script', 'crl': 'certificate', 'crt': 'certificate', 'cs': 'script', 'csr': 'certificate', 'css': 'html', 'csv': 'spreadsheet', 'cue': 'disk', 'd': 'script', 'dds': 'image', 'deb': 'archive', 'der': 'certificate', 'djvu': 'image', 'dmg': 'archive', 'dng': 'image', 'doc': 'document', 'docx': 'document', 'dvi': 'print', 'eot': 'font', 'eps': 'vector', 'exe': 'binary', 'exr': 'image', 'flac': 'audio', 'flv': 'video', 'gif': 'webimage', 'gz': 'archive', 'gzip': 'archive', 'h': 'script', 'htm': 'html', 'html': 'html', 'ico': 'image', 'icon': 'image', 'img': 'disk', 'iso': 'disk', 'jar': 'archive', 'java': 'script', 'jp2': 'image', 'jpe': 'webimage', 'jpeg': 'webimage', 'jpg': 'webimage', 'jpx': 'image', 'js': 'script', 'key': 'presentation', 'keynote': 'presentation', 'lisp': 'script', 'lz': 'archive', 'lzip': 'archive', 'm': 'script', 'm4a': 'audio', 'm4v': 'video', 'mds': 'disk', 'mdx': 'disk', 'mid': 'audio', 'midi': 'audio', 'mkv': 'video', 'mng': 'image', 'mp2': 'audio', 'mp3': 'audio', 'mp4': 'video', 'mpe': 'video', 'mpeg': 'video', 'mpg': 'video', 'nrg': 'disk', 'numbers': 'spreadsheet', 'odg': 'vector', 'odm': 'document', 'odp': 'presentation', 'ods': 'spreadsheet', 'odt': 'document', 'ogg': 'audio', 'ogm': 'video', 'otf': 'font', 'p12': 'certificate', 'pak': 'archive', 'pbm': 'image', 'pdf': 'print', 'pem': 'certificate', 'pfx': 'certificate', 'pgf': 'image', 'pgm': 'image', 'pk3': 'archive', 'pk4': 'archive', 'pl': 'script', 'png': 'webimage', 'pnm': 'image', 'ppm': 'image', 'pps': 'presentation', 'ppt': 'presentation', 'ps': 'print', 'psd': 'image', 'psp': 'image', 'py': 'script', 'r': 'script', 'ra': 'audio', 'rar': 'archive', 'rb': 'script', 'rpm': 'archive', 'rtf': 'text', 'sh': 'script', 'sub': 'disk', 'svg': 'vector', 'sxc': 'spreadsheet', 'sxd': 'vector', 'tar': 'archive', 'tga': 'image', 'tif': 'image', 'tiff': 'image', 'ttf': 'font', 'txt': 'text', 'vbs': 'script', 'vc': 'spreadsheet', 'vml': 'vector', 'wav': 'audio', 'webp': 'image', 'wma': 'audio', 'wmv': 'video', 'woff': 'font', 'xar': 'vector', 'xbm': 'image', 'xcf': 'image', 'xhtml': 'html', 'xls': 'spreadsheet', 'xlsx': 'spreadsheet', 'xml': 'html', 'zip': 'archive' } def get_attachment_type(self, cr, uid, ids, name, args, context=None): result = {} for attachment in self.browse(cr, uid, ids, context=context): fileext = os.path.splitext(attachment.datas_fname or '')[1].lower()[1:] result[attachment.id] = self._fileext_to_type.get(fileext, 'unknown') return result _columns = { 'file_type_icon': fields.function(get_attachment_type, type='char', string='File Type Icon'), 'file_type': fields.related('file_type_icon', type='char'), # FIXME remove in trunk }
class procurement_rule(osv.osv): _inherit = 'procurement.rule' def _get_action(self, cr, uid, context=None): result = super(procurement_rule, self)._get_action(cr, uid, context=context) return result + [('move', _('Move From Another Location'))] def _get_rules(self, cr, uid, ids, context=None): res = [] for route in self.browse(cr, uid, ids): res += [x.id for x in route.pull_ids] return res _columns = { 'location_id': fields.many2one('stock.location', 'Procurement Location'), 'location_src_id': fields.many2one('stock.location', 'Source Location', help="Source location is action=move"), 'route_id': fields.many2one('stock.location.route', 'Route', help="If route_id is False, the rule is global"), 'procure_method': fields.selection( [('make_to_stock', 'Take From Stock'), ('make_to_order', 'Create Procurement')], 'Move Supply Method', required=True, help= """Determines the procurement method of the stock move that will be generated: whether it will need to 'take from the available stock' in its source location or needs to ignore its stock and create a procurement over there.""" ), 'route_sequence': fields.related('route_id', 'sequence', string='Route Sequence', store={ 'stock.location.route': (_get_rules, ['sequence'], 10), 'procurement.rule': (lambda self, cr, uid, ids, c={}: ids, ['route_id'], 10), }), 'picking_type_id': fields.many2one( 'stock.picking.type', 'Picking Type', help= "Picking Type determines the way the picking should be shown in the view, reports, ..." ), 'delay': fields.integer('Number of Days'), 'partner_address_id': fields.many2one('res.partner', 'Partner Address'), 'propagate': fields.boolean( 'Propagate cancel and split', help= 'If checked, when the previous move of the move (which was generated by a next procurement) is cancelled or split, the move generated by this move will too' ), 'warehouse_id': fields.many2one('stock.warehouse', 'Served Warehouse', help='The warehouse this rule is for'), 'propagate_warehouse_id': fields.many2one( 'stock.warehouse', 'Warehouse to Propagate', help= "The warehouse to propagate on the created move/procurement, which can be different of the warehouse this rule is for (e.g for resupplying rules from another warehouse)" ), } _defaults = { 'procure_method': 'make_to_stock', 'propagate': True, 'delay': 0, }
class account_asset_depreciation_line(osv.osv): _name = 'account.asset.depreciation.line' _description = 'Asset depreciation line' def _get_move_check(self, cr, uid, ids, name, args, context=None): res = {} for line in self.browse(cr, uid, ids, context=context): res[line.id] = bool(line.move_id) return res _columns = { 'name': fields.char('Depreciation Name', size=64, required=True, select=1), 'sequence': fields.integer('Sequence', required=True), 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, ondelete='cascade'), 'parent_state': fields.related('asset_id', 'state', type='char', string='State of Asset'), 'amount': fields.float('Current Depreciation', digits_compute=dp.get_precision('Account'), required=True), 'remaining_value': fields.float('Next Period Depreciation', digits_compute=dp.get_precision('Account'), required=True), 'depreciated_value': fields.float('Amount Already Depreciated', required=True), 'depreciation_date': fields.date('Depreciation Date', select=1), 'move_id': fields.many2one('account.move', 'Depreciation Entry'), 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Posted', store=True) } def create_move(self, cr, uid, ids, context=None): can_close = False if context is None: context = {} asset_obj = self.pool.get('account.asset.asset') period_obj = self.pool.get('account.period') move_obj = self.pool.get('account.move') move_line_obj = self.pool.get('account.move.line') currency_obj = self.pool.get('res.currency') created_move_ids = [] asset_ids = [] for line in self.browse(cr, uid, ids, context=context): depreciation_date = context.get( 'depreciation_date') or time.strftime('%Y-%m-%d') period_ids = period_obj.find(cr, uid, depreciation_date, context=context) company_currency = line.asset_id.company_id.currency_id.id current_currency = line.asset_id.currency_id.id context.update({'date': depreciation_date}) amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context) sign = (line.asset_id.category_id.journal_id.type == 'purchase' and 1) or -1 asset_name = line.asset_id.name reference = line.name move_vals = { 'name': asset_name, 'date': depreciation_date, 'ref': reference, 'period_id': period_ids and period_ids[0] or False, 'journal_id': line.asset_id.category_id.journal_id.id, } move_id = move_obj.create(cr, uid, move_vals, context=context) journal_id = line.asset_id.category_id.journal_id.id partner_id = line.asset_id.partner_id.id move_line_obj.create( cr, uid, { 'name': asset_name, 'ref': reference, 'move_id': move_id, 'account_id': line.asset_id.category_id.account_depreciation_id.id, 'debit': 0.0, 'credit': amount, 'period_id': period_ids and period_ids[0] or False, 'journal_id': journal_id, 'partner_id': partner_id, 'currency_id': company_currency != current_currency and current_currency or False, 'amount_currency': company_currency != current_currency and -sign * line.amount or 0.0, 'date': depreciation_date, }) move_line_obj.create( cr, uid, { 'name': asset_name, 'ref': reference, 'move_id': move_id, 'account_id': line.asset_id.category_id.account_expense_depreciation_id. id, 'credit': 0.0, 'debit': amount, 'period_id': period_ids and period_ids[0] or False, 'journal_id': journal_id, 'partner_id': partner_id, 'currency_id': company_currency != current_currency and current_currency or False, 'amount_currency': company_currency != current_currency and sign * line.amount or 0.0, 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id, 'date': depreciation_date, 'asset_id': line.asset_id.id }) self.write(cr, uid, line.id, {'move_id': move_id}, context=context) created_move_ids.append(move_id) asset_ids.append(line.asset_id.id) # we re-evaluate the assets to determine whether we can close them for asset in asset_obj.browse(cr, uid, list(set(asset_ids)), context=context): if currency_obj.is_zero(cr, uid, asset.currency_id, asset.value_residual): asset.write({'state': 'close'}) return created_move_ids
class hon_issue_line(orm.Model): _name = "hon.issue.line" def _amount_line(self, cr, uid, ids, prop, unknow_none, unknow_dict): res = {} for line in self.browse(cr, uid, ids): price = line.price_unit * line.quantity res[line.id] = price return res _columns = { 'sequence': fields.integer('Sequence', help="Gives the sequence of this line when displaying the honorarium issue."), 'name': fields.char('Description', required=True, size=64), 'page_number': fields.char('Pgnr', size=32), 'nr_of_columns': fields.float('#Cols', digits_compute= dp.get_precision('Number of Columns'), required=True), 'issue_id': fields.many2one('hon.issue', 'Issue Reference', ondelete='cascade', select=True), 'partner_id': fields.many2one('res.partner', 'Partner', required=True), 'employee': fields.boolean('Employee', help="It indicates that the partner is an employee."), 'product_category_id': fields.many2one('product.category', 'T/B',required=True, domain=[('parent_id.supportal', '=', True)]), 'product_id': fields.many2one('product.product', 'Product', required=True,), 'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."), 'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null', select=True), 'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')), 'quantity': fields.float('Quantity', digits_compute= dp.get_precision('Product Unit of Measure'), required=True), 'account_analytic_id': fields.related('issue_id','account_analytic_id',type='many2one', relation='account.analytic.account', string='Issue',store=True, readonly=True ), # 'parent_analytic_id': fields.related('account_analytic_id', 'parent_id', type='many2one', # relation='account.analytic.account', string='Title', store=True, # readonly=True), 'date_publish': fields.related('account_analytic_id', 'date_publish', type='date', relation='account.analytic.account', string='Publishing Date', store=True, readonly=True), 'activity_id': fields.many2one('project.activity_al', 'Page Type', ondelete='set null', select=True), 'company_id': fields.related('issue_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True), 'price_subtotal': fields.function(_amount_line, string='Amount', type="float", digits_compute= dp.get_precision('Account'), store=True), 'estimated_price': fields.float('Estimate',), 'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True), 'invoice_id': fields.related('invoice_line_id', 'invoice_id', type='many2one', relation='account.invoice', string='Invoice', readonly=True), 'state': fields.selection( [('cancel', 'Cancelled'), ('draft', 'Draft'), ('confirmed', 'Confirmed'), ('exception', 'Exception'), ('done', 'Done')], 'Status', required=True, readonly=True, help='* The \'Draft\' status is set when the related hon issue in draft status. \ \n* The \'Confirmed\' status is set when the related hon issue is confirmed. \ \n* The \'Exception\' status is set when the related hon issue is set as exception. \ \n* The \'Done\' status is set when the hon line has been picked. \ \n* The \'Cancelled\' status is set when a user cancel the hon issue related.'), 'gratis': fields.boolean('Gratis', help="It indicates that no letter/invoice is generated.") } def _default_account_id(self, cr, uid, context=None): # XXX this gets the default account for the user's company, # it should get the default account for the invoice's company # however, the invoice's company does not reach this point if context is None: context = {} prop = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category', context=context) print "pop ", prop return prop and prop.id or False _defaults = { 'quantity': 1, 'sequence': 10, 'account_id': _default_account_id, 'state': 'draft', 'price_unit': 0.0, 'employee': False, } def _prepare_hon_issue_line_invoice_line(self, cr, uid, line, account_id=False, context=None): """Prepare the dict of values to create the new invoice line for a honorarium line. This method may be overridden to implement custom invoice generation (making sure to call super() to establish a clean extension chain). :param browse_record line: hon.issue.line record to invoice :param int account_id: optional ID of a G/L account to force (this is used for returning products including service) :return: dict of values to create() the invoice line """ res = {} if not line.invoice_line_id: if not account_id: if line.product_id: account_id = line.product_id.property_account_expense_id.id if not account_id: account_id = line.product_id.categ_id.property_account_expense_categ_id.id if not account_id: raise osv.except_osv(_('Error!'), _('Please define expense account for this product: "%s" (id:%d).') % \ (line.product_id.name, line.product_id.id,)) else: prop = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ_id', 'product.category', context=context) account_id = prop and prop.id or False qty = line.quantity fpos = line.partner_id.property_account_position_id or False account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, account_id) if not account_id: raise osv.except_osv(_('Error!'), _('There is no Fiscal Position defined or Expense category account defined for default properties of Product categories.')) res = { 'hon_issue_line_id': line.id, 'account_analytic_id': line.account_analytic_id and line.account_analytic_id.id or False, 'name': line.name, 'sequence': line.sequence, 'origin': line.issue_id.name, 'account_id': account_id, 'price_unit': line.price_unit, 'quantity': qty, 'uos_id': line.uos_id, 'product_id': line.product_id.id or False, 'activity_id': line.activity_id and line.activity_id.id or False, } return res def invoice_line_create(self, cr, uid, ids, context=None): if context is None: context = {} create_ids = [] hon = set() for line in self.browse(cr, uid, ids, context=context): vals = self._prepare_hon_issue_line_invoice_line(cr, uid, line, False, context) if vals: inv_id = self.pool.get('account.invoice.line').create(cr, uid, vals, context=context) self.write(cr, uid, [line.id], {'invoice_line_id': inv_id }, context=context) hon.add(line.issue_id.id) create_ids.append(inv_id) # Trigger workflow events wf_service = netsvc.LocalService("workflow") for issue_id in hon: wf_service.trg_write(uid, 'hon.issue', issue_id, cr) return create_ids def button_cancel(self, cr, uid, ids, context=None): for line in self.browse(cr, uid, ids, context=context): if line.invoice_line_id: raise osv.except_osv(_('Invalid Action!'), _('You cannot cancel a Hon line that has already been invoiced.')) return self.write(cr, uid, ids, {'state': 'cancel'}) def button_confirm(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state': 'confirmed'}) def button_unconfirm(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state': 'draft'}) def button_done(self, cr, uid, ids, context=None): wf_service = netsvc.LocalService("workflow") res = self.write(cr, uid, ids, {'state': 'done'}) for line in self.browse(cr, uid, ids, context=context): wf_service.trg_write(uid, 'hon.issue', line.issue_id.id, cr) return res def unlink(self, cr, uid, ids, context=None): if context is None: context = {} """Allows to delete hon lines in draft,cancel states""" for rec in self.browse(cr, uid, ids, context=context): if rec.state not in ['draft', 'cancel']: raise osv.except_osv(_('Invalid Action!'), _('Cannot delete a hon line which is in state \'%s\'.') %(rec.state,)) return super(hon_issue_line, self).unlink(cr, uid, ids, context=context) def partner_id_change(self, cr, uid, ids, partner_id=False, context=None): if context is None: context = {} if not partner_id : # raise osv.except_osv(_('No Partner Defined!'),_("You must first select a partner!") ) return {'value':{}} part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) result = {} if part.employee : result['employee'] = True else: result['employee'] = False if part.product_category_ids: result['product_category_id'] = part.product_category_ids[0].id res_final = {'value':result} return res_final def product_category_change(self, cr, uid, ids, product_category=False, product=False, context=None): result = {} if product: prod_obj = self.pool['product.product'].browse(cr, uid, product, context=context) if product_category is not prod_obj.categ_id: result['product_id'] = [] else: result['product_id'] = product return {'value': result} def price_quantity_change(self, cr, uid, ids, partner_id=False, price_unit=False, quantity=False, price_subtotal=False, context=None): if context is None: context = {} result = {} if price_unit : if quantity : price = price_unit * quantity result['price_subtotal'] = price else: result['price_subtotal'] = price_subtotal res_final = {'value':result,} return res_final def product_id_change(self, cr, uid, ids, product, partner_id=False, price_unit=False, context=None ): if context is None: context = {} user = self.pool.get('res.users').browse(cr, uid, uid, context=context) company_id = context.get('company_id', user.company_id.id) context = dict(context) context.update({'company_id': company_id, 'force_company': company_id}) if not partner_id : # raise osv.except_osv(_('No Partner Defined!'),_("You must first select a partner!") ) return {'value':{}} if not product: # raise osv.except_osv(_('No Product Defined!'),_("You must first select a Product!") ) return {'value':{}} print "............ca,e" part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) if part.lang: context.update({'lang': part.lang}) result = {} res = self.pool.get('product.product').browse(cr, uid, product, context=context) a = res.property_account_expense_id.id if not a: a = res.categ_id.property_account_expense_categ_id.id if a: result['account_id'] = a pricelist = self.pool.get('partner.product.price').search(cr, uid, [('product_id','=',product), ('partner_id','=', partner_id), ('company_id','=', company_id )], context=context) if len(pricelist) >= 1 : price = self.pool.get('partner.product.price').browse(cr, uid, pricelist, context=context ) if price : result.update( {'price_unit': price[0].price_unit} ) else: result.update( {'price_unit': 0 } ) res_final = {'value':result,} return res_final
class nh_activity(orm.Model): """ Extends class :class:`nh_activity<activity.nh_activity>`. """ _name = 'nh.activity' _inherit = 'nh.activity' _columns = { 'user_ids': fields.many2many('res.users', 'activity_user_rel', 'activity_id', 'user_id', 'Users', readonly=True), 'patient_id': fields.many2one('nh.clinical.patient', 'Patient', readonly=True), 'location_id': fields.many2one('nh.clinical.location', 'Location', readonly=True), 'location_name': fields.related('location_id', 'full_name', type='char', size=150, string='Location Name'), 'pos_id': fields.many2one('nh.clinical.pos', 'POS', readonly=True), 'spell_activity_id': fields.many2one('nh.activity', 'Spell Activity', readonly=True), 'cancel_reason_id': fields.many2one('nh.cancel.reason', 'Cancellation Reason'), 'ward_manager_id': fields.many2one('res.users', 'Ward Manager of the ward on Complete/Cancel') } def create(self, cr, uid, vals, context=None): """ Extends Odoo's `create()` method. Writes ``user_ids`` for responsible users of the activities` location. :param vals: values to create record :type vals: doct :returns: :class:`nh_activity<activity.nh_activity>` id :rtype: int """ res = super(nh_activity, self).create(cr, uid, vals, context=context) if vals.get('location_id'): user_ids = self.pool['nh.activity.data'].get_activity_user_ids( cr, uid, res, context=context) if vals.get('data_model') == 'nh.clinical.spell': self.update_users(cr, uid, user_ids) else: self.write(cr, uid, res, {'user_ids': [[6, False, user_ids]]}) return res def write(self, cr, uid, ids, values, context=None): """ Extends Odoo's `write()` method. Also writes ``user_ids`` for responsible users of the activities' location. See class :mod:`nh_clinical_location<base.nh_clinical_location>`. :param ids: :class:`nh_activity<activity.nh_activity>` record ids :type ids: list :param vals: values to update records (may include ``location_id``) :type vals: dict :returns: ``True`` :rtype: bool """ if not values: values = {} res = super(nh_activity, self).write(cr, uid, ids, values, context=context) if 'location_id' in values: location_pool = self.pool['nh.clinical.location'] location = location_pool.read(cr, uid, values['location_id'], ['user_ids'], context=context) if location: self.write(cr, uid, ids, {'user_ids': [[6, False, location['user_ids']]]}, context=context) return res def cancel_open_activities(self, cr, uid, parent_id, model, context=None): """ Cancels all open activities of parent activity. :param parent_id: id of the parent activity :type parent_id: int :param model: model (type) of activity :type model: str :returns: ``True`` if all open activities are cancelled or if there are no open activities. Otherwise, ``False`` :rtype: bool """ domain = [('parent_id', '=', parent_id), ('data_model', '=', model), ('state', 'not in', ['completed', 'cancelled'])] open_activity_ids = self.search(cr, uid, domain, context=context) return all([ self.cancel(cr, uid, a, context=context) for a in open_activity_ids ]) def update_users(self, cr, uid, user_ids): """ Updates activities with the user_ids of users responsible for the activities' locations. :param user_ids: user ids. See class :class:`res_users<base.res_users>` :type user_ids: list :returns: ``True`` :rtype: bool """ if not user_ids: return True where_clause = "where user_id in (%s)" % list2sqlstr(user_ids) sql = """ delete from activity_user_rel {where_clause}; insert into activity_user_rel select activity_id, user_id from (select distinct on (activity.id, ulr.user_id) activity.id as activity_id, ulr.user_id from user_location_rel ulr inner join res_groups_users_rel gur on ulr.user_id = gur.uid inner join ir_model_access access on access.group_id = gur.gid and access.perm_responsibility = true inner join ir_model model on model.id = access.model_id inner join nh_activity activity on model.model = activity.data_model and activity.location_id = ulr.location_id and activity.state not in ('completed','cancelled') where not exists (select 1 from activity_user_rel where activity_id=activity.id and user_id=ulr.user_id )) pairs {where_clause} """.format(where_clause=where_clause) cr.execute(sql) self.update_spell_users(cr, uid, user_ids) return True def update_spell_users(self, cr, uid, user_ids=None): """ Updates spell activities with the user_ids of users responsible for parent locations of spell location. :param user_ids: user ids. See class :class:`res_users<base.res_users>` :type user_ids: list :returns: ``True`` :rtype: bool """ if not user_ids: return True where_clause = "where user_id in (%s)" % list2sqlstr(user_ids) sql = """ with recursive route(level, path, parent_id, id) as ( select 0, id::text, parent_id, id from nh_clinical_location where parent_id is null union select level + 1, path||','||location.id, location.parent_id, location.id from nh_clinical_location location join route on location.parent_id = route.id ), parent_location as ( select id as location_id, ('{'||path||'}')::int[] as ids from route order by path ) insert into activity_user_rel select activity_id, user_id from ( select distinct on (activity.id, ulr.user_id) activity.id as activity_id, ulr.user_id from user_location_rel ulr inner join res_groups_users_rel gur on ulr.user_id = gur.uid inner join ir_model_access access on access.group_id = gur.gid and access.perm_responsibility = true inner join ir_model model on model.id = access.model_id and model.model = 'nh.clinical.spell' inner join parent_location on parent_location.ids && array[ulr.location_id] inner join nh_activity activity on model.model = activity.data_model and activity.location_id = parent_location.location_id where not exists (select 1 from activity_user_rel where activity_id=activity.id and user_id=ulr.user_id )) pairs %s """ % where_clause cr.execute(sql) return True
class wh_move_line(osv.osv): _name = 'wh.move.line' MOVE_LINE_TYPE = [ ('out', u'出库'), ('in', u'入库'), ] MOVE_LINE_STATE = [ ('draft', u'草稿'), ('done', u'已审核'), ] def default_get(self, cr, uid, fields, context=None): res = super(wh_move_line, self).default_get(cr, uid, fields, context=context) context = context or {} if context.get('goods_id') and context.get('warehouse_id'): res.update({ 'goods_id': context.get('goods_id'), 'warehouse_id': context.get('warehouse_id') }) return res def get_real_price(self, cr, uid, ids, context=None): for line in self.browse(cr, uid, ids, context=context): return safe_division(line.subtotal, line.goods_qty) def name_get(self, cr, uid, ids, context=None): context = context or {} res = [] for line in self.browse(cr, uid, ids, context=context): if context.get('lot'): res.append((line.id, '%s-%s' % (line.lot, line.qty_remaining))) else: res.append((line.id, '%s-%s->%s(%s, %s%s)' % (line.move_id.name, line.warehouse_id.name, line.warehouse_dest_id.name, line.goods_id.name, str(line.goods_qty), line.uom_id.name))) return res def check_availability(self, cr, uid, ids, context=None): for line in self.browse(cr, uid, ids, context=context): if line.warehouse_dest_id.id == line.warehouse_id.id: raise osv.except_osv(u'错误', u'调出仓库不可以和调入仓库一样') return True def prev_action_done(self, cr, uid, ids, context=None): pass def action_done(self, cr, uid, ids, context=None): for line in self.browse(cr, uid, ids, context=context): line.check_availability() line.prev_action_done() line.write({ 'state': 'done', 'date': fields.datetime.now(cr, uid), }) def check_cancel(self, cr, uid, ids, context=None): pass def prev_action_cancel(self, cr, uid, ids, context=None): pass def action_cancel(self, cr, uid, ids, context=None): for line in self.browse(cr, uid, ids, context=context): line.check_cancel() line.prev_action_cancel() line.write({ 'state': 'draft', 'date': False, }) def _get_default_warehouse(self, cr, uid, context=None): context = context or {} if context.get('warehouse_type'): return self.pool.get('warehouse').get_warehouse_by_type( cr, uid, context.get('warehouse_type')) return False def _get_default_warehouse_dest(self, cr, uid, context=None): context = context or {} if context.get('warehouse_dest_type'): return self.pool.get('warehouse').get_warehouse_by_type( cr, uid, context.get('warehouse_dest_type')) return False def _get_subtotal_util(self, cr, uid, goods_qty, price, context=None): return goods_qty * price def onchange_price_by_out(self, cr, uid, ids, goods_id, warehouse_id, goods_qty, context=None): if goods_id and warehouse_id and goods_qty: subtotal, price = self.pool.get( 'goods').get_suggested_cost_by_warehouse(cr, uid, goods_id, warehouse_id, goods_qty, context=context) return { 'value': { 'price': price, 'subtotal': subtotal, } } return {} def onchange_price_by_in(self, cr, uid, ids, goods_qty, price, context=None): if goods_qty and price: return { 'value': { 'subtotal': self._get_subtotal_util(cr, uid, goods_qty, price, context=context) } } return {'value': {'subtotal': 0}} def onchange_goods_by_in(self, cr, uid, ids, goods_id, goods_qty, context=None): # TODO 需要计算保质期 if goods_id: goods = self.pool.get('goods').browse(cr, uid, goods_id, context=context) return { 'value': { 'uom_id': goods.uom_id.id, 'using_batch': goods.using_batch, 'force_batch_one': goods.force_batch_one, 'goods_qty': goods.force_batch_one and 1 or goods_qty, } } return {} def onchange_lot_out(self, cr, uid, ids, lot_id, context=None): if lot_id: lot = self.browse(cr, uid, lot_id, context=context) return { 'value': { 'warehouse_id': lot.warehouse_dest_id.id, 'goods_qty': lot.qty_remaining, 'lot_qty': lot.qty_remaining } } return {} def onchange_goods_by_out(self, cr, uid, ids, goods_id, warehouse_id, lot_id, goods_qty, compute_price=True, context=None): res = {} lot_domain = [('goods_id', '=', goods_id), ('state', '=', 'done'), ('lot', '!=', False), ('qty_remaining', '>', 0)] if goods_id: goods = self.pool.get('goods').browse(cr, uid, goods_id, context=context) res.update({ 'uom_id': goods.uom_id.id, 'using_batch': goods.using_batch, 'force_batch_one': goods.force_batch_one, }) if warehouse_id: if compute_price and goods_qty: subtotal, price = self.pool.get( 'goods').get_suggested_cost_by_warehouse( cr, uid, goods_id, warehouse_id, goods_qty, context=context) res.update({ 'price': price, 'subtotal': subtotal, }) lot_domain.append(('warehouse_dest_id', '=', warehouse_id)) if lot_id: lot = self.browse(cr, uid, lot_id, context=context) if warehouse_id and lot.warehouse_dest_id.id != warehouse_id: res.update({'lot_id': False}) if goods_id and lot.goods_id.id != goods_id: res.update({'lot_id': False}) return {'value': res, 'domain': {'lot_id': lot_domain}} def onchange_goods_by_internal(self, cr, uid, ids, goods_id, goods_qty, context=None): return self.onchange_goods_by_in(cr, uid, ids, goods_id, goods_qty, context=context) def unlink(self, cr, uid, ids, context=None): for line in self.browse(cr, uid, ids, context=context): if line.state == 'done': raise osv.except_osv(u'错误', u'不可以删除已经完成的明细') return super(wh_move_line, self).unlink(cr, uid, ids, context=context) _columns = { 'move_id': fields.many2one('wh.move', string=u'移库单', ondelete='cascade'), 'date': fields.datetime(u'完成日期', copy=False), 'type': fields.selection(MOVE_LINE_TYPE, u'类型'), 'state': fields.selection(MOVE_LINE_STATE, u'状态', copy=False), 'goods_id': fields.many2one('goods', string=u'产品', required=True, index=True), 'using_batch': fields.related('goods_id', 'using_batch', type='boolean', string=u'批次管理'), 'force_batch_one': fields.related('goods_id', 'force_batch_one', type='boolean', string=u'每批次数量为1'), 'lot': fields.char(u'序列号'), 'lot_id': fields.many2one('wh.move.line', u'序列号'), 'lot_qty': fields.related('lot_id', 'qty_remaining', type='float', string=u'序列号数量'), 'production_date': fields.date(u'生产日期'), 'shelf_life': fields.integer(u'保质期(天)'), 'valid_date': fields.date(u'有效期至'), 'uom_id': fields.many2one('uom', string=u'单位'), 'warehouse_id': fields.many2one('warehouse', string=u'调出仓库', required=True), 'warehouse_dest_id': fields.many2one('warehouse', string=u'调入仓库', required=True), 'goods_qty': fields.float(u'数量', digits_compute=dp.get_precision('Goods Quantity')), 'price': fields.float(u'单价', digits_compute=dp.get_precision('Accounting')), 'subtotal': fields.float(u'金额', digits_compute=dp.get_precision('Accounting')), 'note': fields.text(u'备注'), } _defaults = { 'type': lambda self, cr, uid, ctx=None: ctx.get('type'), 'goods_qty': lambda self, cr, uid, ctx=None: 1, 'state': 'draft', 'production_date': fields.date.context_today, 'warehouse_id': _get_default_warehouse, 'warehouse_dest_id': _get_default_warehouse_dest, }
class res_company(osv.osv): _name = "res.company" _description = 'Companies' _order = 'name' def _get_address_data(self, cr, uid, ids, field_names, arg, context=None): """ Read the 'address' functional fields. """ result = {} part_obj = self.pool.get('res.partner') for company in self.browse(cr, uid, ids, context=context): result[company.id] = {}.fromkeys(field_names, False) if company.partner_id: address_data = part_obj.address_get(cr, openerp.SUPERUSER_ID, [company.partner_id.id], adr_pref=['default']) if address_data['default']: address = part_obj.read(cr, openerp.SUPERUSER_ID, address_data['default'], field_names, context=context) for field in field_names: result[company.id][field] = address[field] or False return result def _set_address_data(self, cr, uid, company_id, name, value, arg, context=None): """ Write the 'address' functional fields. """ company = self.browse(cr, uid, company_id, context=context) if company.partner_id: part_obj = self.pool.get('res.partner') address_data = part_obj.address_get(cr, uid, [company.partner_id.id], adr_pref=['default']) address = address_data['default'] if address: part_obj.write(cr, uid, [address], {name: value or False}) else: part_obj.create(cr, uid, {name: value or False, 'parent_id': company.partner_id.id}, context=context) return True def _get_logo_web(self, cr, uid, ids, _field_name, _args, context=None): result = dict.fromkeys(ids, False) for record in self.browse(cr, uid, ids, context=context): size = (180, None) result[record.id] = image_resize_image(record.partner_id.image, size) return result def _get_companies_from_partner(self, cr, uid, ids, context=None): return self.pool['res.company'].search(cr, uid, [('partner_id', 'in', ids)], context=context) _columns = { 'name': fields.related('partner_id', 'name', string='Company Name', size=128, required=True, store=True, type='char'), 'parent_id': fields.many2one('res.company', 'Parent Company', select=True), 'child_ids': fields.one2many('res.company', 'parent_id', 'Child Companies'), 'partner_id': fields.many2one('res.partner', 'Partner', required=True), 'rml_header': fields.text('RML Header', required=True), 'rml_header1': fields.char('Company Tagline', size=200, help="Appears by default on the top right corner of your printed documents (report header)."), 'rml_header2': fields.text('RML Internal Header', required=True), 'rml_header3': fields.text('RML Internal Header for Landscape Reports', required=True), 'rml_footer': fields.text('Report Footer', help="Footer text displayed at the bottom of all reports."), 'rml_footer_readonly': fields.related('rml_footer', type='text', string='Report Footer', readonly=True), 'custom_footer': fields.boolean('Custom Footer', help="Check this to define the report footer manually. Otherwise it will be filled in automatically."), 'logo': fields.related('partner_id', 'image', string="Logo", type="binary"), 'logo_web': fields.function(_get_logo_web, string="Logo Web", type="binary", store={ 'res.company': (lambda s, c, u, i, x: i, ['partner_id'], 10), 'res.partner': (_get_companies_from_partner, ['image'], 10), }), 'currency_id': fields.many2one('res.currency', 'Currency', required=True), 'currency_ids': fields.one2many('res.currency', 'company_id', 'Currency'), 'user_ids': fields.many2many('res.users', 'res_company_users_rel', 'cid', 'user_id', 'Accepted Users'), 'account_no':fields.char('Account No.', size=64), 'street': fields.function(_get_address_data, fnct_inv=_set_address_data, size=128, type='char', string="Street", multi='address'), 'street2': fields.function(_get_address_data, fnct_inv=_set_address_data, size=128, type='char', string="Street2", multi='address'), 'zip': fields.function(_get_address_data, fnct_inv=_set_address_data, size=24, type='char', string="Zip", multi='address'), 'city': fields.function(_get_address_data, fnct_inv=_set_address_data, size=24, type='char', string="City", multi='address'), 'state_id': fields.function(_get_address_data, fnct_inv=_set_address_data, type='many2one', domain="[('country_id', '=', country_id)]", relation='res.country.state', string="Fed. State", multi='address'), 'bank_ids': fields.one2many('res.partner.bank','company_id', 'Bank Accounts', help='Bank accounts related to this company'), 'country_id': fields.function(_get_address_data, fnct_inv=_set_address_data, type='many2one', relation='res.country', string="Country", multi='address'), 'email': fields.function(_get_address_data, fnct_inv=_set_address_data, size=64, type='char', string="Email", multi='address'), 'phone': fields.function(_get_address_data, fnct_inv=_set_address_data, size=64, type='char', string="Phone", multi='address'), 'fax': fields.function(_get_address_data, fnct_inv=_set_address_data, size=64, type='char', string="Fax", multi='address'), 'website': fields.related('partner_id', 'website', string="Website", type="char", size=64), 'vat': fields.related('partner_id', 'vat', string="Tax ID", type="char", size=32), 'company_registry': fields.char('Company Registry', size=64), 'paper_format': fields.selection([('a4', 'A4'), ('us_letter', 'US Letter')], "Paper Format", required=True), } _sql_constraints = [ ('name_uniq', 'unique (name)', 'The company name must be unique !') ] def onchange_footer(self, cr, uid, ids, custom_footer, phone, fax, email, website, vat, company_registry, bank_ids, context=None): if custom_footer: return {} # first line (notice that missing elements are filtered out before the join) res = ' | '.join(filter(bool, [ phone and '%s: %s' % (_('Phone'), phone), fax and '%s: %s' % (_('Fax'), fax), email and '%s: %s' % (_('Email'), email), website and '%s: %s' % (_('Website'), website), vat and '%s: %s' % (_('TIN'), vat), company_registry and '%s: %s' % (_('Reg'), company_registry), ])) # second line: bank accounts res_partner_bank = self.pool.get('res.partner.bank') account_data = self.resolve_2many_commands(cr, uid, 'bank_ids', bank_ids, context=context) account_names = res_partner_bank._prepare_name_get(cr, uid, account_data, context=context) if account_names: title = _('Bank Accounts') if len(account_names) > 1 else _('Bank Account') res += '\n%s: %s' % (title, ', '.join(name for id, name in account_names)) return {'value': {'rml_footer': res, 'rml_footer_readonly': res}} def on_change_country(self, cr, uid, ids, country_id, context=None): currency_id = self._get_euro(cr, uid, context=context) if country_id: currency_id = self.pool.get('res.country').browse(cr, uid, country_id, context=context).currency_id.id return {'value': {'currency_id': currency_id}} def _search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None): if context is None: context = {} if context.get('user_preference'): # We browse as superuser. Otherwise, the user would be able to # select only the currently visible companies (according to rules, # which are probably to allow to see the child companies) even if # she belongs to some other companies. user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context) cmp_ids = list(set([user.company_id.id] + [cmp.id for cmp in user.company_ids])) return cmp_ids return super(res_company, self)._search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count, access_rights_uid=access_rights_uid) def _company_default_get(self, cr, uid, object=False, field=False, context=None): """ Check if the object for this company have a default value """ if not context: context = {} proxy = self.pool.get('multi_company.default') args = [ ('object_id.model', '=', object), ('field_id', '=', field), ] ids = proxy.search(cr, uid, args, context=context) user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context) for rule in proxy.browse(cr, uid, ids, context): if eval(rule.expression, {'context': context, 'user': user}): return rule.company_dest_id.id return user.company_id.id @tools.ormcache() def _get_company_children(self, cr, uid=None, company=None): if not company: return [] ids = self.search(cr, uid, [('parent_id','child_of',[company])]) return ids def _get_partner_hierarchy(self, cr, uid, company_id, context=None): if company_id: parent_id = self.browse(cr, uid, company_id)['parent_id'] if parent_id: return self._get_partner_hierarchy(cr, uid, parent_id.id, context) else: return self._get_partner_descendance(cr, uid, company_id, [], context) return [] def _get_partner_descendance(self, cr, uid, company_id, descendance, context=None): descendance.append(self.browse(cr, uid, company_id).partner_id.id) for child_id in self._get_company_children(cr, uid, company_id): if child_id != company_id: descendance = self._get_partner_descendance(cr, uid, child_id, descendance) return descendance # # This function restart the cache on the _get_company_children method # def cache_restart(self, cr): self._get_company_children.clear_cache(self) def create(self, cr, uid, vals, context=None): if not vals.get('name', False) or vals.get('partner_id', False): self.cache_restart(cr) return super(res_company, self).create(cr, uid, vals, context=context) obj_partner = self.pool.get('res.partner') partner_id = obj_partner.create(cr, uid, {'name': vals['name'], 'is_company':True, 'image': vals.get('logo', False)}, context=context) vals.update({'partner_id': partner_id}) self.cache_restart(cr) company_id = super(res_company, self).create(cr, uid, vals, context=context) obj_partner.write(cr, uid, partner_id, {'company_id': company_id}, context=context) return company_id def write(self, cr, uid, ids, values, context=None): self.cache_restart(cr) return super(res_company, self).write(cr, uid, ids, values, context=context) def _get_euro(self, cr, uid, context=None): rate_obj = self.pool.get('res.currency.rate') rate_id = rate_obj.search(cr, uid, [('rate', '=', 1)], context=context) return rate_id and rate_obj.browse(cr, uid, rate_id[0], context=context).currency_id.id or False def _get_logo(self, cr, uid, ids): return open(os.path.join( tools.config['root_path'], 'addons', 'base', 'res', 'res_company_logo.png'), 'rb') .read().encode('base64') _header = """ <header> <pageTemplate> <frame id="first" x1="28.0" y1="28.0" width="%s" height="%s"/> <pageGraphics> <fill color="black"/> <stroke color="black"/> <setFont name="DejaVu Sans" size="8"/> <drawString x="%s" y="%s"> [[ formatLang(time.strftime("%%Y-%%m-%%d"), date=True) ]] [[ time.strftime("%%H:%%M") ]]</drawString> <setFont name="DejaVu Sans Bold" size="10"/> <drawCentredString x="%s" y="%s">[[ company.partner_id.name ]]</drawCentredString> <stroke color="#000000"/> <lines>%s</lines> </pageGraphics> </pageTemplate> </header>""" _header2 = _header % (539, 772, "1.0cm", "28.3cm", "11.1cm", "28.3cm", "1.0cm 28.1cm 20.1cm 28.1cm") _header3 = _header % (786, 525, 25, 555, 440, 555, "25 550 818 550") def _get_header(self,cr,uid,ids): try : header_file = tools.file_open(os.path.join('base', 'report', 'corporate_rml_header.rml')) try: return header_file.read() finally: header_file.close() except: return self._header_a4 _header_main = """ <header> <pageTemplate> <frame id="first" x1="1.3cm" y1="3.0cm" height="%s" width="19.0cm"/> <stylesheet> <paraStyle name="main_footer" fontName="DejaVu Sans" fontSize="8.0" alignment="CENTER"/> <paraStyle name="main_header" fontName="DejaVu Sans" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> </stylesheet> <pageGraphics> <!-- You Logo - Change X,Y,Width and Height --> <image x="1.3cm" y="%s" height="40.0" >[[ company.logo or removeParentNode('image') ]]</image> <setFont name="DejaVu Sans" size="8"/> <fill color="black"/> <stroke color="black"/> <!-- page header --> <lines>1.3cm %s 20cm %s</lines> <drawRightString x="20cm" y="%s">[[ company.rml_header1 ]]</drawRightString> <drawString x="1.3cm" y="%s">[[ company.partner_id.name ]]</drawString> <place x="1.3cm" y="%s" height="1.8cm" width="15.0cm"> <para style="main_header">[[ display_address(company.partner_id) or '' ]]</para> </place> <drawString x="1.3cm" y="%s">Phone:</drawString> <drawRightString x="7cm" y="%s">[[ company.partner_id.phone or '' ]]</drawRightString> <drawString x="1.3cm" y="%s">Mail:</drawString> <drawRightString x="7cm" y="%s">[[ company.partner_id.email or '' ]]</drawRightString> <lines>1.3cm %s 7cm %s</lines> <!-- left margin --> <rotate degrees="90"/> <fill color="grey"/> <drawString x="2.65cm" y="-0.4cm">generated by OpenERP.com</drawString> <fill color="black"/> <rotate degrees="-90"/> <!--page bottom--> <lines>1.2cm 2.65cm 19.9cm 2.65cm</lines> <place x="1.3cm" y="0cm" height="2.55cm" width="19.0cm"> <para style="main_footer">[[ company.rml_footer ]]</para> <para style="main_footer">Contact : [[ user.name ]] - Page: <pageNumber/></para> </place> </pageGraphics> </pageTemplate> </header>""" _header_a4 = _header_main % ('21.7cm', '27.7cm', '27.7cm', '27.7cm', '27.8cm', '27.3cm', '25.3cm', '25.0cm', '25.0cm', '24.6cm', '24.6cm', '24.5cm', '24.5cm') _header_letter = _header_main % ('20cm', '26.0cm', '26.0cm', '26.0cm', '26.1cm', '25.6cm', '23.6cm', '23.3cm', '23.3cm', '22.9cm', '22.9cm', '22.8cm', '22.8cm') def onchange_paper_format(self, cr, uid, ids, paper_format, context=None): if paper_format == 'us_letter': return {'value': {'rml_header': self._header_letter}} return {'value': {'rml_header': self._header_a4}} _defaults = { 'currency_id': _get_euro, 'paper_format': 'a4', 'rml_header':_get_header, 'rml_header2': _header2, 'rml_header3': _header3, 'logo':_get_logo } _constraints = [ (osv.osv._check_recursion, 'Error! You can not create recursive companies.', ['parent_id']) ]
if store['website']['is_default'] == '1': store_info['website_id'] = int(store['website']['website_id']) store_info['store_id'] = view_pool._get_store_view(cr, uid, store, context) break; return store_info _columns = { 'name': fields.char('Base URL', required=True, size=255 ,select=True), 'instance_name': fields.char("Instance Name",size=64,select=True), 'user':fields.char('API User Name', required=True, size=100), 'pwd':fields.char('API Password',required=True, size=100), 'status':fields.char('Connection Status',readonly=True, size=255), 'active':fields.boolean('Active'), 'store_id':fields.many2one('magento.store.view', 'Default Magento Store'), 'group_id':fields.related('store_id', 'group_id', type="many2one", relation="magento.store", string="Default Store", readonly=True, store=True), 'website_id':fields.related('group_id', 'website_id', type="many2one", relation="magento.website", string="Default Magento Website", readonly=True), 'credential':fields.boolean('Show/Hide Credentials Tab', help="If Enable, Credentials tab will be displayed, " "And after filling the details you can hide the Tab."), 'auto_invoice':fields.boolean('Auto Invoice', help="If Enabled, Order will automatically Invoiced on Magento " " when Odoo order Get invoiced."), 'auto_ship':fields.boolean('Auto Shipment', help="If Enabled, Order will automatically shipped on Magento" " when Odoo order Get Delivered."), 'notify':fields.boolean('Notify Customer By Email', help="If True, customer will be notify" "during order shipment and invoice, else it won't."), 'language':fields.selection(_lang_get, "Default Language", help="Selected language is loaded in the system, " "all documents related to this contact will be synched in this language."),
class Message(osv.Model): #TODO: turn off for data import only _log_access = True _name = "message.message" _order = "write_date desc,sequence" _description = 'UPDIS Message' _inherit = ['mail.thread', 'ir.needaction_mixin'] def _default_fbbm(self, cr, uid, context=None): employee_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)]) if employee_ids: return self.pool.get('hr.employee').browse(cr, uid, employee_ids[0]).department_id.name def _default_department(self, cr, uid, context=None): employee_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)]) if employee_ids: return self.pool.get('hr.employee').browse(cr, uid, employee_ids[0]).department_id.id def _get_image(self, cr, uid, ids, name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): result[obj.id] = tools.image_get_resized_images(obj.image) return result def _set_image(self, cr, uid, id, field_name, value, args, context=None): return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context) def _get_name_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): result[obj.id] = obj.is_display_name and obj.create_uid.name or u'匿名用户' return result def _get_message_meta_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): message_meta = '' if obj.category_id.message_meta: env = Env(cr, uid, 'message.message', [obj.id]) message_meta = eval(obj.category_id.message_meta, env, nocopy=True) result[obj.id] = message_meta return result def _get_category_message_title_meta_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): category_message_title_meta = '' if obj.category_id.category_message_title_meta: env = Env(cr, uid, 'message.message', [obj.id]) category_message_title_meta = eval(obj.category_id.category_message_title_meta, env, nocopy=True) result[obj.id] = category_message_title_meta return result def _get_create_date_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): if obj.create_date: create_date_display = datetime.datetime.strptime(obj.create_date, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(hours=8) result[obj.id] = create_date_display.strftime('%Y-%m-%d %H:%M:%S') return result def _get_write_date_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): if obj.write_date: write_date_display = datetime.datetime.strptime(obj.write_date, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(hours=8) result[obj.id] = write_date_display.strftime('%Y-%m-%d %H:%M:%S') return result def _get_shorten_name(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): size = obj.category_id.category_message_title_size title = len(obj.name) > size and obj.name[:size] + '...' or obj.name result[obj.id] = title return result def _get_message_list_meta_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): message_meta = '' if obj.category_id.message_meta: env = Env(cr, uid, 'message.message', [obj.id]) message_meta = eval(obj.category_id.phone_message_list_meta, env, nocopy=True) result[obj.id] = message_meta return result def _get_message_detail_meta_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): category_message_title_meta = '' if obj.category_id.category_message_title_meta: env = Env(cr, uid, 'message.message', [obj.id]) category_message_title_meta = eval(obj.category_id.phone_message_detail_meta, env, nocopy=True) result[obj.id] = category_message_title_meta return result def _get_vote_count(self, cr, uid, ids, field_name, args, context=None): """ Either way, it must return a dictionary of values of the form {'id_1_': 'value_1_', 'id_2_': 'value_2_',...}. If multi is set, then field_name is replaced by field_names: a list of the field names that should be calculated. Each value in the returned dictionary is also a dictionary from field name to value. For example, if the fields 'name', and 'age' are both based on the vital_statistics function, then the return value of vital_statistics might look like this when ids is [1, 2, 5]: { 1: {'name': 'Bob', 'age': 23}, 2: {'name': 'Sally', 'age', 19}, 5: {'name': 'Ed', 'age': 62} } """ result = dict.fromkeys(ids, False) message_vote_obj = self.pool.get('message.vote') for message in self.browse(cr, uid, ids, context=context): vote_like_count = len(message_vote_obj.search(cr, SUPERUSER_ID, [('message_id', '=', message.id), ('up', '=', True)], context)) vote_unlike_count = len(message_vote_obj.search(cr, SUPERUSER_ID, [('message_id', '=', message.id), ('up', '=', False)], context)) result[message.id] = { 'vote_like': vote_like_count, 'vote_unlike': vote_unlike_count, } return result _columns = { 'name': fields.char("Title", size=128, required=True), 'shorten_name': fields.function(_get_shorten_name, type="char", size=256, string="Shorten title"), 'message_meta_display': fields.function(_get_message_meta_display, type="char", size=256, string="Meta"), 'category_message_title_meta_display': fields.function(_get_category_message_title_meta_display, type="char", size=256, string="Category meta"), 'message_list_meta_display': fields.function(_get_message_list_meta_display, type="char", size=256, string="Phone Message List Meta"), 'message_detail_meta_display': fields.function(_get_message_detail_meta_display, type="char", size=256, string="Phone Message Detail meta"), 'category_id': fields.many2one('message.category', 'Category', required=True, change_default=True), 'content': fields.text("Content"), 'sequence': fields.integer("Display Sequence"), 'is_display_name': fields.boolean('Display name?'), 'fbbm': fields.char('Publisher', size=128), 'read_times': fields.integer("Read Times"), 'expire_date': fields.date('Expire Date'), 'create_date': fields.datetime('Created on', select=True ,readonly=True), 'department_id': fields.many2one("hr.department", "Department", domain=[('deleted', '=', False)]), 'create_uid': fields.many2one('res.users', 'Author', select=True, readonly=True), 'write_date': fields.datetime('Modification date', select=True , readonly=True), 'write_uid': fields.many2one('res.users', 'Last Contributor', select=True , readonly=True), 'source': fields.char("Message Source", size=128), 'name_for_display': fields.function(_get_name_display, type="char", size=64, string="Name"), 'sms_receiver_ids': fields.many2many("hr.employee", "message_hr_employee_rel", "message_id", "hr_employee_id", "SMS Receiver"), 'sms': fields.text('SMS', size=140), 'is_allow_send_sms': fields.related('category_id', 'is_allow_send_sms', type="boolean", string="Allow send SMS?"), 'is_allow_sms_receiver': fields.related('category_id', 'is_allow_send_sms', type="boolean", string="Allow specify sms receiver?"), 'category_id_name': fields.related('category_id', 'name', type="char", string="category name"), 'category_id_is_anonymous_allowed': fields.related('category_id', 'is_anonymous_allowed', type="boolean", string="category is anonymous allowed"), 'category_id_is_allowed_edit_sms_text': fields.related('category_id', 'is_allowed_edit_sms_text', type="boolean", string="category is allowed edit sms text"), 'create_date_display': fields.function(_get_create_date_display, type="datetime", string="Create Date Display", readonly=True), 'write_date_display': fields.function(_get_write_date_display, type="datetime", string="Write Date Display", readonly=True), 'vote_user_ids': fields.many2many('res.users', 'message_vote', 'message_id', 'user_id', string='Votes', help='Users that voted for this message'), 'vote_like': fields.function(_get_vote_count, type="integer", string='Like', multi='vote'), 'vote_unlike': fields.function(_get_vote_count, type='integer', string='Unlike', multi='vote'), } _defaults = { 'fbbm': _default_fbbm, 'department_id': _default_department, 'is_display_name': True, } def onchange_category(self, cr, uid, ids, category_id, context=None): ret = {'value': {}} if category_id: message_category = self.pool.get('message.category').browse(cr, uid, category_id) sms_vals = { 'is_allow_send_sms': message_category.is_allow_send_sms, 'is_allow_sms_receiver': message_category.is_allow_sms_receiver, 'sms_receiver_ids': [x.id for x in message_category.default_sms_receiver_ids], 'category_id_name': message_category.name, 'category_id_is_anonymous_allowed': message_category.is_anonymous_allowed, 'category_id_is_allowed_edit_sms_text': message_category.is_allowed_edit_sms_text, } ret['value'].update(sms_vals) return ret #abandon def onchange_name(self, cr, uid, ids, name, sms, context=None): ret = {'value': {}} if not sms: name_vals = { 'sms': name, } if not len(ids): ret['value'].update(name_vals) return ret def create(self, cr, uid, vals, context=None): if context is None: context = {'mail_create_nolog': True} else: context.update({'mail_create_nolog': True}) if self._log_access is True: if not vals['category_id_is_allowed_edit_sms_text']: vals['sms'] = vals['name'] mid = super(Message, self).create(cr, uid, vals, context) sms = self.pool.get('sms.sms') message = self.pool.get('message.message').browse(cr, uid, mid, context=context) if message.is_allow_send_sms: to = ','.join( [rid.mobile_phone.strip() for rid in message.sms_receiver_ids if rid.mobile_phone and rid.mobile_phone.strip()]) if to: content = message.sms and message.category_id.name + ':' + message.sms or message.category_id.name + ':' + message.name sid = sms.create(cr, uid, {'to': to, 'content': content, 'model': 'message.message', 'res_id': mid}, context=context) else: mid = super(Message, self).create(cr, uid, vals, context) return mid def write(self, cr, uid, ids, vals, context=None): #get message old position if self._log_access is True: TYPE = [] if not (len(vals) == 1 and 'read_times' in vals.keys()): messages_old = self.pool.get('message.message').browse(cr, 1, ids, context=context) for message_old in messages_old: if message_old.category_id.display_position == 'shortcut': TYPE.append('0') if message_old.category_id.display_position == 'content_left': TYPE.append('1') if message_old.category_id.display_position == 'content_right': TYPE.append('2') TYPE = set(TYPE) super(Message, self).write(cr, uid, ids, vals, context=context) NEW_TYPE = [] #refresh cms page if not (len(vals) == 1 and 'read_times' in vals.keys()): #get message new position messages = self.pool.get('message.message').browse(cr, 1, ids, context=context) for message in messages: if message.category_id.display_position == 'shortcut': NEW_TYPE.append('0') if message.category_id.display_position == 'content_left': NEW_TYPE.append('1') if message.category_id.display_position == 'content_right': NEW_TYPE.append('2') NEW_TYPE = set(NEW_TYPE) #if old and new position is different refresh both. for v in (TYPE | NEW_TYPE): fresh_old = CMSFresh(v) fresh_old.start() else: super(Message, self).write(cr, uid, ids, vals, context=context) return True def unlink(self, cr, uid, ids, context=None): #get old position TYPE = [] messages_old = self.pool.get('message.message').browse(cr, 1, ids, context=context) for message_old in messages_old: if message_old.category_id.display_position == 'shortcut': TYPE.append('0') if message_old.category_id.display_position == 'content_left': TYPE.append('1') if message_old.category_id.display_position == 'content_right': TYPE.append('2') super(Message, self).unlink(cr, uid, ids, context=None) TYPE = set(TYPE) #refresh for v in TYPE: fresh_old = CMSFresh(v) fresh_old.start() return True def vote_like(self, cr, uid, user_id, message_id, context=None): message_vote_obj = self.pool.get('message.vote') message = self.read(cr, SUPERUSER_ID, int(message_id), ['vote_user_ids'], context=context) new_has_voted = not (user_id in message.get('vote_user_ids')) if new_has_voted: message_vote_obj.create(cr, SUPERUSER_ID, {'message_id': message.get('id'), 'user_id': user_id, 'up': True}) else: self.write(cr, SUPERUSER_ID, [message.get('id')], {'vote_user_ids': [(3, user_id)]}, context=context) message_vote_obj.create(cr, SUPERUSER_ID, {'message_id': message.get('id'), 'user_id': user_id, 'up': True}) return new_has_voted or False def vote_unlike(self, cr, uid, user_id, message_id, context=None): message_vote_obj = self.pool.get('message.vote') message = self.read(cr, SUPERUSER_ID, int(message_id), ['vote_user_ids'], context=context) new_has_voted = not (user_id in message.get('vote_user_ids')) if new_has_voted: message_vote_obj.create(cr, SUPERUSER_ID, {'message_id': message.get('id'), 'user_id': user_id, 'up': False}) else: self.write(cr, SUPERUSER_ID, [message.get('id')], {'vote_user_ids': [(3, user_id)]}, context=context) message_vote_obj.create(cr, SUPERUSER_ID, {'message_id': message.get('id'), 'user_id': user_id, 'up': False}) return new_has_voted or False
'name':fields.char('Memo', size=256), 'log_ref': fields.char('Check-log Ref', size=128), <<<<<<< HEAD # 'origin' : fields.char('Origin', size=128), 'check_status' :fields.selection([('void','Voided'),('print','Printed'),('re_print','Re-Printed'),('clear','Cleared')]), 'chk_seq': fields.char("Check Number", size=64, readonly=True), 'invoice_ids': fields.one2many('account.invoice', 'voucher_id', 'Invoices', ondelete='cascade'), 'jtype':fields.related('journal_id','type', string="Journal Type", type='selection', selection=[('sale', 'Sale'),('sale_refund','Sale Refund'), ('purchase', 'Purchase'), ('purchase_refund','Purchase Refund'), ('cash', 'Cash'), ('bank', 'Bank and Checks'), ('general', 'General'), ('situation', 'Opening/Closing Situation')],), } ======= # 'origin' : fields.char('Origin', size=128), 'check_status' :fields.selection([('void','Voided'),('print','Printed'),('re_print','Re-Printed'),('clear','Cleared')]), 'chk_seq': fields.char("Check Number", size=64, readonly=True), 'invoice_ids': fields.one2many('account.invoice', 'voucher_id', 'Invoices', ondelete='cascade'), 'reference': fields.char('Origin', size=64, readonly=True, states={'draft':[('readonly',False)]}, help="Source document which generated the payment."), 'jtype':fields.related('journal_id','type', string="Journal Type", type='selection', selection=[('sale', 'Sale'),('sale_refund','Sale Refund'), ('purchase', 'Purchase'), ('purchase_refund','Purchase Refund'), ('cash', 'Cash'), ('bank', 'Bank and Checks'), ('general', 'General'), ('situation', 'Opening/Closing Situation')],), } >>>>>>> c1979f64b3360c86d60e00c92be0271d89f97f2d def print_checks(self, cr, uid, ids, context=None): check_state = self.browse(cr, uid, ids[0],context=None).check_status view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_check_writing', 'view_account_check_write') view_id = view_ref and view_ref[1] or False, context.update({'active_ids':ids, 'check_state': check_state}) return { 'type': 'ir.actions.act_window', 'name': 'Print Checks', 'view_mode': 'form', 'view_type': 'form', 'view_id': view_id, 'res_model': 'account.check.write',
class hr_so_project(osv.osv_memory): _name = 'hr.sign.out.project' _description = 'Sign Out By Project' _columns = { 'account_id': fields.many2one('account.analytic.account', 'Project / Analytic Account', domain=[('type', 'in', ['normal', 'contract'])]), 'info': fields.char('Work Description', required=True), 'date_start': fields.datetime('Starting Date', readonly=True), 'date': fields.datetime('Closing Date'), 'analytic_amount': fields.float('Minimum Analytic Amount'), 'name': fields.char('Employee\'s Name', required=True, readonly=True), 'state': fields.related('emp_id', 'state', string='Current Status', type='selection', selection=[('present', 'Present'), ('absent', 'Absent')], required=True, readonly=True), 'server_date': fields.datetime('Current Date', required=True, readonly=True), 'emp_id': fields.many2one('hr.employee', 'Employee ID') } def _get_empid(self, cr, uid, context=None): emp_obj = self.pool.get('hr.employee') emp_ids = emp_obj.search(cr, uid, [('user_id', '=', uid)], context=context) if emp_ids: for employee in emp_obj.browse(cr, uid, emp_ids, context=context): return { 'name': employee.name, 'state': employee.state, 'emp_id': emp_ids[0], 'server_date': time.strftime('%Y-%m-%d %H:%M:%S') } def _get_empid2(self, cr, uid, context=None): res = self._get_empid(cr, uid, context=context) cr.execute( 'select name,action from hr_attendance where employee_id=%s order by name desc limit 1', (res['emp_id'], )) res['server_date'] = time.strftime('%Y-%m-%d %H:%M:%S') date_start = cr.fetchone() if date_start: res['date_start'] = date_start[0] return res def default_get(self, cr, uid, fields_list, context=None): res = super(hr_so_project, self).default_get(cr, uid, fields_list, context=context) res.update(self._get_empid2(cr, uid, context=context)) return res def _write(self, cr, uid, data, emp_id, context=None): timesheet_obj = self.pool.get('hr.analytic.timesheet') emp_obj = self.pool.get('hr.employee') if context is None: context = {} hour = (time.mktime( time.strptime(data['date'] or time.strftime('%Y-%m-%d %H:%M:%S'), '%Y-%m-%d %H:%M:%S')) - time.mktime( time.strptime(data['date_start'], '%Y-%m-%d %H:%M:%S'))) / 3600.0 minimum = data['analytic_amount'] if minimum: hour = round(round((hour + minimum / 2) / minimum) * minimum, 2) res = timesheet_obj.default_get(cr, uid, ['product_id', 'product_uom_id'], context=context) if not res['product_uom_id']: raise osv.except_osv( _('User Error!'), _('Please define cost unit for this employee.')) up = timesheet_obj.on_change_unit_amount( cr, uid, False, res['product_id'], hour, False, res['product_uom_id'])['value'] res['name'] = data['info'] res['account_id'] = data['account_id'].id res['unit_amount'] = hour emp_journal = emp_obj.browse(cr, uid, emp_id, context=context).journal_id res['journal_id'] = emp_journal and emp_journal.id or False res.update(up) up = timesheet_obj.on_change_account_id(cr, uid, [], res['account_id']).get( 'value', {}) res.update(up) return timesheet_obj.create(cr, uid, res, context=context) def sign_out_result_end(self, cr, uid, ids, context=None): emp_obj = self.pool.get('hr.employee') for data in self.browse(cr, uid, ids, context=context): emp_id = data.emp_id.id emp_obj.attendance_action_change(cr, uid, [emp_id], { 'action': 'sign_out', 'action_date': data.date }) self._write(cr, uid, data, emp_id, context=context) return {'type': 'ir.actions.act_window_close'} def sign_out_result(self, cr, uid, ids, context=None): emp_obj = self.pool.get('hr.employee') for data in self.browse(cr, uid, ids, context=context): emp_id = data.emp_id.id emp_obj.attendance_action_change(cr, uid, [emp_id], { 'action': 'action', 'action_date': data.date }) self._write(cr, uid, data, emp_id, context=context) return {'type': 'ir.actions.act_window_close'}
def _setup_bound_dimension(cls, dimension, columns, defaults, orm_name, name, bases, nmspc): """Bind a dimension to the model, creating a code for each record.""" if dimension is True: dimension = {} elif isinstance(dimension, basestring): dimension = {"name": dimension} dimension_name = dimension.get("name", None) if dimension_name is None: dimension_name = nmspc.get("_description", False) or orm_name column = dimension.get("column", "analytic_id") ref_module = dimension.get("ref_module", "") ref_id = dimension.get("ref_id", None) if ref_id is None: ref_id = orm_name.replace(".", "_") + "_analytic_dimension_id" # To use an inherited, renamed parent field, you have to give its name. sync_parent = dimension.get("sync_parent", False) if sync_parent is True: sync_parent = nmspc.get("_parent_name", "parent_id") rel_name = dimension.get("rel_name", tuple()) if rel_name is True: rel_name = u"Name" if isinstance(rel_name, basestring): rel_name = (rel_name, "name") rel_description = dimension.get("rel_description", tuple()) if rel_description is True: rel_description = u"Description" if isinstance(rel_description, basestring): rel_description = (rel_description, "description") rel_active = dimension.get("rel_active", tuple()) if rel_active is True: rel_active = u"Active" if isinstance(rel_active, basestring): rel_active = (rel_active, "active") rel_view_type = dimension.get("rel_view_type", tuple()) if rel_view_type is True: rel_view_type = u"View type" if isinstance(rel_view_type, basestring): rel_view_type = (rel_view_type, "view_type") rel_disabled_per_company = dimension.get("rel_disabled_per_company", tuple()) if rel_disabled_per_company is True: rel_disabled_per_company = u"Disabled in my company" if isinstance(rel_disabled_per_company, basestring): rel_disabled_per_company = (rel_disabled_per_company, "disabled_per_company") # By default, only use inherits if we can be sure there is no conflict # on the required fields 'name' and 'nd_id'. # There can still be conflicts on analytic_code's optional fields. use_inherits = dimension.get("use_inherits", None) if use_inherits is None: use_inherits = not ( any(col in columns for col in ("name", "nd_id")) or nmspc.get("_inherits", False) or nmspc.get("_inherit", False) ) use_code_name_methods = dimension.get("use_code_name_methods", False) code_ref_ids = dimension.get("code_ref_ids", False) if code_ref_ids is True: code_ref_ids = ref_id code_ref_module = dimension.get("code_ref_module", "") if use_inherits: inherits = nmspc.get("_inherits", {}) inherits["analytic.code"] = column nmspc["_inherits"] = inherits # Default column for the underlying analytic code. if column not in columns: columns[column] = fields.many2one( "analytic.code", u"Bound Analytic Code", required=True, ondelete="restrict" ) rel_cols = [ cols for cols in [ rel_name + ("name", "char", True, ""), rel_description + ("description", "char", False, ""), rel_active + ("active", "boolean", False, True), rel_view_type + ("view_type", "boolean", False, False), ] if len(cols) == 6 ] if rel_cols: # NOT a method nor a class member. 'self' is the analytic_code OSV. def _record_from_code_id(self, cr, uid, ids, context=None): """Get the entries to update from the modified codes.""" osv = self.pool.get(orm_name) domain = [(column, "in", ids)] return osv.search(cr, uid, domain, context=context) for string, model_col, code_col, dtype, req, default in rel_cols: columns[model_col] = fields.related( column, code_col, string=string, type=dtype, relation="analytic.code", required=req, store={"analytic.code": (_record_from_code_id, [code_col], 10)}, ) if model_col not in defaults: defaults[model_col] = default # In order to preserve inheritance, possible overrides, and OEMetaSL's # expected behavior, work on a new class that inherits the given bases, # then make our model class inherit from this class. superclass_name = "_{name}_SuperDimension".format(name=name) # Set _register to False in order to prevent its instantiation. superclass = type(superclass_name, bases, {"_register": False}) @AddMethod(superclass) def __init__(self, pool, cr): """Load or create the analytic dimension bound to the model.""" super(superclass, self).__init__(pool, cr) data_osv = self.pool["ir.model.data"] try: self._bound_dimension_id = data_osv.get_object_reference(cr, SUPERUSER_ID, ref_module, ref_id)[1] except ValueError: vals = {"name": dimension_name, "validated": True} self._bound_dimension_id = data_osv._update( cr, SUPERUSER_ID, "analytic.dimension", ref_module, vals, xml_id=ref_id, noupdate=True ) if code_ref_ids: prefix = config.get_misc("analytic", "code_ref_prefix", False) # This function is called as a method and can be overridden. @AddMethod(superclass) def _generate_code_ref_id(self, cr, uid, ids, context=None): data_osv = self.pool["ir.model.data"] records = self.browse(cr, uid, ids, context=None) if not isinstance(records, list): records = [records] for record in records: code = record[column] code_ref_id_builder = [prefix] if prefix else [] if "company_id" in record and record.company_id: code_ref_id_builder.append(record.company_id.code) code_ref_id_builder.append("ANC") code_ref_id_builder.append(code_ref_ids) code_ref_id_builder.append(code.name) vals = { "name": "_".join(code_ref_id_builder), "module": code_ref_module, "model": "analytic.code", "res_id": code.id, } data_osv.create(cr, uid, vals, context=context) @AddMethod(superclass) def create(self, cr, uid, vals, context=None): """Create the analytic code.""" code_vals = {} if sync_parent: cp = self._get_code_parent(cr, uid, vals, context=context) if cp is not None: code_vals["code_parent_id"] = cp # Direct changes to the 'bound analytic code' field are ignored # unless the 'force_code_id' context key is passed as True. force_code_id = vals.pop(column, False) if context and context.get("force_code_id", False) == True: self._force_code(cr, uid, force_code_id, code_vals, context) vals[column] = force_code_id else: if use_inherits: code_vals.update(vals) else: code_vals["name"] = vals.get("name") # OpenERP bug: related fields do not work properly on creation. for rel in rel_cols: model_col, code_col = rel[1:3] if model_col in vals: code_vals[code_col] = vals[model_col] elif model_col in self._defaults: code_vals[code_col] = self._defaults[model_col] # We have to create the code separately, even with inherits. code_osv = self.pool["analytic.code"] code_vals["nd_id"] = self._bound_dimension_id code_id = code_osv.create(cr, uid, code_vals, context=context) vals[column] = code_id res = super(superclass, self).create(cr, uid, vals, context=context) if code_ref_ids: self._generate_code_ref_id(cr, uid, res, context=context) return res @AddMethod(superclass) def write(self, cr, uid, ids, vals, context=None): """Update the analytic code's name if it is not inherited, and its parent code if parent-child relations are synchronized. """ code_vals = {} new = False if not isinstance(ids, (list, tuple)): ids = [ids] if sync_parent: cp = self._get_code_parent(cr, uid, vals, context=context) if cp is not None: code_vals["code_parent_id"] = cp # Direct changes to the 'bound analytic code' field are ignored # unless the 'force_code_id' context key is passed as True. force_code_id = vals.pop(column, False) if context and context.get("force_code_id", False) == True: self._force_code(cr, uid, force_code_id, code_vals, context) vals[column] = force_code_id elif use_inherits: vals.update(code_vals) else: name_col = rel_name[1] if rel_name else "name" if name_col in vals: code_vals["name"] = vals[name_col] records = self.browse(cr, uid, ids, context=context) code_ids = [getattr(rec, column).id for rec in records] # If updating a single record with no code, create it. code_osv = self.pool["analytic.code"] if code_ids == [False]: new = ids[0] code_vals["nd_id"] = self._bound_dimension_id if "name" not in code_vals: code_vals["name"] = self.read(cr, uid, new, [name_col], context=context)[name_col] vals[column] = code_osv.create(cr, uid, code_vals, context=context) elif code_vals: code_osv.write(cr, uid, code_ids, code_vals, context=context) res = super(superclass, self).write(cr, uid, ids, vals, context=context) if code_ref_ids and new is not False: self._generate_code_ref_id(cr, uid, new, context=context) return res @AddMethod(superclass) def _force_code(self, cr, uid, force_code_id, code_vals, context=None): code_osv = self.pool["analytic.code"] if not force_code_id: raise ValueError( "An analytic code ID MUST be specified if the " "force_code_id key is enabled in the context" ) force_code_dim = code_osv.read(cr, uid, force_code_id, ["nd_id"], context=context)["nd_id"][0] if force_code_dim != self._bound_dimension_id: raise ValueError( "If specified, codes must belong to the bound " "analytic dimension {}".format(dimension_name) ) if code_vals: code_osv.write(cr, uid, force_code_id, code_vals, context=context) if sync_parent: # This function is called as a method and can be overridden. @AddMethod(superclass) def _get_code_parent(self, cr, uid, vals, context=None): """If parent_id is in the submitted values, return the analytic code of this parent, to be used as the child's code's parent. """ parent_id = vals.get(sync_parent, None) if parent_id is not None: if parent_id: res = self.read(cr, uid, parent_id, [column], context=context)[column] return res[0] if res else False else: return False return None if use_code_name_methods: @AddMethod(superclass) def name_get(self, cr, uid, ids, context=None): """Return the analytic code's name.""" code_osv = self.pool.get("analytic.code") code_reads = self.read(cr, uid, ids, [column], context=context) c2m = { # Code IDs to model IDs code_read[column][0]: code_read["id"] for code_read in code_reads if code_read[column] is not False } names = code_osv.name_get(cr, uid, c2m.keys(), context=context) return [(c2m[cid], name) for cid, name in names if cid in c2m] @AddMethod(superclass) def name_search(self, cr, uid, name, args=None, operator="ilike", context=None, limit=100): """Return the records whose analytic code matches the name.""" code_osv = self.pool.get("analytic.code") args.append(("nd_id", "=", self._bound_dimension_id)) names = code_osv.name_search(cr, uid, name, args, operator, context, limit) if not names: return [] dom = [(column, "in", zip(*names)[0])] ids = self.search(cr, uid, dom, context=context) code_reads = self.read(cr, uid, ids, [column], context=context) c2m = { # Code IDs to model IDs code_read[column][0]: code_read["id"] for code_read in code_reads if code_read[column] is not False } return [(c2m[cid], cname) for cid, cname in names if cid in c2m] return (superclass,)
class event_registration(orm.Model): _name = "event.registration" _inherit = ['event.registration', 'mozaik.abstract.model'] _description = "Event Registration" # private methods def _get_coordinates(self, cr, uid, vals, context=None): ''' if `partner_id`` in vals then set `email' and `phone` with email coordinate and phone.coordinate of the partner ''' r_fields = [ 'email_coordinate_id', 'mobile_coordinate_id', 'fix_coordinate_id', 'display_name' ] p_obj = self.pool['res.partner'] p_id = vals['partner_id'] p_value = p_obj.read(cr, uid, p_id, r_fields, context=context) vals['name'] = p_value['display_name'] # select mobile id or fix id of no phone phone_id = p_value['mobile_coordinate_id'] and \ p_value['mobile_coordinate_id'][0] or \ p_value['fix_coordinate_id'] and \ p_value['fix_coordinate_id'][0] or False if phone_id: ph_obj = self.pool['phone.coordinate'] vals['phone'] = ph_obj.browse(cr, uid, phone_id, context=context).phone_id.name email_id = p_value['email_coordinate_id'] and \ p_value['email_coordinate_id'][0] or False if email_id: e_obj = self.pool['email.coordinate'] vals['email'] = e_obj.read(cr, uid, email_id, ['email'], context=context)['email'] vals['email_coordinate_id'] = email_id # constraints _int_instance_store_trigger = { 'event.registration': (lambda self, cr, uid, ids, context=None: ids, ['partner_id'], 10), 'res.partner': (lambda self, cr, uid, ids, context=None: self. pool['event.registration'].search( cr, SUPERUSER_ID, [('partner_id', 'in', ids)], context=context), ['int_instance_id'], 10), } _columns = { 'email_coordinate_id': fields.many2one('email.coordinate', string='Email Coordinate'), 'partner_instance_id': fields.related('partner_id', 'int_instance_id', string='Partner Internal Instance', type='many2one', relation='int.instance', select=True, readonly=True, store=_int_instance_store_trigger), } _unicity_keys = 'event_id, partner_id' # orm methods def create(self, cr, uid, vals, context=None): ''' recompute email and phone field ''' if vals.get('event_id', False): event = self.pool['event.event'].browse(cr, uid, vals['event_id'], context=context) if event.int_instance_id: ia_obj = self.pool['int.assembly'] followers_ids = ia_obj.get_followers_assemblies( cr, uid, event.int_instance_id.id, context=context) vals['message_follower_ids'] = [(6, 0, followers_ids)] if vals.get('partner_id', False): self._get_coordinates(cr, uid, vals, context=context) return super(event_registration, self).create(cr, uid, vals, context=context) # public methods def update_coordinates(self, cr, uid, reg_id, context=None): reg_vals = self.read(cr, uid, reg_id, ['partner_id']) partner_id = reg_vals['partner_id'][0] vals = { 'partner_id': partner_id, } self._get_coordinates(cr, uid, vals, context=context) return self.write(cr, uid, reg_id, vals, context=context) def button_reg_cancel(self, cr, uid, ids, context=None): ''' deactivate registration if canceled ''' res = super(event_registration, self).button_reg_cancel(cr, uid, ids, context=context) self.action_invalidate(cr, uid, ids, context=context) return res
"move_ids": fields.one2many("stock.move", "rent_line_id", "Inventory Moves", readonly=True), "state": fields.selection( [ ("draft", "Draft"), ("cancel", "Cancelled"), ("confirmed", "Waiting Approval"), ("accepted", "Approved"), ("returned", "Returned"), ("done", "Done"), ], "Status", required=True, readonly=True, ), "order_partner_id": fields.related( "order_id", "partner_id", type="many2one", relation="res.partner", store=True, string="Customer" ), "salesman_id": fields.related( "order_id", "user_id", type="many2one", relation="res.users", store=True, string="Salesperson" ), "company_id": fields.related( "order_id", "company_id", type="many2one", relation="res.company", string="Company", store=True, readonly=True, ), }
class sale_order(orm.Model): _inherit = "sale.order" def default_get(self, cr, uid, fields, context=None): context = context or self.pool['res.users'].context_get(cr, uid) # sale_order_obj = self.pool['sale.order'] # sale_order_line_obj = self.pool['sale.order.line'] res = super(sale_order, self).default_get(cr, uid, fields, context=context) if not res.get('shop_id', False): shop_ids = self.pool['sale.order'].search(cr, uid, [], limit=1, context=context) if shop_ids: res['shop_id'] = shop_ids[0] if not res.get('section_id', False): section_ids = self.pool['crm.case.section'].search(cr, uid, [('user_id', '=', uid)], context=context) if section_ids: res['section_id'] = section_ids[0] return res def service_only(self, cr, uid, ids, context): context = context or self.pool['res.users'].context_get(cr, uid) service = True if not isinstance(ids, (list, tuple)): ids = [ids] for order in self.browse(cr, uid, ids, context): if order.order_line: for order_line in order.order_line: if order_line.product_id and order_line.product_id.type != 'service': return False elif not service: return False return True def hook_sale_state(self, cr, uid, orders, vals, context): context = context or self.pool['res.users'].context_get(cr, uid) # function call if change state the sale order return True def adaptative_function(self, cr, uid, ids, vals, context): context = context or self.pool['res.users'].context_get(cr, uid) if not isinstance(ids, (list, tuple)): ids = [ids] if vals.get('section_id', False) or vals.get('carrier_id', False) or vals.get('payment_term'): for order in self.browse(cr, uid, ids, context): partner_vals = {} if not order.partner_id.section_id: partner_vals['section_id'] = vals.get('section_id') if not order.partner_id.property_delivery_carrier: partner_vals['property_delivery_carrier'] = vals.get('carrier_id') if not order.partner_id.property_payment_term: partner_vals['property_payment_term'] = vals.get('payment_term') if partner_vals: self.pool['res.partner'].write(cr, uid, [order.partner_id.id], partner_vals, context) return True def create(self, cr, uid, vals, context=None): context = context or self.pool['res.users'].context_get(cr, uid) ids = super(sale_order, self).create(cr, uid, vals, context=context) self.adaptative_function(cr, uid, ids, vals, context) return ids def write(self, cr, uid, ids, vals, context=None): if context is None: context = self.pool['res.users'].context_get(cr, uid) if not isinstance(ids, (list, tuple)): ids = [ids] orders = self.browse(cr, uid, ids, context) self.adaptative_function(cr, uid, ids, vals, context) if vals.get('state', False): self.hook_sale_state(cr, uid, orders, vals, context) return super(sale_order, self).write(cr, uid, ids, vals, context=context) def action_wait(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): company = self.pool['res.users'].browse(cr, uid, uid).company_id if self.service_only(cr, uid, [order.id], context) and order.order_policy and order.order_policy == 'picking': if company.auto_order_policy: order.write({'order_policy': 'manual'}) else: raise orm.except_orm(_('Warning'), _( "You can't create an order with Invoicing being based on Picking if there are only service products")) else: if company.auto_order_policy: default = self.default_get(cr, uid, ['order_policy'], context) order.write({'order_policy': default.get('order_policy')}) return super(sale_order, self).action_wait(cr, uid, ids, context) def copy(self, cr, uid, ids, default, context=None): context = context or self.pool['res.users'].context_get(cr, uid) default.update( { 'tech_validation': False, 'manager_validation': False, 'customer_validation': False, 'email_sent_validation': False, 'supervisor_validation': False, 'date_order': fields.date.context_today, } ) default.update(self.default_get(cr, uid, ['order_policy', 'picking_policy', 'invoice_quantity'], context)) return super(sale_order, self).copy(cr, uid, ids, default, context) def action_cancel_draft(self, cr, uid, ids, *args): self.write(cr, uid, ids, { 'tech_validation': False, 'manager_validation': False, 'customer_validation': False, 'email_sent_validation': False, 'supervisor_validation': False, }) super(sale_order, self).action_cancel_draft(cr, uid, ids, *args) return True def onchange_invoice_type_id(self, cr, uid, ids, invoice_type_id, context=None): if context is None: context = self.pool['res.users'].context_get(cr, uid) res = {} if invoice_type_id: invoice_type_obj = self.pool['sale_journal.invoice.type'] invoice_type = invoice_type_obj.browse(cr, uid, invoice_type_id, context) if invoice_type.invoicing_method == 'grouped': res['order_policy'] = 'picking' return {'value': res} def onchange_partner_id(self, cr, uid, ids, part, context=None): context = context or self.pool['res.users'].context_get(cr, uid) res = super(sale_order, self).onchange_partner_id(cr, uid, ids, part) if res.get('value', False) and part: if not res['value'].get('fiscal_position', False): company_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.id company = self.pool['res.company'].browse(cr, uid, company_id, context) if company.default_property_account_position: res['value']['fiscal_position'] = company.default_property_account_position and company.default_property_account_position.id return res def _credit_limit(self, cr, uid, ids, field_name, arg, context): context = context or self.pool['res.users'].context_get(cr, uid) res = dict.fromkeys(ids, 0.0) for order in self.browse(cr, uid, ids, context=context): if order.order_policy == 'prepaid': res[order.id] = 0 continue partner = order.partner_id credit = partner.credit # We sum from all the sale orders that are aproved, the sale order lines that are not yet invoiced order_obj = self.pool['sale.order'] approved_invoices_ids = order_obj.search(cr, uid, [('partner_id', '=', partner.id), ('state', 'not in', ['draft', 'cancel', 'done'])], context=context) approved_invoices_amount = 0.0 for orders in order_obj.browse(cr, uid, approved_invoices_ids, context=context): for order_line in orders.order_line: if not order_line.invoiced: approved_invoices_amount += order_line.price_subtotal # We sum from all the invoices that are in draft the total amount invoice_obj = self.pool['account.invoice'] draft_invoices_ids = invoice_obj.search(cr, uid, [('partner_id', '=', partner.id), ('state', '=', 'draft')], context=context) draft_invoices_amount = 0.0 for invoice in invoice_obj.browse(cr, uid, draft_invoices_ids, context=context): draft_invoices_amount += invoice.amount_total available_credit = partner.credit_limit - credit - approved_invoices_amount - draft_invoices_amount res[order.id] = available_credit - order.amount_total return res def partner_overdue_check(self, cr, uid, company, partner, context): # return True if there are same overdue payment account_move_line_obj = self.pool['account.move.line'] overdue_date = (datetime.today() - relativedelta(days=company.date_max_overdue or 0.0)).strftime(DEFAULT_SERVER_DATE_FORMAT) account_move_ids = account_move_line_obj.search(cr, uid, [ ('partner_id', '=', partner.id), ('account_id.type', 'in', ['receivable', 'payable']), ('stored_invoice_id', '!=', False), ('reconcile_id', '=', False), ('date_maturity', '<', overdue_date)], context=context) if account_move_ids: return True return False def check_limit(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context=context): if order.credit_limit < 0 and order.company_id and order.company_id.check_credit_limit: title = _(u'Credit Over Limit') msg = _(u'Is not possible to confirm because customer exceed the credit limit. \n Is Possible change the Order Policy \"Pay Before Delivery\" \n on tab \"Other Information\"') raise orm.except_orm(_(title), _(msg)) return False if order.visible_minimum and order.sale_order_minimun > order.amount_untaxed: if order.shop_id.user_allow_minimun_id and order.shop_id.user_allow_minimun_id.id == uid: # if user can validate return True # test if on line there are the product if order.shop_id.product_allow_minimun_id: for line in order.order_line: if line.product_id and line.product_id == order.shop_id.product_allow_minimun_id: return True title = _(u'Minimum Amount Billable') if order.shop_id.user_allow_minimun_id: msg = _(u'Is not possible to confirm because is not reached the minimum billable {amount} {currency} \n Only {user} can do it').format(amount=order.sale_order_minimun, currency=order.pricelist_id.currency_id.symbol, user=order.shop_id.user_allow_minimun_id.name) else: msg = _(u'Is not possible to confirm because is not reached the minimum billable {amount} {currency}').format(amount=order.sale_order_minimun, currency=order.pricelist_id.currency_id.symbol) if order.shop_id.product_allow_minimun_id: msg += _(u'\n\n or add the product \'{product}\'').format(product=order.shop_id.product_allow_minimun_id.name_get()[0][1]) raise orm.except_orm(_(title), _(msg)) return False if order.company_id and order.company_id.check_overdue: if self.partner_overdue_check(cr, uid, order.company_id, order.partner_id, context): title = _(u'Overdue Limit') msg = _(u'Is not possible to confirm because customer have a overdue payment') raise orm.except_orm(_(title), _(msg)) return False return True def name_get(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) if not len(ids): return [] res = [] for sale in self.browse(cr, uid, ids, context=context): name = u'[{sale_name}] {partner_name}'.format(sale_name=sale.name, partner_name=sale.partner_id.name) res.append((sale.id, name)) return res def name_search(self, cr, uid, name='', args=None, operator='ilike', context=None, limit=10): context = context or self.pool['res.users'].context_get(cr, uid) if not args: args = [] if name: ids = self.search(cr, uid, [('name', operator, name)] + args, limit=limit, context=context) if not len(ids): ids = self.search(cr, uid, [('partner_id', 'ilike', name)] + args, limit=limit, context=context) ids = list(set(ids)) if not len(ids): ptrn = re.compile('(\[(.*?)\])') res = ptrn.search(name) if res: ids = self.search( cr, uid, [('name', '=', res.group(2))] + args, limit=limit, context=context) else: ids = self.search(cr, uid, args, limit=limit, context=context) result = self.name_get(cr, uid, ids, context=context) return result def __init__(self, registry, cr): """ Add state "Suspended" """ super(sale_order, self).__init__(registry, cr) options = [('wait_technical_validation', _('Technical Validation')), ('wait_manager_validation', _('Manager Validation')), ('send_to_customer', _('Send To Customer')), ('wait_customer_validation', _('Customer Validation')), ('wait_supervisor_validation', _('Supervisor Validation'))] type_selection = self._columns['state'].selection for option in options: if option not in type_selection: type_selection.append(option) def _get_shop_id(self, cr, uid, context): shop_ids = self.pool['sale.shop'].search(cr, uid, [], context=context, limit=1) return shop_ids and shop_ids[0] or False _columns = { 'create_uid': fields.many2one('res.users', 'Created by', readonly=True), 'credit_limit': fields.function(_credit_limit, string="Remaining Credit Limit", type='float', readonly=True, method=True), 'sale_order_minimun': fields.related('shop_id', 'sale_order_minimun', type='float', string='Minimun Invoice', store=False, readonly=True), 'visible_minimum': fields.related('shop_id', 'sale_order_have_minimum', type='boolean', string=_('Minimun Amount'), store=False, readonly=True), 'visible_credit_limit': fields.related('company_id', 'check_credit_limit', type='boolean', string=_('Fido Residuo Visibile'), store=False, readonly=True), 'validity': fields.date('Validity'), 'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', readonly=True, states={ 'draft': [('readonly', False)], 'wait_technical_validation': [('readonly', False)], 'wait_manager_validation': [('readonly', False)]} ), 'project_id': fields.many2one('account.analytic.account', 'Contract/Analytic Account', readonly=True, states={ 'draft': [('readonly', False)], 'wait_technical_validation': [('readonly', False)], 'wait_manager_validation': [('readonly', False)], 'send_to_customer': [('readonly', False)], 'wait_customer_validation': [('readonly', False)], }, help="The analytic account related to a sales order."), 'required_tech_validation': fields.related('company_id', 'need_tech_validation', type='boolean', string=_('Required Technical Validation'), store=False, readonly=True), 'need_tech_validation': fields.boolean("Technical Validation", readonly=True), 'tech_validation': fields.boolean("Tech Validated ?", readonly=True), 'required_manager_validation': fields.related('company_id', 'need_manager_validation', type='boolean', string=_('Required Manager Validation'), store=False, readonly=True), 'need_manager_validation': fields.boolean("Manager Validation", readonly=True), 'manager_validation': fields.boolean("Manager Validated ?", readonly=True), 'email_sent_validation': fields.boolean("Email Sent to Customer ?", readonly=True), 'customer_validation': fields.boolean("Customer Validated ?", readonly=True), # A validation after customer confirmation: 'required_supervisor_validation': fields.related('company_id', 'need_supervisor_validation', type='boolean', string=_('Required Supervisor Validation'), store=False, readonly=True), 'skip_supervisor_validation_onstandard_product': fields.related('company_id', 'skip_supervisor_validation_onstandard_product', type='boolean', string=_( 'Skip Supervisor Verification if there are only standard product'), store=False, readonly=True), 'supervisor_validation': fields.boolean(_("Supervisor Validated?"), readonly=True), 'product_id': fields.related('order_line', 'product_id', type='many2one', relation='product.product', string='Product'), 'revision_note': fields.char('Reason', size=256, select=True), 'lost_reason_id': fields.many2one('crm.lost.reason', string='Lost Reason'), 'last_revision_note': fields.related('sale_version_id', 'revision_note', type='char', string="Last Revision Note", store=True), } _defaults = { 'need_tech_validation': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.need_tech_validation, 'need_manager_validation': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.need_manager_validation, 'skip_supervisor_validation_onstandard_product': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.skip_supervisor_validation_onstandard_product, 'required_tech_validation': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.need_tech_validation, 'required_manager_validation': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.need_manager_validation, 'required_supervisor_validation': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.need_supervisor_validation, 'validity': lambda self, cr, uid, context: (datetime.today() + relativedelta(days=self.pool['res.users'].browse(cr, uid, uid, context).company_id.default_sale_order_validity or 0.0)).strftime(DEFAULT_SERVER_DATE_FORMAT), 'shop_id': lambda self, cr, uid, context: self._get_shop_id(cr, uid, context), } def action_reopen(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) result = super(sale_order, self).action_reopen(cr, uid, ids, context=context) for order in self.browse(cr, uid, ids, context): if order.state == 'draft': self.write(cr, uid, ids, { 'tech_validation': False, 'manager_validation': False, 'email_sent_validation': False, 'customer_validation': False, }, context) return result def check_direct_order_confirm(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): if order.state == 'draft' and order.pricelist_id and order.pricelist_id.contract: return True else: return False def check_tech_validation(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): if order.shop_id.user_tech_validation_id: if order.shop_id.user_tech_validation_id.id == uid: return True else: title = _('Technical Validation') msg = _(u"It's not possible to confirm, for shop {shop} only user '{user}' can do it".format(shop=order.shop_id.name, user=order.shop_id.user_tech_validation_id.name)) raise orm.except_orm(_(title), _(msg)) return False else: return True def check_manager_validation(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): if order.shop_id.user_manager_validation_id: if order.shop_id.user_manager_validation_id.id == uid: return True else: title = _('Manager Validation') msg = _(u"It's not possible to confirm, for shop {shop} only user '{user}' can do it".format(shop=order.shop_id.name, user=order.shop_id.user_manager_validation_id.name)) raise orm.except_orm(_(title), _(msg)) return False else: return True def check_supervisor_validation(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): if order.shop_id.user_supervisor_validation_id: if order.shop_id.user_supervisor_validation_id.id == uid: return True else: title = _('Supervisor Validation') msg = _(u"It's not possible to confirm, for shop {shop} only user '{user}' can do it".format(shop=order.shop_id.name, user=order.shop_id.user_supervisor_validation_id.name)) raise orm.except_orm(_(title), _(msg)) return False else: return True def required_tech_validation(self, order): if order.company_id.tech_validation_if_no_product: for line in order.order_line: if not line.product_id: order.write({'need_tech_validation': True}) return True return False def check_discount(self, order): if order.company_id.enable_discount_validation: max_discount = order.company_id.max_discount for line in order.order_line: if line.discount > max_discount: order.write({'need_manager_validation': True}) return True return False def action_validate(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): if not order.partner_id.validate and order.company_id.enable_partner_validation: title = _('Partner To Validate') msg = _("It's not possible to confirm because customer must be validated") raise orm.except_orm(_(title), _(msg)) return False if order.need_tech_validation and not order.tech_validation or self.required_tech_validation(order): vals = { 'state': 'wait_technical_validation', } elif self.check_discount(order): vals = { 'state': 'wait_manager_validation', } elif order.company_id.enable_margin_validation and order.amount_untaxed and (order.margin / order.amount_untaxed) * 100 < order.company_id.minimum_margin and not order.manager_validation: vals = { 'state': 'wait_manager_validation', } elif order.need_manager_validation and not order.manager_validation: vals = { 'state': 'wait_manager_validation', } elif not order.email_sent_validation: vals = { 'state': 'send_to_customer', } elif not order.customer_validation: vals = { 'state': 'wait_customer_validation', } elif order.required_supervisor_validation and not order.supervisor_validation: vals = { 'state': 'wait_supervisor_validation', } else: vals = { 'state': 'draft', 'tech_validation': False, 'manager_validation': False, 'customer_validation': False, 'email_sent_validation': False, 'supervisor_validation': False } order.write(vals) return True def check_validate(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): res = True if order.need_tech_validation and not order.tech_validation: res = False elif order.need_manager_validation and not order.manager_validation: res = False elif order.required_supervisor_validation and not order.supervisor_validation: if order.skip_supervisor_validation_onstandard_product: for line in order.order_line: if line.product_id and line.product_id.is_kit: return False res = True else: res = False return res and order.email_sent_validation and order.customer_validation return True def check_direct_confirm(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) if self.check_limit(cr, uid, ids, context): for order in self.browse(cr, uid, ids, context): values = { 'state': 'wait_customer_validation', 'customer_validation': True } if order.need_tech_validation: values['tech_validation'] = True if (order.company_id.enable_margin_validation and order.amount_untaxed and (order.margin / order.amount_untaxed) < order.company_id.minimum_margin) or order.need_manager_validation: values['manager_validation'] = True if order.required_supervisor_validation: values['supervisor_validation'] = True self.write(cr, uid, [order.id], values, context) return self.action_validate(cr, uid, ids, context) else: return False def copy(self, cr, uid, ids, default={}, context=None): default = default or {} context = context or self.pool['res.users'].context_get(cr, uid) default.update({ 'validity': (datetime.today() + relativedelta(days=self.pool['res.users'].browse(cr, uid, uid, context).company_id.default_sale_order_validity or 0.0)).strftime(DEFAULT_SERVER_DATE_FORMAT), 'tech_validation': False, 'manager_validation': False, 'customer_validation': False, 'email_sent_validation': False, 'supervisor_validation': False, 'lost_reason_id': False }) return super(sale_order, self).copy(cr, uid, ids, default, context=context)
class hr_si_project(osv.osv_memory): _name = 'hr.sign.in.project' _description = 'Sign In By Project' _columns = { 'name': fields.char('Employee\'s Name', readonly=True), 'state': fields.related('emp_id', 'state', string='Current Status', type='selection', selection=[('present', 'Present'), ('absent', 'Absent')], required=True, readonly=True), 'date': fields.datetime('Starting Date'), 'server_date': fields.datetime('Current Date', readonly=True), 'emp_id': fields.many2one('hr.employee', 'Employee ID') } 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 user’s ID for security checks, @param fields: List of fields for default value @param context: A standard dictionary for contextual values """ emp_obj = self.pool.get('hr.employee') emp_id = emp_obj.search(cr, uid, [('user_id', '=', uid)], context=context) if not emp_id: raise osv.except_osv(_('User Error!'), _('Please define employee for your user.')) return False def check_state(self, cr, uid, ids, context=None): obj_model = self.pool.get('ir.model.data') emp_id = self.default_get(cr, uid, ['emp_id'], context)['emp_id'] # get the latest action (sign_in or out) for this employee cr.execute( 'select action from hr_attendance where employee_id=%s and action in (\'sign_in\',\'sign_out\') order by name desc limit 1', (emp_id, )) res = (cr.fetchone() or ('sign_out', ))[0] in_out = (res == 'sign_out') and 'in' or 'out' #TODO: invert sign_in et sign_out model_data_ids = obj_model.search( cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_hr_timesheet_sign_%s' % in_out)], context=context) resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] return { 'name': _('Sign in / Sign out'), 'view_type': 'form', 'view_mode': 'tree,form', 'res_model': 'hr.sign.%s.project' % in_out, 'views': [(resource_id, 'form')], 'type': 'ir.actions.act_window', 'target': 'new' } def sign_in_result(self, cr, uid, ids, context=None): emp_obj = self.pool.get('hr.employee') for data in self.browse(cr, uid, ids, context=context): emp_id = data.emp_id.id emp_obj.attendance_action_change(cr, uid, [emp_id], { 'action': 'sign_in', 'action_date': data.date }) return {'type': 'ir.actions.act_window_close'} def default_get(self, cr, uid, fields_list, context=None): res = super(hr_si_project, self).default_get(cr, uid, fields_list, context=context) emp_obj = self.pool.get('hr.employee') emp_id = emp_obj.search(cr, uid, [('user_id', '=', uid)], context=context) if emp_id: for employee in emp_obj.browse(cr, uid, emp_id, context=context): res.update({ 'name': employee.name, 'state': employee.state, 'emp_id': emp_id[0], 'server_date': time.strftime('%Y-%m-%d %H:%M:%S') }) return res
class pos_session_opening(osv.osv_memory): _name = 'pos.session.opening' _columns = { 'pos_config_id': fields.many2one('pos.config', 'Point of Sale', required=True), 'pos_session_id': fields.many2one('pos.session', 'PoS Session'), 'pos_state': fields.related('pos_session_id', 'state', type='selection', selection=pos_session.POS_SESSION_STATE, string='Session Status', readonly=True), 'pos_state_str': fields.char('Status', 32, readonly=True), 'show_config': fields.boolean('Show Config', readonly=True), 'pos_session_name': fields.related('pos_session_id', 'name', type='char', size=64, readonly=True), 'pos_session_username': fields.related('pos_session_id', 'user_id', 'name', type='char', size=64, readonly=True) } def open_ui(self, cr, uid, ids, context=None): context = context or {} data = self.browse(cr, uid, ids[0], context=context) context['active_id'] = data.pos_session_id.id or False print "******CONTEXT['active_id']*****: ", context['active_id'] return { 'type': 'ir.actions.client', 'name': _('Start Point Of Sale'), 'tag': 'pos.ui', 'context': context } def open_existing_session_cb_close(self, cr, uid, ids, context=None): wf_service = netsvc.LocalService("workflow") wizard = self.browse(cr, uid, ids[0], context=context) wf_service.trg_validate(uid, 'pos.session', wizard.pos_session_id.id, 'cashbox_control', cr) return self.open_session_cb(cr, uid, ids, context) def open_session_cb(self, cr, uid, ids, context=None): assert len(ids) == 1, "you can open only one session at a time" proxy = self.pool.get('pos.session') wizard = self.browse(cr, uid, ids[0], context=context) if not wizard.pos_session_id: values = { 'user_id': uid, 'config_id': wizard.pos_config_id.id, } session_id = proxy.create(cr, uid, values, context=context) s = proxy.browse(cr, uid, session_id, context=context) if s.state == 'opened': return self.open_ui(cr, uid, ids, context=context) return self._open_session(session_id) return self._open_session(wizard.pos_session_id.id) def open_existing_session_cb(self, cr, uid, ids, context=None): assert len(ids) == 1 wizard = self.browse(cr, uid, ids[0], context=context) return self._open_session(wizard.pos_session_id.id) def _open_session(self, session_id): return { 'name': _('Session'), 'view_type': 'form', 'view_mode': 'form,tree', 'res_model': 'pos.session', 'res_id': session_id, 'view_id': False, 'type': 'ir.actions.act_window', } def on_change_config(self, cr, uid, ids, config_id, context=None): result = { 'pos_session_id': False, 'pos_state': False, 'pos_state_str': '', 'pos_session_username': False, 'pos_session_name': False, } if not config_id: return {'value': result} proxy = self.pool.get('pos.session') session_ids = proxy.search(cr, uid, [ ('state', '!=', 'closed'), ('config_id', '=', config_id), ], context=context) if session_ids: session = proxy.browse(cr, uid, session_ids[0], context=context) result['pos_state'] = str(session.state) result['pos_state_str'] = dict(pos_session.POS_SESSION_STATE).get( session.state, '') result['pos_session_id'] = session.id result['pos_session_name'] = session.name result['pos_session_username'] = session.user_id.name return {'value': result} def default_get(self, cr, uid, fieldnames, context=None): so = self.pool.get('pos.session') session_ids = so.search(cr, uid, [('state', '<>', 'closed'), ('user_id', '=', uid)], context=context) if session_ids: result = so.browse(cr, uid, session_ids[0], context=context).config_id.id else: current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context) result = current_user.pos_config and current_user.pos_config.id or False if not result: r = self.pool.get('pos.config').search(cr, uid, [], context=context) result = r and r[0] or False count = self.pool.get('pos.config').search_count( cr, uid, [('state', '=', 'active')], context=context) show_config = bool(count > 1) return { 'pos_config_id': result, 'show_config': show_config, }
import xml2dic <<<<<<< HEAD from openerp.tools.translate import _ class res_partner(osv.osv): ======= from tools.translate import _ class res_partner(osv.Model): >>>>>>> c1979f64b3360c86d60e00c92be0271d89f97f2d _inherit = 'res.partner' _rec_name = 'payment_profile_id' _columns = { 'payment_profile_id': fields.many2one('cust.profile', 'Payment Profiles', help='Store customers payment profile id', readonly='True'), # 'payment_profile_ids':fields.one2many('cust.payment.profile', 'partner_id','Payment Profiles' ,help='Store customers payment profile id',readonly=True), 'payment_profile_ids': fields.related('payment_profile_id', 'payment_profile_ids', type='one2many', relation='cust.payment.profile', string='Payment Profiles', readonly=True), } def request_to_server(self, Request_string, url, url_path): ''' Sends a POST request to url and returns the response from the server''' if ('http' or 'https') in url[:5]: raise osv.except_osv(_('Configuration Error!'), _('Request URL should not start with http or https.\nPlease check Authorization Configuration in Company.')) conn = httplib.HTTPSConnection(url) conn.putrequest('POST', url_path) conn.putheader('content-type', 'text/xml') conn.putheader('content-length', len(Request_string)) conn.endheaders() conn.send(Request_string) response = conn.getresponse() create_CustomerProfile_response_xml = response.read() return create_CustomerProfile_response_xml
class sale_order_line(orm.Model): _inherit = "sale.order.line" def _prepare_order_line_invoice_line(self, cr, uid, line, account_id=False, context=None): context = context or self.pool['res.users'].context_get(cr, uid) vals = super(sale_order_line, self)._prepare_order_line_invoice_line(cr, uid, line, account_id, context) if vals: vals.update({'origin_document': 'sale.order.line, {line_id}'.format(line_id=line.id)}) return vals def _delivered_qty(self, cr, uid, ids, field_name, arg, context=None): context = context or self.pool['res.users'].context_get(cr, uid) res = {} for line in self.browse(cr, uid, ids, context=context): qty = 0 for move in line.move_ids: if move.state == 'done': qty += move.product_qty res[line.id] = qty return res def _product_available(self, cr, uid, ids, field_names=None, arg=False, context=None): """ Finds the incoming and outgoing quantity of product. @return: Dictionary of values """ context = context or self.pool['res.users'].context_get(cr, uid) res = {} # if line.order_id: # context['warehouse'] = self.order_id.shop_id.warehouse_id.id for line in self.browse(cr, uid, ids, context): res[line.id] = { 'qty_available': line.product_id and line.product_id.type != 'service' and line.product_id.qty_available or False, 'virtual_available': line.product_id and line.product_id.type != 'service' and line.product_id.virtual_available or False} return res # overwrite of a funcion inside sale_margin def product_id_change(self, cr, uid, ids, pricelist, product_id, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None): context = context or self.pool['res.users'].context_get(cr, uid) context.update(error_on_available=False) res = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product_id, qty=qty, uom=uom, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id, lang=lang, update_tax=update_tax, date_order=date_order, packaging=packaging, fiscal_position=fiscal_position, flag=flag, context=context) if not pricelist: return res frm_cur = self.pool['res.users'].browse(cr, uid, uid, context).company_id.currency_id.id to_cur = self.pool['product.pricelist'].browse(cr, uid, [pricelist], context)[0].currency_id.id if product_id: product = self.pool['product.product'].browse(cr, uid, product_id, context) price = self.pool['res.currency'].compute(cr, uid, frm_cur, to_cur, product.cost_price, round=False) res['value'].update({ 'purchase_price': price, 'product_type': product.type }) return res _columns = { 'order_id': fields.many2one('sale.order', 'Order Reference', ondelete='cascade', select=True, readonly=True, states={'draft': [('readonly', False)]}), 'readonly_price_unit': fields.related('order_id', 'company_id', 'readonly_price_unit', type='boolean', string=_('Readonly Price Unit'), store=False, readonly=True), 'delivered_qty': fields.function(_delivered_qty, digits_compute=dp.get_precision('Product UoM'), string='Delivered Qty'), 'qty_available': fields.function(_product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product UoM'), string='Quantity On Hand'), 'virtual_available': fields.function(_product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product UoM'), string='Quantity Available'), 'product_type': fields.char('Product type', size=64), } _defaults = { 'readonly_price_unit': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.readonly_price_unit, 'order_id': lambda self, cr, uid, context: context.get('default_sale_order', False) or False } def default_get(self, cr, uid, fields, context=None): """ """ if not context: context = self.pool['res.users'].context_get(cr, uid) res = super(sale_order_line, self).default_get(cr, uid, fields, context=context) if not res.get('tax_id', False): fpos_obj = self.pool['account.fiscal.position'] product_default_get = self.pool['product.product'].default_get(cr, uid, ['taxes_id', 'uom_id']) taxes = product_default_get.get('taxes_id', False) if taxes: taxes = self.pool['account.tax'].browse(cr, uid, taxes, context) if context.get('fiscal_position', False): fpos = fpos_obj.browse(cr, uid, context['fiscal_position'], context) if taxes: tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes) else: tax_id = [] else: if taxes: tax_id = [line.id for line in taxes] else: tax_id = [] res.update({ 'tax_id': [(6, 0, tax_id)], }) uom_id = product_default_get.get('uom_id', False) if uom_id: res.update({ 'product_uom': uom_id }) return res
class membership_line(osv.osv): _name = 'membership.membership_line' _description = __doc__ def _get_partners(self, cr, uid, ids, context=None): list_membership_line = [] member_line_obj = self.pool.get('membership.membership_line') for partner in self.pool.get('res.partner').browse(cr, uid, ids, context=context): if partner.member_lines: list_membership_line += member_line_obj.search( cr, uid, [('id', 'in', [l.id for l in partner.member_lines])], context=context) return list_membership_line def _get_membership_lines(self, cr, uid, ids, context=None): list_membership_line = [] member_line_obj = self.pool.get('membership.membership_line') for invoice in self.pool.get('account.invoice').browse( cr, uid, ids, context=context): if invoice.invoice_line_ids: list_membership_line += member_line_obj.search( cr, uid, [('account_invoice_line', 'in', [l.id for l in invoice.invoice_line_ids])], context=context) return list_membership_line def _check_membership_date(self, cr, uid, ids, context=None): """Check if membership product is not in the past """ cr.execute( ''' SELECT MIN(ml.date_to - ai.date_invoice) FROM membership_membership_line ml JOIN account_invoice_line ail ON ( ml.account_invoice_line = ail.id ) JOIN account_invoice ai ON ( ai.id = ail.invoice_id) WHERE ml.id IN %s''', (tuple(ids), )) res = cr.fetchall() for r in res: if r[0] and r[0] < 0: return False return True def _state(self, cr, uid, ids, name, args, context=None): """Compute the state lines """ res = {} inv_obj = self.pool.get('account.invoice') for line in self.browse(cr, uid, ids, context=context): cr.execute( ''' SELECT i.state, i.id FROM account_invoice i WHERE i.id = ( SELECT l.invoice_id FROM account_invoice_line l WHERE l.id = ( SELECT ml.account_invoice_line FROM membership_membership_line ml WHERE ml.id = %s ) ) ''', (line.id, )) fetched = cr.fetchone() if not fetched: res[line.id] = 'canceled' continue istate = fetched[0] state = 'none' if (istate == 'draft') | (istate == 'proforma'): state = 'waiting' elif istate == 'open': state = 'invoiced' elif istate == 'paid': state = 'paid' inv = inv_obj.browse(cr, uid, fetched[1], context=context) for payment in inv.payment_ids: if payment.invoice_ids and any( inv.type == 'out_refund' for inv in payment.invoice_ids): state = 'canceled' elif istate == 'cancel': state = 'canceled' res[line.id] = state return res _columns = { 'partner': fields.many2one('res.partner', 'Partner', ondelete='cascade', select=1), 'membership_id': fields.many2one('product.product', string="Membership", required=True), 'date_from': fields.date('From', readonly=True), 'date_to': fields.date('To', readonly=True), 'date_cancel': fields.date('Cancel date'), 'date': fields.date('Join Date', help="Date on which member has joined the membership"), 'member_price': fields.float('Membership Fee', digits_compute=dp.get_precision('Product Price'), required=True, help='Amount for the membership'), 'account_invoice_line': fields.many2one('account.invoice.line', 'Account Invoice line', readonly=True), 'account_invoice_id': fields.related('account_invoice_line', 'invoice_id', type='many2one', relation='account.invoice', string='Invoice', readonly=True), 'state': fields.function(_state, string='Membership Status', type='selection', selection=STATE, store={ 'account.invoice': (_get_membership_lines, ['state'], 10), 'res.partner': (_get_partners, ['membership_state'], 12), }, help="""It indicates the membership status. -Non Member: A member who has not applied for any membership. -Cancelled Member: A member who has cancelled his membership. -Old Member: A member whose membership date has expired. -Waiting Member: A member who has applied for the membership and whose invoice is going to be created. -Invoiced Member: A member whose invoice has been created. -Paid Member: A member who has paid the membership amount.""" ), 'company_id': fields.related('account_invoice_line', 'invoice_id', 'company_id', type="many2one", relation="res.company", string="Company", readonly=True, store=True) } _rec_name = 'partner' _order = 'id desc' _constraints = [(_check_membership_date, 'Error, this membership product is out of date', [])]
class res_partner(osv.Model, format_address): _description = 'Partner' _name = "res.partner" def _address_display(self, cr, uid, ids, name, args, context=None): res = {} for partner in self.browse(cr, uid, ids, context=context): res[partner.id] = self._display_address(cr, uid, partner, context=context) return res @api.multi def _get_tz_offset(self, name, args): return dict(( p.id, datetime.datetime.now(pytz.timezone(p.tz or 'GMT')).strftime('%z')) for p in self) def _commercial_partner_compute(self, cr, uid, ids, name, args, context=None): """ Returns the partner that is considered the commercial entity of this partner. The commercial entity holds the master data for all commercial fields (see :py:meth:`~_commercial_fields`) """ result = dict.fromkeys(ids, False) for partner in self.browse(cr, uid, ids, context=context): current_partner = partner while not current_partner.is_company and current_partner.parent_id: current_partner = current_partner.parent_id result[partner.id] = current_partner.id return result def _display_name_compute(self, cr, uid, ids, name, args, context=None): context = dict(context or {}) context.pop('show_address', None) context.pop('show_address_only', None) context.pop('show_email', None) return dict(self.name_get(cr, uid, ids, context=context)) # indirections to avoid passing a copy of the overridable method when declaring the function field _commercial_partner_id = lambda self, *args, **kwargs: self._commercial_partner_compute( *args, **kwargs) _display_name = lambda self, *args, **kwargs: self._display_name_compute( *args, **kwargs) _commercial_partner_store_triggers = { 'res.partner': (lambda self, cr, uid, ids, context=None: self.search( cr, uid, [('id', 'child_of', ids)], context=dict(active_test=False)), ['parent_id', 'is_company'], 10) } _display_name_store_triggers = { 'res.partner': (lambda self, cr, uid, ids, context=None: self.search( cr, uid, [('id', 'child_of', ids)], context=dict(active_test=False)), ['parent_id', 'is_company', 'name'], 10) } _order = "display_name" _columns = { 'name': fields.char('Name', select=True), 'display_name': fields.function(_display_name, type='char', string='Name', store=_display_name_store_triggers, select=True), 'date': fields.date('Date', select=1), 'title': fields.many2one('res.partner.title', 'Title'), 'parent_id': fields.many2one('res.partner', 'Related Company', select=True), 'parent_name': fields.related('parent_id', 'name', type='char', readonly=True, string='Parent name'), 'child_ids': fields.one2many( 'res.partner', 'parent_id', 'Contacts', domain=[ ('active', '=', True) ]), # force "active_test" domain to bypass _search() override 'ref': fields.char('Internal Reference', select=1), 'lang': fields.selection( _lang_get, 'Language', help= "If the selected language is loaded in the system, all documents related to this contact will be printed in this language. If not, it will be English." ), 'tz': fields.selection( _tz_get, 'Timezone', size=64, help= "The partner's timezone, used to output proper date and time values inside printed reports. " "It is important to set a value for this field. You should use the same timezone " "that is otherwise used to pick and render date and time values: your computer's timezone." ), 'tz_offset': fields.function(_get_tz_offset, type='char', size=5, string='Timezone offset', invisible=True), 'user_id': fields.many2one( 'res.users', 'Salesperson', help= 'The internal user that is in charge of communicating with this contact if any.' ), 'vat': fields.char( 'TIN', help= "Tax Identification Number. Fill it if the company is subjected to taxes. Used by the some of the legal statements." ), 'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'), 'website': fields.char('Website', help="Website of Partner or Company"), 'comment': fields.text('Notes'), 'category_id': fields.many2many('res.partner.category', id1='partner_id', id2='category_id', string='Tags'), 'credit_limit': fields.float(string='Credit Limit'), 'barcode': fields.char('Barcode', oldname='ean13'), 'active': fields.boolean('Active'), 'customer': fields.boolean('Is a Customer', help="Check this box if this contact is a customer."), 'supplier': fields.boolean( 'Is a Vendor', help= "Check this box if this contact is a vendor. If it's not checked, purchase people will not see it when encoding a purchase order." ), 'employee': fields.boolean('Employee', help="Check this box if this contact is an Employee."), 'function': fields.char('Job Position'), 'type': fields.selection( [('contact', 'Contact'), ('invoice', 'Invoice address'), ('delivery', 'Shipping address'), ('other', 'Other address')], 'Address Type', help= "Used to select automatically the right address according to the context in sales and purchases documents." ), 'street': fields.char('Street'), 'street2': fields.char('Street2'), 'zip': fields.char('Zip', size=24, change_default=True), 'city': fields.char('City'), 'state_id': fields.many2one("res.country.state", 'State', ondelete='restrict'), 'country_id': fields.many2one('res.country', 'Country', ondelete='restrict'), 'email': fields.char('Email'), 'phone': fields.char('Phone'), 'fax': fields.char('Fax'), 'mobile': fields.char('Mobile'), 'birthdate': fields.char('Birthdate'), 'is_company': fields.boolean( 'Is a Company', help="Check if the contact is a company, otherwise it is a person" ), 'company_type': fields.selection( selection=[('person', 'Individual'), ('company', 'Company')], string='Company Type', help='Technical field, used only to display a boolean using a radio ' 'button. As for Odoo v9 RadioButton cannot be used on boolean ' 'fields, this one serves as interface. Due to the old API ' 'limitations with interface function field, we implement it ' 'by hand instead of a true function field. When migrating to ' 'the new API the code should be simplified.'), 'use_parent_address': fields.boolean( 'Use Company Address', help= "Select this if you want to set company's address information for this contact" ), 'company_id': fields.many2one('res.company', 'Company', select=1), 'color': fields.integer('Color Index'), 'user_ids': fields.one2many('res.users', 'partner_id', 'Users', auto_join=True), 'contact_address': fields.function(_address_display, type='char', string='Complete Address'), # technical field used for managing commercial fields 'commercial_partner_id': fields.function(_commercial_partner_id, type='many2one', relation='res.partner', string='Commercial Entity', store=_commercial_partner_store_triggers) } # image: all image fields are base64 encoded and PIL-supported image = openerp.fields.Binary( "Image", attachment=True, help= "This field holds the image used as avatar for this contact, limited to 1024x1024px", default=lambda self: self._get_default_image(False, True)) image_medium = openerp.fields.Binary("Medium-sized image", attachment=True, help="Medium-sized image of this contact. It is automatically "\ "resized as a 128x128px image, with aspect ratio preserved. "\ "Use this field in form views or some kanban views.") image_small = openerp.fields.Binary("Small-sized image", attachment=True, help="Small-sized image of this contact. It is automatically "\ "resized as a 64x64px image, with aspect ratio preserved. "\ "Use this field anywhere a small image is required.") @api.model def _default_category(self): category_id = self.env.context.get('category_id', False) return [category_id] if category_id else False @api.model def _get_default_image(self, is_company, colorize=False): if getattr(threading.currentThread(), 'testing', False) or self.env.context.get('install_mode'): return False if self.env.context.get('partner_type') == 'delivery': img_path = openerp.modules.get_module_resource( 'base', 'static/src/img', 'truck.png') elif self.env.context.get('partner_type') == 'invoice': img_path = openerp.modules.get_module_resource( 'base', 'static/src/img', 'money.png') else: img_path = openerp.modules.get_module_resource( 'base', 'static/src/img', 'company_image.png' if is_company else 'avatar.png') with open(img_path, 'rb') as f: image = f.read() # colorize user avatars if not is_company and colorize: image = tools.image_colorize(image) return tools.image_resize_image_big(image.encode('base64')) def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): if (not view_id) and (view_type == 'form') and context and context.get( 'force_email', False): view_id = self.pool['ir.model.data'].get_object_reference( cr, user, 'base', 'view_partner_simple_form')[1] res = super(res_partner, self).fields_view_get(cr, user, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) if view_type == 'form': res['arch'] = self.fields_view_get_address(cr, user, res['arch'], context=context) return res @api.model def _default_company(self): return self.env['res.company']._company_default_get('res.partner') _defaults = { 'active': True, 'lang': api.model(lambda self: self.env.lang), 'tz': api.model(lambda self: self.env.context.get('tz', False)), 'customer': True, 'category_id': _default_category, 'company_id': _default_company, 'color': 0, 'is_company': False, 'company_type': 'person', 'type': 'contact', 'image': False, } _constraints = [ (osv.osv._check_recursion, 'You cannot create recursive Partner hierarchies.', ['parent_id']), ] _sql_constraints = [ ('check_name', "CHECK( (type='contact' AND name IS NOT NULL) or (type!='contact') )", 'Contacts require a name.'), ] @api.one def copy(self, default=None): default = dict(default or {}) default['name'] = _('%s (copy)') % self.name return super(res_partner, self).copy(default) def onchange_parent_id(self, cr, uid, ids, parent_id, context=None): def value_or_id(val): """ return val or val.id if val is a browse record """ return val if isinstance(val, (bool, int, long, float, basestring)) else val.id if not parent_id or not ids: return {'value': {}} if parent_id: result = {} partner = self.browse(cr, uid, ids[0], context=context) if partner.parent_id and partner.parent_id.id != parent_id: result['warning'] = { 'title': _('Warning'), 'message': _('Changing the company of a contact should only be done if it ' 'was never correctly set. If an existing contact starts working for a new ' 'company then a new contact should be created under that new ' 'company. You can use the "Discard" button to abandon this change.' ) } # for contacts: copy the parent address, if set (aka, at least # one value is set in the address: otherwise, keep the one from # the contact) if partner.type == 'contact': parent = self.browse(cr, uid, parent_id, context=context) address_fields = self._address_fields(cr, uid, context=context) if any(parent[key] for key in address_fields): result['value'] = dict((key, value_or_id(parent[key])) for key in address_fields) return result @api.multi def onchange_state(self, state_id): if state_id: state = self.env['res.country.state'].browse(state_id) return {'value': {'country_id': state.country_id.id}} return {'value': {}} @api.multi def on_change_company_type(self, company_type): return {'value': {'is_company': company_type == 'company'}} def _update_fields_values(self, cr, uid, partner, fields, context=None): """ Returns dict of write() values for synchronizing ``fields`` """ values = {} for fname in fields: field = self._fields[fname] if field.type == 'one2many': raise AssertionError( 'One2Many fields cannot be synchronized as part of `commercial_fields` or `address fields`' ) if field.type == 'many2one': values[fname] = partner[fname].id if partner[fname] else False elif field.type == 'many2many': values[fname] = [(6, 0, [r.id for r in partner[fname] or []])] else: values[fname] = partner[fname] return values def _address_fields(self, cr, uid, context=None): """ Returns the list of address fields that are synced from the parent when the `use_parent_address` flag is set. """ return list(ADDRESS_FIELDS) def update_address(self, cr, uid, ids, vals, context=None): address_fields = self._address_fields(cr, uid, context=context) addr_vals = dict( (key, vals[key]) for key in address_fields if key in vals) if addr_vals: return super(res_partner, self).write(cr, uid, ids, addr_vals, context) def _commercial_fields(self, cr, uid, context=None): """ Returns the list of fields that are managed by the commercial entity to which a partner belongs. These fields are meant to be hidden on partners that aren't `commercial entities` themselves, and will be delegated to the parent `commercial entity`. The list is meant to be extended by inheriting classes. """ return ['vat', 'credit_limit'] def _commercial_sync_from_company(self, cr, uid, partner, context=None): """ Handle sync of commercial fields when a new parent commercial entity is set, as if they were related fields """ commercial_partner = partner.commercial_partner_id if not commercial_partner: # On child partner creation of a parent partner, # the commercial_partner_id is not yet computed commercial_partner_id = self._commercial_partner_compute( cr, uid, [partner.id], 'commercial_partner_id', [], context=context)[partner.id] commercial_partner = self.browse(cr, uid, commercial_partner_id, context=context) if commercial_partner != partner: commercial_fields = self._commercial_fields(cr, uid, context=context) sync_vals = self._update_fields_values(cr, uid, commercial_partner, commercial_fields, context=context) partner.write(sync_vals) def _commercial_sync_to_children(self, cr, uid, partner, context=None): """ Handle sync of commercial fields to descendants """ commercial_fields = self._commercial_fields(cr, uid, context=context) commercial_partner = partner.commercial_partner_id if not commercial_partner: # On child partner creation of a parent partner, # the commercial_partner_id is not yet computed commercial_partner_id = self._commercial_partner_compute( cr, uid, [partner.id], 'commercial_partner_id', [], context=context)[partner.id] commercial_partner = self.browse(cr, uid, commercial_partner_id, context=context) sync_vals = self._update_fields_values(cr, uid, commercial_partner, commercial_fields, context=context) sync_children = [c for c in partner.child_ids if not c.is_company] for child in sync_children: self._commercial_sync_to_children(cr, uid, child, context=context) return self.write(cr, uid, [c.id for c in sync_children], sync_vals, context=context) def _fields_sync(self, cr, uid, partner, update_values, context=None): """ Sync commercial fields and address fields from company and to children after create/update, just as if those were all modeled as fields.related to the parent """ # 1. From UPSTREAM: sync from parent if update_values.get('parent_id') or update_values.get( 'type', 'contact' ): # TDE/ fp change to check, get default value not sure # 1a. Commercial fields: sync if parent changed if update_values.get('parent_id'): self._commercial_sync_from_company(cr, uid, partner, context=context) # 1b. Address fields: sync if parent or use_parent changed *and* both are now set if partner.parent_id and partner.type == 'contact': onchange_vals = self.onchange_parent_id( cr, uid, [partner.id], parent_id=partner.parent_id.id, context=context).get('value', {}) partner.update_address(onchange_vals) # 2. To DOWNSTREAM: sync children if partner.child_ids: # 2a. Commercial Fields: sync if commercial entity if partner.commercial_partner_id == partner: commercial_fields = self._commercial_fields(cr, uid, context=context) if any(field in update_values for field in commercial_fields): self._commercial_sync_to_children(cr, uid, partner, context=context) # 2b. Address fields: sync if address changed address_fields = self._address_fields(cr, uid, context=context) if any(field in update_values for field in address_fields): domain_children = [('parent_id', '=', partner.id), ('type', '=', 'contact')] update_ids = self.search(cr, uid, domain_children, context=context) self.update_address(cr, uid, update_ids, update_values, context=context) def _handle_first_contact_creation(self, cr, uid, partner, context=None): """ On creation of first contact for a company (or root) that has no address, assume contact address was meant to be company address """ parent = partner.parent_id address_fields = self._address_fields(cr, uid, context=context) if parent and (parent.is_company or not parent.parent_id) and len(parent.child_ids) == 1 and \ any(partner[f] for f in address_fields) and not any(parent[f] for f in address_fields): addr_vals = self._update_fields_values(cr, uid, partner, address_fields, context=context) parent.update_address(addr_vals) def _clean_website(self, website): (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(website) if not scheme: if not netloc: netloc, path = path, '' website = urlparse.urlunparse( ('http', netloc, path, params, query, fragment)) return website @api.multi def write(self, vals): # res.partner must only allow to set the company_id of a partner if it # is the same as the company of all users that inherit from this partner # (this is to allow the code from res_users to write to the partner!) or # if setting the company_id to False (this is compatible with any user # company) if vals.get('website'): vals['website'] = self._clean_website(vals['website']) if vals.get('company_id'): company = self.env['res.company'].browse(vals['company_id']) for partner in self: if partner.user_ids: companies = set(user.company_id for user in partner.user_ids) if len(companies) > 1 or company not in companies: raise UserError( _("You can not change the company as the partner/user has multiple user linked with different companies." )) # function field implemented by hand -> remove my when migrating c_type = vals.get('company_type') is_company = vals.get('is_company') if c_type: vals['is_company'] = c_type == 'company' elif 'is_company' in vals: vals['company_type'] = is_company and 'company' or 'person' tools.image_resize_images(vals) result = super(res_partner, self).write(vals) for partner in self: if any( u.has_group('base.group_user') for u in partner.user_ids if u != self.env.user): self.env['res.users'].check_access_rights('write') self._fields_sync(partner, vals) return result @api.model def create(self, vals): if vals.get('type') in ['delivery', 'invoice' ] and not vals.get('image'): # force no colorize for images with no transparency vals['image'] = self.with_context( partner_type=vals['type'])._get_default_image(False, False) if vals.get('website'): vals['website'] = self._clean_website(vals['website']) # function field not correctly triggered at create -> remove me when # migrating to the new API c_type = vals.get('company_type', self._context.get('default_company_type')) is_company = vals.get('is_company', self._context.get('default_is_company')) if c_type: vals['is_company'] = c_type == 'company' else: vals['company_type'] = is_company and 'company' or 'person' tools.image_resize_images(vals) partner = super(res_partner, self).create(vals) self._fields_sync(partner, vals) self._handle_first_contact_creation(partner) return partner def open_commercial_entity(self, cr, uid, ids, context=None): """ Utility method used to add an "Open Company" button in partner views """ partner = self.browse(cr, uid, ids[0], context=context) return { 'type': 'ir.actions.act_window', 'res_model': 'res.partner', 'view_mode': 'form', 'res_id': partner.commercial_partner_id.id, 'target': 'current', 'flags': { 'form': { 'action_buttons': True } } } def open_parent(self, cr, uid, ids, context=None): """ Utility method used to add an "Open Parent" button in partner views """ partner = self.browse(cr, uid, ids[0], context=context) address_form_id = self.pool['ir.model.data'].xmlid_to_res_id( cr, uid, 'base.view_partner_address_form') return { 'type': 'ir.actions.act_window', 'res_model': 'res.partner', 'view_mode': 'form', 'views': [(address_form_id, 'form')], 'res_id': partner.parent_id.id, 'target': 'new', 'flags': { 'form': { 'action_buttons': True } } } def name_get(self, cr, uid, ids, context=None): if context is None: context = {} if isinstance(ids, (int, long)): ids = [ids] res = [] types_dict = dict( self.fields_get(cr, uid, context=context)['type']['selection']) for record in self.browse(cr, uid, ids, context=context): name = record.name or '' if record.parent_id and not record.is_company: if not name and record.type in [ 'invoice', 'delivery', 'other' ]: name = types_dict[record.type] name = "%s, %s" % (record.parent_name, name) if context.get('show_address_only'): name = self._display_address(cr, uid, record, without_company=True, context=context) if context.get('show_address'): name = name + "\n" + self._display_address( cr, uid, record, without_company=True, context=context) name = name.replace('\n\n', '\n') name = name.replace('\n\n', '\n') if context.get('show_email') and record.email: name = "%s <%s>" % (name, record.email) if context.get('html_format'): name = name.replace('\n', '<br/>') res.append((record.id, name)) return res def _parse_partner_name(self, text, context=None): """ Supported syntax: - 'Raoul <*****@*****.**>': will find name and email address - otherwise: default, everything is set as the name """ emails = tools.email_split(text.replace(' ', ',')) if emails: email = emails[0] name = text[:text.index(email)].replace('"', '').replace('<', '').strip() else: name, email = text, '' return name, email def name_create(self, cr, uid, name, context=None): """ Override of orm's name_create method for partners. The purpose is to handle some basic formats to create partners using the name_create. If only an email address is received and that the regex cannot find a name, the name will have the email value. If 'force_email' key in context: must find the email address. """ if context is None: context = {} name, email = self._parse_partner_name(name, context=context) if context.get('force_email') and not email: raise UserError( _("Couldn't create contact without email address!")) if not name and email: name = email rec_id = self.create(cr, uid, { self._rec_name: name or email, 'email': email or False }, context=context) return self.name_get(cr, uid, [rec_id], context)[0] def _search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None): """ Override search() to always show inactive children when searching via ``child_of`` operator. The ORM will always call search() with a simple domain of the form [('parent_id', 'in', [ids])]. """ # a special ``domain`` is set on the ``child_ids`` o2m to bypass this logic, as it uses similar domain expressions if len(args) == 1 and len(args[0]) == 3 and args[0][:2] == ('parent_id','in') \ and args[0][2] != [False]: context = dict(context or {}, active_test=False) return super(res_partner, self)._search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count, access_rights_uid=access_rights_uid) def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100): if not args: args = [] if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'): self.check_access_rights(cr, uid, 'read') where_query = self._where_calc(cr, uid, args, context=context) self._apply_ir_rules(cr, uid, where_query, 'read', context=context) from_clause, where_clause, where_clause_params = where_query.get_sql( ) where_str = where_clause and (" WHERE %s AND " % where_clause) or ' WHERE ' # search on the name of the contacts and of its company search_name = name if operator in ('ilike', 'like'): search_name = '%%%s%%' % name if operator in ('=ilike', '=like'): operator = operator[1:] unaccent = get_unaccent_wrapper(cr) query = """SELECT id FROM res_partner {where} ({email} {operator} {percent} OR {display_name} {operator} {percent} OR {reference} {operator} {percent}) -- don't panic, trust postgres bitmap ORDER BY {display_name} {operator} {percent} desc, {display_name} """.format(where=where_str, operator=operator, email=unaccent('email'), display_name=unaccent('display_name'), reference=unaccent('ref'), percent=unaccent('%s')) where_clause_params += [search_name] * 4 if limit: query += ' limit %s' where_clause_params.append(limit) cr.execute(query, where_clause_params) ids = map(lambda x: x[0], cr.fetchall()) if ids: return self.name_get(cr, uid, ids, context) else: return [] return super(res_partner, self).name_search(cr, uid, name, args, operator=operator, context=context, limit=limit) def find_or_create(self, cr, uid, email, context=None): """ Find a partner with the given ``email`` or use :py:method:`~.name_create` to create one :param str email: email-like string, which should contain at least one email, e.g. ``"Raoul Grosbedon <*****@*****.**>"``""" assert email, 'an email is required for find_or_create to work' emails = tools.email_split(email) if emails: email = emails[0] ids = self.search(cr, uid, [('email', '=ilike', email)], context=context) if not ids: return self.name_create(cr, uid, email, context=context)[0] return ids[0] def _email_send(self, cr, uid, ids, email_from, subject, body, on_error=None): partners = self.browse(cr, uid, ids) for partner in partners: if partner.email: tools.email_send(email_from, [partner.email], subject, body, on_error) return True def email_send(self, cr, uid, ids, email_from, subject, body, on_error=''): while len(ids): self.pool['ir.cron'].create( cr, uid, { 'name': 'Send Partner Emails', 'user_id': uid, 'model': 'res.partner', 'function': '_email_send', 'args': repr( [ids[:16], email_from, subject, body, on_error]) }) ids = ids[16:] return True def address_get(self, cr, uid, ids, adr_pref=None, context=None): """ Find contacts/addresses of the right type(s) by doing a depth-first-search through descendants within company boundaries (stop at entities flagged ``is_company``) then continuing the search at the ancestors that are within the same company boundaries. Defaults to partners of type ``'default'`` when the exact type is not found, or to the provided partner itself if no type ``'default'`` is found either. """ adr_pref = set(adr_pref or []) if 'contact' not in adr_pref: adr_pref.add('contact') result = {} visited = set() if isinstance(ids, (int, long)): ids = [ids] for partner in self.browse(cr, uid, filter(None, ids), context=context): current_partner = partner while current_partner: to_scan = [current_partner] # Scan descendants, DFS while to_scan: record = to_scan.pop(0) visited.add(record) if record.type in adr_pref and not result.get(record.type): result[record.type] = record.id if len(result) == len(adr_pref): return result to_scan = [ c for c in record.child_ids if c not in visited if not c.is_company ] + to_scan # Continue scanning at ancestor if current_partner is not a commercial entity if current_partner.is_company or not current_partner.parent_id: break current_partner = current_partner.parent_id # default to type 'contact' or the partner itself default = result.get('contact', ids and ids[0] or False) for adr_type in adr_pref: result[adr_type] = result.get(adr_type) or default return result def view_header_get(self, cr, uid, view_id, view_type, context): res = super(res_partner, self).view_header_get(cr, uid, view_id, view_type, context) if res: return res if not context.get('category_id', False): return False return _('Partners: ') + self.pool['res.partner.category'].browse( cr, uid, context['category_id'], context).name @api.model @api.returns('self') def main_partner(self): ''' Return the main partner ''' return self.env.ref('base.main_partner') def _display_address(self, cr, uid, address, without_company=False, context=None): ''' The purpose of this function is to build and return an address formatted accordingly to the standards of the country where it belongs. :param address: browse record of the res.partner to format :returns: the address formatted in a display that fit its country habits (or the default ones if not country is specified) :rtype: string ''' # get the information that will be injected into the display format # get the address format address_format = address.country_id.address_format or \ "%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s" args = { 'state_code': address.state_id.code or '', 'state_name': address.state_id.name or '', 'country_code': address.country_id.code or '', 'country_name': address.country_id.name or '', 'company_name': address.parent_name or '', } for field in self._address_fields(cr, uid, context=context): args[field] = getattr(address, field) or '' if without_company: args['company_name'] = '' elif address.parent_id: address_format = '%(company_name)s\n' + address_format return address_format % args