class SubscriptionLine(metaclass=PoolMeta): __name__ = 'sale.subscription.line' asset_lot = fields.Many2One( 'stock.lot', "Asset Lot", domain=[ ('subscription_services', '=', Eval('service')), ], states={ 'required': ((Eval('subscription_state') == 'running') & Eval('asset_lot_required')), 'invisible': ~Eval('asset_lot_required'), 'readonly': Eval('subscription_state') != 'draft', }, depends=['service', 'subscription_state', 'asset_lot_required']) asset_lot_required = fields.Function(fields.Boolean("Asset Lot Required"), 'on_change_with_asset_lot_required') @classmethod def __setup__(cls): super(SubscriptionLine, cls).__setup__() cls.quantity.domain = [ cls.quantity.domain, If(Bool(Eval('asset_lot')), ('quantity', '=', 1), ()), ] cls.quantity.depends.append('asset_lot') @fields.depends('service') def on_change_with_asset_lot_required(self, name=None): if not self.service: return False return bool(self.service.asset_lots) @classmethod def copy(cls, lines, default=None): if default is None: default = {} else: default = default.copy() default.setdefault('lot') return super(SubscriptionLine, cls).copy(lines, default) @classmethod def validate(cls, lines): super(SubscriptionLine, cls).validate(lines) cls._validate_dates(lines) @classmethod def _validate_dates(cls, lines): transaction = Transaction() connection = transaction.connection cursor = connection.cursor() transaction.database.lock(connection, cls._table) line = cls.__table__() other = cls.__table__() overlap_where = (((line.end_date == Null) & ((other.end_date == Null) | (other.start_date > line.start_date) | (other.end_date > line.start_date))) | ((line.end_date != Null) & (((other.end_date == Null) & (other.start_date < line.end_date)) | ((other.end_date != Null) & (((other.end_date >= line.start_date) & (other.end_date < line.end_date)) | ((other.start_date >= line.start_date) & (other.start_date < line.end_date))))))) for sub_lines in grouped_slice(lines): sub_ids = [l.id for l in sub_lines] cursor.execute( *line.join(other, condition=((line.id != other.id) & (line.asset_lot == other.asset_lot)) ).select(line.id, other.id, where=((line.asset_lot != Null) & reduce_ids(line.id, sub_ids) & overlap_where), limit=1)) overlapping = cursor.fetchone() if overlapping: sline1, sline2 = cls.browse(overlapping) raise ValidationError( gettext('sale_subscription_asset.msg_asset_line_overlap', line1=sline1.rec_name, line2=sline2.rec_name))
class Template: "Product Template" __metaclass__ = PoolMeta __name__ = 'product.template' manufacturers_code = fields.Char('Manufacturers_code') medical_insurance_code = fields.Char('Medical_insurance_code', select=True) attach = fields.Char('Attach', select=True) concentration = fields.Char('Concentration', select=True) # concentration_unit = fields.Char('Concentration_unit', select=True) dose = fields.Char('Dose', select=True) # dose_unit = fields.Char('Dose_unit', select=True) retrieve_the_code = fields.Char('Retrieve_the_code', select=True, required=True) capacity = fields.Char('Capacity', select=True, required=False) # drug_specifications = fields.Function( fields.Char('Drug_specifications', select=True, readonly=True), 'get_drug_specifications') is_direct_sending = fields.Boolean('Is_direct_sending', select=True) is_antimicrobials = fields.Boolean('Is_antimicrobials', select=True) a_charge = fields.Integer('A_charge', select=True) drup_level = fields.Char('Drup_level', select=True) compound_dose = fields.Float('Compound_dose', select=True) purchase_code = fields.Char('purchase_code', select=True, required=True) retail_package = fields.Many2One('product.uom', 'Retail Package') medical_insurance_description = fields.Char( 'Medical_insurance_description', select=True) new_term = fields.Char('new_term', select=True) state = fields.Char('State', select=True, required=False) min_Package = fields.Many2One('product.uom', 'Min_Package', required=True) dosage_form_description = fields.Char('Dosage_form_description', select=True) manufacturers_describtion = fields.Char('Manufacturers_describtion', select=True, required=False) poison_hemp_spirit = fields.Selection([('common', u'普通'), ('narcosis', u'麻醉'), ('spirit_one', u'精神1'), ('spirit_two', u'精神2')], 'Poison_hemp_spirit', select=True) national_essential_medicines = fields.Boolean( 'National_essential_medicines', select=True) interim = fields.Selection([('1', u''), ('2', u'是')], 'interim', select=True) approval_number = fields.Char('Approval number') is_atict = fields.Boolean('is_atict') homemade = fields.Boolean('homemade') def get_drug_specifications(self, name): if self.dose != None and self.dose_unit != '': drug_specificationss = str(self.dose.encode('utf-8')) + str( (self.dose_unit).encode('utf-8')) + '*' + str( (self.capacity).encode('utf-8')) + str( self.min_Package.name.encode('utf-8')) elif self.concentration != None and self.concentration_unit != None: drug_specificationss = str(self.concentration).encode('utf-8') + ( self.concentration_unit).encode('utf-8') + '*' + str( (self.capacity).encode('utf-8')) + str( self.min_Package.name.encode('utf-8')) else: drug_specificationss = '*' + str( (self.capacity).encode('utf-8')) + str( self.min_Package.name.encode('utf-8')) return drug_specificationss
class ActionActWindowDomain(sequence_ordered(), DeactivableMixin, ModelSQL, ModelView): "Action act window domain" __name__ = 'ir.action.act_window.domain' name = fields.Char('Name', translate=True) domain = fields.Char('Domain') count = fields.Boolean('Count') act_window = fields.Many2One('ir.action.act_window', 'Action', select=True, required=True, ondelete='CASCADE') @classmethod def __register__(cls, module_name): super().__register__(module_name) table = cls.__table_handler__(module_name) # Migration from 5.0: remove required on sequence table.not_null_action('sequence', 'remove') @classmethod def default_count(cls): return False @classmethod def validate(cls, actions): super(ActionActWindowDomain, cls).validate(actions) cls.check_domain(actions) @classmethod def check_domain(cls, actions): for action in actions: if not action.domain: continue try: value = PYSONDecoder().decode(action.domain) except Exception: value = None if isinstance(value, PYSON): if not value.types() == set([list]): value = None elif not isinstance(value, list): value = None else: try: fields.domain_validate(value) except Exception: value = None if value is None: raise DomainError( gettext('ir.msg_action_invalid_domain', domain=action.domain, action=action.rec_name)) @classmethod def create(cls, vlist): pool = Pool() domains = super(ActionActWindowDomain, cls).create(vlist) pool.get('ir.action.keyword')._get_keyword_cache.clear() return domains @classmethod def write(cls, domains, values, *args): pool = Pool() super(ActionActWindowDomain, cls).write(domains, values, *args) pool.get('ir.action.keyword')._get_keyword_cache.clear() @classmethod def delete(cls, domains): pool = Pool() super(ActionActWindowDomain, cls).delete(domains) pool.get('ir.action.keyword')._get_keyword_cache.clear()
class EncounterComponentType(ModelSQL, ModelView): '''Encounter Component Stores encounter component type definitions. The Encounter models and views use this model to determine which components are available ''' __name__ = 'gnuhealth.encounter.component_type' name = fields.Char('Type Name') code = fields.Char('Code', size=15, help="Short name Displayed in the first column of the" "encounter. Maximum 15 characters") model = fields.Char('Model name', help='e.g. gnuhealth.encounter.clinical', required=True) view_form = fields.Char('View name', required=True, help='full xml id of view, e.g. module.xml_id') ordering = fields.Integer('Display order') active = fields.Boolean('Active') @classmethod def __setup__(cls): super(EncounterComponentType, cls).__setup__() cls._order = [('ordering', 'ASC'), ('name', 'ASC')] @classmethod def register_type(cls, model_class, view): # first check if it was previously registered and deactivated registration = cls.search([('model', '=', model_class.__name__), ('view_form', '=', view)]) if registration: registration = cls.browse(registration) if not registration.active: cls.write(registration, {'active': True}) else: cdata = {'model': model_class.__name__, 'view_form': view} cdata['name'] = ''.join( filter(None, model_class.__doc__.split('\n'))[:1]) cdata['code'] = cdata['name'][:15] # we need to create the registration cls.create([cdata]) return True @classmethod def get_selection_list(cls): '''returns a list of active Encounter component types in a tuple of (id, Name, Code, model)''' try: return cls._component_type_list except AttributeError: pass cursor = Transaction().cursor TableHandler = backend.get('TableHandler') if not TableHandler.table_exist(cursor, 'gnuhealth_encounter_component_type'): return [] ectypes = cls.search_read([('active', '=', True)], fields_names=['id', 'name', 'code', 'model'], order=[('ordering', 'ASC'), ('name', 'ASC')]) cls._component_type_list = [ ComponentTypeInfo(x['id'], x['name'], x['code'], x['model']) for x in ectypes ] return cls._component_type_list @classmethod def get_view_name(cls, ids): '''returns the name of the view used to edit/display a component type''' if not isinstance(ids, (list, tuple)): ids = [ids] # ids = map(int, ids) forms = cls.read(ids, fields_names=['view_form']) if forms: return forms[0]['view_form'] return None
class Line: __metaclass__ = PoolMeta __name__ = 'account.move.line' reverse_moves = fields.Function(fields.Boolean('With Reverse Moves'), 'get_reverse_moves', searcher='search_reverse_moves') netting_moves = fields.Function(fields.Boolean('With Netting Moves'), 'get_netting_moves', searcher='search_netting_moves') def get_reverse_moves(self, name): if (not self.account or self.account.kind not in ['receivable', 'payable']): return False domain = [ ('account', '=', self.account.id), ('reconciliation', '=', None), ] if self.party: domain.append(('party', '=', self.party.id)) if self.credit > Decimal('0.0'): domain.append(('debit', '>', 0)) if self.debit > Decimal('0.0'): domain.append(('credit', '>', 0)) moves = self.search(domain, limit=1) return len(moves) > 0 @classmethod def search_reverse_moves(cls, name, clause): operator = 'in' if clause[2] else 'not in' query = """ SELECT id FROM account_move_line l WHERE (account, party) IN ( SELECT aa.id, aml.party FROM account_account aa, account_move_line aml WHERE aa.reconcile AND aa.id = aml.account AND aml.reconciliation IS NULL GROUP BY aa.id, aml.party HAVING bool_or(aml.debit <> 0) AND bool_or(aml.credit <> 0) ) """ cursor = Transaction().connection.cursor() cursor.execute(query) return [('id', operator, [x[0] for x in cursor.fetchall()])] def get_netting_moves(self, name): if (not self.account or not self.account.kind in ['receivable', 'payable']): return False if not self.account.party_required: return False domain = [ ('party', '=', self.party.id), ('reconciliation', '=', None), ] if self.credit > Decimal('0.0'): domain.append(('debit', '>', 0)) if self.debit > Decimal('0.0'): domain.append(('credit', '>', 0)) moves = self.search(domain, limit=1) return len(moves) > 0 @classmethod def search_netting_moves(cls, name, clause): operator = 'in' if clause[2] else 'not in' query = """ SELECT id FROM account_move_line l WHERE party IN ( SELECT aml.party FROM account_account aa, account_move_line aml WHERE aa.reconcile AND aa.id = aml.account AND aml.reconciliation IS NULL GROUP BY aml.party HAVING bool_or(aml.debit <> 0) AND bool_or(aml.credit <> 0) ) """ cursor = Transaction().connection.cursor() cursor.execute(query) return [('id', operator, [x[0] for x in cursor.fetchall()])]
class Component(ModelSQL, ModelView): 'Component' __name__ = 'lims.component' equipment = fields.Many2One('lims.equipment', 'Equipment', required=True, ondelete='CASCADE', select=True) kind = fields.Many2One('lims.component.kind', 'Kind', required=True) location = fields.Many2One('lims.component.location', 'Location') product_type = fields.Function( fields.Many2One('lims.product.type', 'Product type'), 'get_product_type') comercial_product = fields.Many2One('lims.comercial.product', 'Comercial product') capacity = fields.Char('Capacity (lts)') serial_number = fields.Char('Serial number') model = fields.Char('Model') power = fields.Char('Power') brand = fields.Many2One('lims.brand', 'Brand') internal_id = fields.Char('Internal ID Code') customer_description = fields.Char('Customer description') year_manufacturing = fields.Integer('Year of manufacturing') plant = fields.Function(fields.Many2One('lims.plant', 'Plant'), 'get_plant', searcher='search_plant') party = fields.Function(fields.Many2One('party.party', 'Party'), 'get_party', searcher='search_party') missing_data = fields.Boolean('Missing data') @classmethod def __setup__(cls): super().__setup__() cls._order.insert(0, ('equipment', 'ASC')) cls._order.insert(1, ('kind', 'ASC')) t = cls.__table__() cls._sql_constraints = [ ('kind_location_description_unique', Unique(t, t.equipment, t.kind, t.location, t.customer_description), 'lims_industry.msg_component_unique'), ] @classmethod def __register__(cls, module_name): table_h = cls.__table_handler__(module_name) type_exist = table_h.column_exist('type') super().__register__(module_name) if type_exist: cursor = Transaction().connection.cursor() ComponentType = Pool().get('lims.component.type') cursor.execute('UPDATE "' + cls._table + '" c ' 'SET kind = ct.kind, location = ct.location ' 'FROM "' + ComponentType._table + '" ct ' 'WHERE ct.id = c.type') table_h.drop_constraint('type_unique') table_h.drop_constraint('type_description_unique') table_h.drop_column('type') @classmethod def create(cls, vlist): TaskTemplate = Pool().get('lims.administrative.task.template') components = super().create(vlist) TaskTemplate.create_tasks('component_missing_data', cls._for_task_missing_data(components)) return components @classmethod def _for_task_missing_data(cls, components): AdministrativeTask = Pool().get('lims.administrative.task') res = [] for component in components: if not component.missing_data: continue if AdministrativeTask.search([ ('type', '=', 'component_missing_data'), ('origin', '=', '%s,%s' % (cls.__name__, component.id)), ('state', 'not in', ('done', 'discarded')), ]): continue res.append(component) return res @classmethod def delete(cls, components): cls.check_delete(components) super().delete(components) @classmethod def check_delete(cls, components): Sample = Pool().get('lims.sample') for component in components: samples = Sample.search_count([ ('component', '=', component.id), ]) if samples != 0: raise UserError( gettext('lims_industry.msg_delete_component', component=component.get_rec_name(None))) @classmethod def copy(cls, records, default=None): if default is None: default = {} current_default = default.copy() new_records = [] for record in records: current_default['customer_description'] = '%s (copy)' % ( record.customer_description) new_record, = super().copy([record], default=current_default) new_records.append(new_record) return new_records def get_rec_name(self, name): res = self.kind.rec_name if self.location: res += ' ' + self.location.name if self.brand: res += ' - ' + self.brand.rec_name if self.model: res += ' - ' + self.model if self.customer_description: res += ' [' + self.customer_description + ']' return res @classmethod def search_rec_name(cls, name, clause): return [ 'OR', ('kind.name', ) + tuple(clause[1:]), ('location.name', ) + tuple(clause[1:]), ('brand.name', ) + tuple(clause[1:]), ('model', ) + tuple(clause[1:]), ('customer_description', ) + tuple(clause[1:]), ] @classmethod def get_plant(cls, component, name): result = {} for c in component: result[c.id] = c.equipment and c.equipment.plant.id or None return result @classmethod def search_plant(cls, name, clause): return [('equipment.plant', ) + tuple(clause[1:])] @classmethod def get_party(cls, component, name): result = {} for c in component: result[c.id] = c.equipment and c.equipment.plant.party.id or None return result @classmethod def search_party(cls, name, clause): return [('equipment.plant.party', ) + tuple(clause[1:])] @fields.depends('kind', '_parent_kind.product_type') def on_change_with_product_type(self, name=None): return self.get_product_type([self], name)[self.id] @classmethod def get_product_type(cls, components, name): result = {} for c in components: result[c.id] = c.kind and c.kind.product_type.id or None return result
class SupportRequest (ModelSQL, ModelView): 'Support Request Registration' __name__ = 'gnuhealth.support_request' _rec_name = 'code' code = fields.Char('Code',help='Request Code', readonly=True) operator = fields.Many2One( 'gnuhealth.healthprofessional', 'Operator', help="Operator who took the call / support request") requestor = fields.Many2One('party.party', 'Requestor', domain=[('is_person', '=', True)], help="Related party (person)") patient = fields.Many2One('gnuhealth.patient', 'Patient') evaluation = fields.Many2One('gnuhealth.patient.evaluation', 'Evaluation', domain=[('patient', '=', Eval('patient'))], depends=['patient'], help='Related Patient Evaluation') request_date = fields.DateTime('Date', required=True, help="Date and time of the call for help") operational_sector = fields.Many2One('gnuhealth.operational_sector', 'O. Sector',help="Operational Sector") latitude = fields.Numeric('Latidude', digits=(3, 14)) longitude = fields.Numeric('Longitude', digits=(4, 14)) address = fields.Text("Address", help="Free text address / location") urladdr = fields.Char( 'OSM Map', help="Maps the location on Open Street Map") healthcenter = fields.Many2One('gnuhealth.institution','Calling Institution') patient_sex = fields.Function( fields.Char('Sex'), 'get_patient_sex') patient_age = fields.Function( fields.Char('Age'), 'get_patient_age') complaint = fields.Function( fields.Char('Chief Complaint'), 'get_patient_complaint') urgency = fields.Selection([ (None, ''), ('low', 'Low'), ('urgent', 'Urgent'), ('emergency', 'Emergency'), ], 'Urgency', sort=False) place_occurrance = fields.Selection([ (None, ''), ('home', 'Home'), ('street', 'Street'), ('institution', 'Institution'), ('school', 'School'), ('commerce', 'Commercial Area'), ('recreational', 'Recreational Area'), ('transportation', 'Public transportation'), ('sports', 'Sports event'), ('publicbuilding', 'Public Building'), ('unknown', 'Unknown'), ], 'Origin', help="Place of occurrance",sort=False) event_type = fields.Selection([ (None, ''), ('event1', 'Acute Coronary Syndrome'), ('event2', 'Acute pain'), ('event3', 'Acute illness'), ('event4', 'Allergic reaction'), ('event5', 'Bullying, battering'), ('event6', 'Gastrointestinal event'), ('event7', 'Endocrine event (diabetes, adrenal crisis, ..)'), ('event8', 'Choke'), ('event9', 'Domestic violence'), ('event10', 'Environmental event (weather, animals, ...)'), ('event11', 'Sexual assault'), ('event12', 'Drug intoxication'), ('event13', 'Robbery, violent assault'), ('event14', 'Respiratory distress'), ('event15', 'Pregnancy related event'), ('event16', 'Gas intoxication'), ('event17', 'Food intoxication'), ('event18', 'Neurological event (stroke, TIA, seizure, ...)'), ('event19', 'Chronic illness'), ('event20', 'Near drowning'), ('event21', 'Eye, Ear and Nose event'), ('event22', 'Fall'), ('event23', 'Deceased person'), ('event24', 'Psychiatric event'), ('event25', 'Suicide attempt'), ('event26', 'Fire'), ('event27', 'Transportation accident'), ('event28', 'Traumatic Injuries'), ('event29', 'Explosion'), ('event30', 'Other specified'), ], 'Event type') event_specific = fields.Many2One ('gnuhealth.pathology','Incident') multiple_casualties = fields.Boolean('Multiple Casualties') request_actions = fields.One2Many( 'gnuhealth.support_request.log', 'sr', 'Activities', help='Support request activity log') ambulances = fields.One2Many( 'gnuhealth.ambulance.support', 'sr', 'Ambulances', help='Ambulances requested in this Support Request') request_extra_info = fields.Text('Details') state = fields.Selection([ (None, ''), ('open', 'Open'), ('closed', 'Closed'), ], 'State', sort=False, readonly=True) @staticmethod def default_request_date(): return datetime.now() def get_patient_sex(self, name): if self.patient: return self.patient.gender def get_patient_age(self, name): if self.patient: return self.patient.name.age def get_patient_complaint(self, name): if self.evaluation: if self.evaluation.chief_complaint: return self.evaluation.chief_complaint @staticmethod def default_operator(): pool = Pool() HealthProf= pool.get('gnuhealth.healthprofessional') operator = HealthProf.get_health_professional() return operator @staticmethod def default_state(): return 'open' @fields.depends('latitude', 'longitude') def on_change_with_urladdr(self): # Generates the URL to be used in OpenStreetMap # The address will be mapped to the URL in the following way # If the latitud and longitude of the Accident / Injury # are given, then those parameters will be used. ret_url = '' if (self.latitude and self.longitude): ret_url = 'http://openstreetmap.org/?mlat=' + \ str(self.latitude) + '&mlon=' + str(self.longitude) return ret_url @classmethod def create(cls, vlist): Sequence = Pool().get('ir.sequence') Config = Pool().get('gnuhealth.sequences') vlist = [x.copy() for x in vlist] for values in vlist: if not values.get('code'): config = Config(1) values['code'] = Sequence.get_id( config.support_request_code_sequence.id) return super(SupportRequest, cls).create(vlist) @classmethod def __setup__(cls): super(SupportRequest, cls).__setup__() t = cls.__table__() cls._sql_constraints = [ ('code_uniq', Unique(t,t.code), 'This Request Code already exists'), ] cls._buttons.update({ 'open_support': {'invisible': Equal(Eval('state'), 'open')}, 'close_support': {'invisible': Equal(Eval('state'), 'closed')}, }) @classmethod @ModelView.button def open_support(cls, srs): cls.write(srs, { 'state': 'open'}) @classmethod @ModelView.button def close_support(cls, srs): cls.write(srs, { 'state': 'closed'})
class Template: __metaclass__ = PoolMeta __name__ = "product.template" purchasable = fields.Boolean('Purchasable', states={ 'readonly': ~Eval('active', True), }, depends=['active']) product_suppliers = fields.One2Many( 'purchase.product_supplier', 'product', 'Suppliers', states={ 'readonly': ~Eval('active', True), 'invisible': (~Eval('purchasable', False) | ~Eval('context', {}).get('company')), }, depends=['active', 'purchasable']) purchase_uom = fields.Many2One( 'product.uom', 'Purchase UOM', states={ 'readonly': ~Eval('active'), 'invisible': ~Eval('purchasable'), 'required': Eval('purchasable', False), }, domain=[('category', '=', Eval('default_uom_category'))], depends=['active', 'purchasable', 'default_uom_category']) @classmethod def __setup__(cls): super(Template, cls).__setup__() cls._error_messages.update({ 'change_purchase_uom': ('Purchase prices are based ' 'on the purchase uom.'), }) required = ~Eval('account_category') & Eval('purchasable', False) if not cls.account_expense.states.get('required'): cls.account_expense.states['required'] = required else: cls.account_expense.states['required'] = ( cls.account_expense.states['required'] | required) if 'account_category' not in cls.account_expense.depends: cls.account_expense.depends.append('account_category') if 'purchasable' not in cls.account_expense.depends: cls.account_expense.depends.append('purchasable') @fields.depends('default_uom', 'purchase_uom', 'purchasable') def on_change_default_uom(self): try: super(Template, self).on_change_default_uom() except AttributeError: pass if self.default_uom: if self.purchase_uom: if self.default_uom.category != self.purchase_uom.category: self.purchase_uom = self.default_uom else: self.purchase_uom = self.default_uom @classmethod def view_attributes(cls): return super(Template, cls).view_attributes() + [ ('//page[@id="suppliers"]', 'states', { 'invisible': ~Eval('purchasable'), }) ] @classmethod def write(cls, *args): actions = iter(args) for templates, values in zip(actions, actions): if not values.get("purchase_uom"): continue for template in templates: if not template.purchase_uom: continue if template.purchase_uom.id == values["purchase_uom"]: continue for product in template.products: if not product.product_suppliers: continue cls.raise_user_warning('%s@product_template' % template.id, 'change_purchase_uom') super(Template, cls).write(*args)
class Journal(ModelSQL, ModelView): 'Journal' __name__ = 'account.journal' name = fields.Char('Name', size=None, required=True, translate=True) code = fields.Char('Code', size=None) active = fields.Boolean('Active', select=True) type = fields.Selection('get_types', 'Type', required=True) view = fields.Many2One('account.journal.view', 'View') update_posted = fields.Boolean('Allow updating posted moves') sequence = fields.Property( fields.Many2One('ir.sequence', 'Sequence', domain=[('code', '=', 'account.journal')], context={'code': 'account.journal'}, states={ 'required': Bool(Eval('context', {}).get('company', -1)), })) credit_account = fields.Property( fields.Many2One('account.account', 'Default Credit Account', domain=[ ('kind', '!=', 'view'), ('company', '=', Eval('context', {}).get('company', -1)), ], states={ 'required': ((Eval('type').in_(['cash', 'write-off'])) & (Eval('context', {}).get('company', -1) != -1)), 'invisible': ~Eval('context', {}).get('company', -1), }, depends=['type'])) debit_account = fields.Property( fields.Many2One('account.account', 'Default Debit Account', domain=[ ('kind', '!=', 'view'), ('company', '=', Eval('context', {}).get('company', -1)), ], states={ 'required': ((Eval('type').in_(['cash', 'write-off'])) & (Eval('context', {}).get('company', -1) != -1)), 'invisible': ~Eval('context', {}).get('company', -1), }, depends=['type'])) debit = fields.Function( fields.Numeric('Debit', digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']), 'get_debit_credit_balance') credit = fields.Function( fields.Numeric('Credit', digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']), 'get_debit_credit_balance') balance = fields.Function( fields.Numeric('Balance', digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']), 'get_debit_credit_balance') currency_digits = fields.Function(fields.Integer('Currency Digits'), 'get_currency_digits') @classmethod def __setup__(cls): super(Journal, cls).__setup__() cls._order.insert(0, ('name', 'ASC')) @classmethod def __register__(cls, module_name): TableHandler = backend.get('TableHandler') super(Journal, cls).__register__(module_name) cursor = Transaction().connection.cursor() table = TableHandler(cls, module_name) # Migration from 1.0 sequence Many2One change into Property if table.column_exist('sequence'): Property = Pool().get('ir.property') sql_table = cls.__table__() cursor.execute(*sql_table.select(sql_table.id, sql_table.sequence)) for journal_id, sequence_id in cursor.fetchall(): Property.set('sequence', cls._name, journal_id, (sequence_id and 'ir.sequence,' + str(sequence_id) or False)) table.drop_column('sequence', exception=True) @staticmethod def default_active(): return True @staticmethod def default_update_posted(): return False @staticmethod def default_sequence(): return None @staticmethod def get_types(): Type = Pool().get('account.journal.type') types = Type.search([]) return [(x.code, x.name) for x in types] @classmethod def search_rec_name(cls, name, clause): if clause[1].startswith('!') or clause[1].startswith('not '): bool_op = 'AND' else: bool_op = 'OR' return [ bool_op, ('code', ) + tuple(clause[1:]), (cls._rec_name, ) + tuple(clause[1:]), ] @classmethod def get_currency_digits(cls, journals, name): pool = Pool() Company = pool.get('company.company') company_id = Transaction().context.get('company') if company_id: company = Company(company_id) digits = company.currency.digits else: digits = 2 return dict.fromkeys([j.id for j in journals], digits) @classmethod def get_debit_credit_balance(cls, journals, names): pool = Pool() MoveLine = pool.get('account.move.line') Move = pool.get('account.move') context = Transaction().context cursor = Transaction().connection.cursor() result = {} ids = [j.id for j in journals] for name in ['debit', 'credit', 'balance']: result[name] = dict.fromkeys(ids, 0) line = MoveLine.__table__() move = Move.__table__() where = ((move.date >= context['start_date']) & (move.date <= context['end_date'])) for sub_journals in grouped_slice(journals): sub_journals = list(sub_journals) red_sql = reduce_ids(move.journal, [j.id for j in sub_journals]) accounts = None for journal in sub_journals: credit_account = (journal.credit_account.id if journal.credit_account else None) debit_account = (journal.debit_account.id if journal.debit_account else None) clause = ((move.journal == journal.id) & (((line.credit != Null) & (line.account == credit_account)) | ((line.debit != Null) & (line.account == debit_account)))) if accounts is None: accounts = clause else: accounts |= clause query = line.join(move, 'LEFT', condition=line.move == move.id).select( move.journal, Sum(line.debit), Sum(line.credit), where=where & red_sql & accounts, group_by=move.journal) cursor.execute(*query) for journal_id, debit, credit in cursor.fetchall(): # SQLite uses float for SUM if not isinstance(debit, Decimal): debit = Decimal(str(debit)) if not isinstance(credit, Decimal): credit = Decimal(str(credit)) result['debit'][journal_id] = debit result['credit'][journal_id] = credit result['balance'][journal_id] = debit - credit return result
class WebSite(ModelSQL, ModelView): """ One of the most powerful features of Nereid is the ability to manage multiple websites from one back-end. A web site in nereid represents a collection or URLs, settings. :param name: Name of the web site :param base_url: The unique URL of the website, You cannot have two websites, with the same base_url :param url_map: The active URL Map for the website (M2O URLMap) :param company: The company linked with the website. :param active: Whether the website is active or not. """ __name__ = "nereid.website" #: The name field is used for both information and also as #: the site identifier for nereid. The WSGI application requires #: SITE argument. The SITE argument is then used to load URLs and #: other settings for the website. Needs to be unique name = fields.Char('Name', required=True, select=True) #: The company to which the website belongs. Useful when creating #: records like sale order which require a company to be present company = fields.Many2One('company.company', 'Company', required=True) active = fields.Boolean('Active') #: The list of countries this website operates in. Used for generating #: Countries list in the registration form etc. countries = fields.Many2Many('nereid.website-country.country', 'website', 'country', 'Countries Available') #: Allowed currencies in the website currencies = fields.Many2Many('nereid.website-currency.currency', 'website', 'currency', 'Currencies Available') #: Default locale default_locale = fields.Many2One('nereid.website.locale', 'Default Locale', required=True) #: Allowed locales in the website locales = fields.Many2Many('nereid.website-nereid.website.locale', 'website', 'locale', 'Languages Available') #: The res.user with which the nereid application will be loaded #: .. versionadded: 0.3 application_user = fields.Many2One('res.user', 'Application User', required=True) timezone = fields.Function(fields.Selection( [(x, x) for x in pytz.common_timezones], 'Timezone', translate=False), getter="get_timezone") def get_timezone(self, name): warnings.warn( "Website timezone is deprecated, use company timezone instead", DeprecationWarning, stacklevel=2) return self.company.timezone @staticmethod def default_active(): return True @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_default_locale(): ModelData = Pool().get('ir.model.data') if not Pool().test: return ModelData.get_id("nereid", "website_locale_en-us") @staticmethod def default_application_user(): ModelData = Pool().get('ir.model.data') return ModelData.get_id("nereid", "web_user") @classmethod def __setup__(cls): super(WebSite, cls).__setup__() table = cls.__table__() cls._sql_constraints = [ ('name_uniq', Unique(table, table.name), 'Another site with the same name already exists!') ] @classmethod @route("/countries", methods=["GET"]) def country_list(cls): """ Return the list of countries in JSON """ return jsonify(result=[{ 'key': c.id, 'value': c.name } for c in current_website.countries]) @classmethod @route("/subdivisions", methods=["GET"]) def subdivision_list(cls): """ Return the list of states for given country """ Subdivision = Pool().get('country.subdivision') country = int(request.args.get('country', 0)) if country not in [c.id for c in current_website.countries]: abort(404) subdivisions = Subdivision.search([('country', '=', country)]) return jsonify(result=[s.serialize() for s in subdivisions]) def stats(self, **arguments): """ Test method. """ return u'Request: %s\nArguments: %s\nEnviron: %s\n' \ % (request, arguments, request.environ) @classmethod @route('/') def home(cls): "A dummy home method which just renders home.jinja" return render_template('home.jinja') @classmethod @route('/login', methods=['GET', 'POST']) def login(cls): """ Simple login based on the email and password Required post data see :class:LoginForm """ login_form = LoginForm(request.form) if not current_user.is_anonymous and request.args.get('next'): return redirect(request.args['next']) if request.method == 'POST' and login_form.validate(): NereidUser = Pool().get('nereid.user') user = NereidUser.authenticate(login_form.email.data, login_form.password.data) # Result can be the following: # 1 - Browse record of User (successful login) # 2 - None - Login failure without message # 3 - Any other false value (no message is shown. useful if you # want to handle the message shown to user) if user: # NOTE: Translators leave %s as such flash( _("You are now logged in. Welcome %(name)s", name=user.display_name)) if login_user(user, remember=login_form.remember.data): if request.is_xhr: return jsonify({ 'success': True, 'user': user.serialize(), }) else: return redirect( request.values.get('next', url_for('nereid.website.home'))) else: flash(_("Your account has not been activated yet!")) elif user is None: flash(_("Invalid login credentials")) failed_login.send(form=login_form) if request.is_xhr: rv = jsonify(message="Bad credentials") rv.status_code = 401 return rv return render_template('login.jinja', login_form=login_form) @classmethod @route('/logout') def logout(cls): "Log the user out" logout_user() flash( _('You have been logged out successfully. Thanks for visiting us')) return redirect( request.args.get('next', url_for('nereid.website.home'))) @classmethod @login_required @route('/login/token', methods=['POST']) def get_auth_token(cls): """ A method that returns a login token and user information in a json response. This should probably be called with basic authentication in the header. The token generated could then be used for subsequent requests. """ return jsonify({ 'user': current_user.serialize(), 'token': current_user.get_auth_token(), }) @staticmethod def account_context(): """This fills the account context for the template rendering my account. Additional modules might want to fill extra data into the context """ return dict( user=current_user, party=current_user.party, ) @classmethod @route("/account", methods=["GET"]) @login_required def account(cls): return render_template('account.jinja', **cls.account_context()) def get_currencies(self): """Returns available currencies for current site .. note:: A special method is required so that the fetch can be speeded up, by pushing the categories to the central cache which cannot be done directly on a browse node. """ cache_key = key_from_list([ Transaction().database.name, Transaction().user, 'nereid.website.get_currencies', ]) # The website is automatically appended to the cache prefix rv = cache.get(cache_key) if rv is None: rv = [{ 'id': c.id, 'name': c.name, 'symbol': c.symbol, } for c in self.currencies] cache.set(cache_key, rv, 60 * 60) return rv @staticmethod def _user_status(): """Returns the commonly required status parameters of the user This method could be inherited and components could be added """ rv = { 'messages': map(unicode, get_flashed_messages()), } if current_user.is_anonymous: rv.update({'logged_id': False}) else: rv.update({'logged_in': True, 'name': current_user.display_name}) return rv @classmethod @route("/user_status", methods=["GET", "POST"]) def user_status(cls): """ Returns a JSON of the user_status """ return jsonify(status=cls._user_status()) @classmethod def get_from_host(cls, host, silent=False): """ Returns the website with name as given host If not silent a website not found error is raised. """ if cls.search([], count=True) == 1: return cls.search([])[0] try: website, = cls.search([('name', '=', host)]) except ValueError: if not silent: raise WebsiteNotFound() else: return website def get_context(self): """ Returns transaction context to be used by nereid dispatcher for this website """ return {} _url_adapter_cache = Cache('nereid.website.url_adapter', context=False) @classmethod def clear_url_adapter_cache(cls, *args): """ A method which conveniently clears the cache """ cls._url_adapter_cache.clear() def get_url_adapter(self, app): """ Returns the URL adapter for the website """ cache_rv = self._url_adapter_cache.get(self.id) if cache_rv is not None: return cache_rv url_rules = app.get_urls()[:] # Add the static url url_rules.append( app.url_rule_class( app.static_url_path + '/<path:filename>', endpoint='static', )) url_map = Map() if self.locales: # Create the URL map with locale prefix url_map.add( app.url_rule_class( '/', redirect_to='/%s' % self.default_locale.code, ), ) url_map.add(Submount('/<locale>', url_rules)) else: # Create a new map with the given URLs map(url_map.add, url_rules) # Add the rules from the application's url map filled through the # route decorator or otherwise for rule in app.url_map._rules: url_map.add(rule.empty()) self._url_adapter_cache.set(self.id, url_map) return url_map def get_current_locale(self, req): """ Returns the active record of the current locale. The locale could either be from the URL if the locale was specified in the URL, or the default locale from the website. """ if req.view_args and 'locale' in req.view_args: for locale in self.locales: if locale.code == req.view_args['locale']: return locale # Return the default locale return self.default_locale
class Dunning(ModelSQL, ModelView): 'Account Dunning' __name__ = 'account.dunning' company = fields.Many2One( 'company.company', 'Company', required=True, help="Make the dunning belong to the company.", select=True, domain=[ ('id', If(Eval('context', {}).contains('company'), '=', '!='), Eval('context', {}).get('company', -1)), ], states=_STATES, depends=_DEPENDS) line = fields.Many2One('account.move.line', 'Line', required=True, help="The receivable line to dun for.", domain=[ ('account.type.receivable', '=', True), ('account.company', '=', Eval('company', -1)), [ 'OR', ('debit', '>', 0), ('credit', '<', 0), ], ], states=_STATES, depends=_DEPENDS + ['company']) procedure = fields.Many2One('account.dunning.procedure', 'Procedure', required=True, states=_STATES, depends=_DEPENDS) level = fields.Many2One('account.dunning.level', 'Level', required=True, domain=[ ('procedure', '=', Eval('procedure', -1)), ], states=_STATES, depends=_DEPENDS + ['procedure']) blocked = fields.Boolean( 'Blocked', help="Check to block further levels of the procedure.") state = fields.Selection([ ('draft', 'Draft'), ('waiting', "Waiting"), ('final', "Final"), ], 'State', readonly=True) active = fields.Function(fields.Boolean('Active'), 'get_active', searcher='search_active') party = fields.Function(fields.Many2One('party.party', 'Party'), 'get_line_field', searcher='search_line_field') amount = fields.Function( fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']), 'get_amount') currency_digits = fields.Function(fields.Integer('Currency Digits'), 'get_line_field') maturity_date = fields.Function(fields.Date('Maturity Date'), 'get_line_field', searcher='search_line_field') amount_second_currency = fields.Function( fields.Numeric('Amount Second Currency', digits=(16, Eval('second_currency_digits', 2)), depends=['second_currency_digits']), 'get_amount_second_currency') second_currency = fields.Function( fields.Many2One('currency.currency', 'Second Currency'), 'get_second_currency') second_currency_digits = fields.Function( fields.Integer('Second Currency Digits'), 'get_second_currency_digits') @classmethod def __setup__(cls): super(Dunning, cls).__setup__() table = cls.__table__() cls._sql_constraints = [ ('line_unique', Unique(table, table.line), 'account_dunning.msg_dunning_line_unique'), ] cls._active_field = 'active' @classmethod def __register__(cls, module): dunning = cls.__table__() super().__register__(module) cursor = Transaction().connection.cursor() # Migration from 4.8: rename done state into waiting cursor.execute(*dunning.update([dunning.state], ['waiting'], where=dunning.state == 'done')) @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_state(): return 'draft' def get_active(self, name): return not self.line.reconciliation def get_line_field(self, name): value = getattr(self.line, name) if isinstance(value, Model): return value.id else: return value @classmethod def search_line_field(cls, name, clause): return [('line.' + clause[0], ) + tuple(clause[1:])] def get_amount(self, name): return self.line.debit - self.line.credit def get_amount_second_currency(self, name): amount = self.line.debit - self.line.credit if self.line.amount_second_currency: return self.line.amount_second_currency.copy_sign(amount) else: return amount def get_second_currency(self, name): if self.line.second_currency: return self.line.second_currency.id else: return self.line.account.company.currency.id def get_second_currency_digits(self, name): if self.line.second_currency: return self.line.second_currency.digits else: return self.line.account.company.currency.digits @classmethod def search_active(cls, name, clause): reverse = { '=': '!=', '!=': '=', } if clause[1] in reverse: if clause[2]: return [('line.reconciliation', clause[1], None)] else: return [('line.reconciliation', reverse[clause[1]], None)] else: return [] @classmethod def _overdue_line_domain(cls, date): return [ ('account.type.receivable', '=', True), ('dunnings', '=', None), ('maturity_date', '<=', date), [ 'OR', ('debit', '>', 0), ('credit', '<', 0), ], ('party', '!=', None), ('reconciliation', '=', None), ] @classmethod def generate_dunnings(cls, date=None): pool = Pool() Date = pool.get('ir.date') MoveLine = pool.get('account.move.line') if date is None: date = Date.today() set_level = defaultdict(list) for dunning in cls.search([ ('state', '=', 'waiting'), ('blocked', '=', False), ]): procedure = dunning.procedure levels = procedure.levels levels = levels[levels.index(dunning.level) + 1:] if levels: for level in levels: if level.test(dunning.line, date): break else: level = dunning.level if level != dunning.level: set_level[level].append(dunning) else: set_level[None].append(dunning) to_write = [] for level, dunnings in set_level.items(): if level: to_write.extend((dunnings, { 'level': level.id, 'state': 'draft', })) else: to_write.extend((dunnings, { 'state': 'final', })) if to_write: cls.write(*to_write) lines = MoveLine.search(cls._overdue_line_domain(date)) dunnings = (cls._get_dunning(line, date) for line in lines) cls.save([d for d in dunnings if d]) @classmethod def _get_dunning(cls, line, date): procedure = line.dunning_procedure if not procedure: return for level in procedure.levels: if level.test(line, date): break else: return return cls( line=line, procedure=procedure, level=level, ) @classmethod def process(cls, dunnings): cls.write( [d for d in dunnings if not d.blocked and d.state == 'draft'], { 'state': 'waiting', })
class Sale: "Sale" __name__ = 'sale.sale' is_ups_shipping = fields.Function( fields.Boolean('Is Shipping', readonly=True), 'get_is_ups_shipping') ups_service_type = fields.Many2One( 'ups.service', 'UPS Service Type', ) ups_package_type = fields.Selection(UPS_PACKAGE_TYPES, 'Package Content Type') ups_saturday_delivery = fields.Boolean("Is Saturday Delivery") def _get_weight_uom(self): """ Returns uom for ups """ UPSConfiguration = Pool().get('ups.configuration') weight_uom = UPSConfiguration(1).weight_uom if self.is_ups_shipping and weight_uom: return weight_uom return super(Sale, self)._get_weight_uom() @classmethod def __setup__(cls): super(Sale, cls).__setup__() cls._buttons.update({ 'update_ups_shipment_cost': { 'invisible': Eval('state') != 'quotation' } }) @staticmethod def default_ups_package_type(): Config = Pool().get('sale.configuration') config = Config(1) return config.ups_package_type @staticmethod def default_ups_service_type(): Config = Pool().get('sale.configuration') config = Config(1) return config.ups_service_type and config.ups_service_type.id or None @staticmethod def default_ups_saturday_delivery(): return False def on_change_lines(self): """Pass a flag in context which indicates the get_sale_price method of ups carrier not to calculate cost on each line change """ with Transaction().set_context({'ignore_carrier_computation': True}): return super(Sale, self).on_change_lines() def get_is_ups_shipping(self, name): """ Check if shipping is from UPS """ return self.carrier and self.carrier.carrier_cost_method == 'ups' def _get_carrier_context(self): "Pass sale in the context" context = super(Sale, self)._get_carrier_context() if not self.carrier.carrier_cost_method == 'ups': return context context = context.copy() context['sale'] = self.id return context def apply_ups_shipping(self): "Add a shipping line to sale for ups" Sale = Pool().get('sale.sale') Currency = Pool().get('currency.currency') if self.is_ups_shipping: with Transaction().set_context(self._get_carrier_context()): shipment_cost, currency_id = self.carrier.get_sale_price() if not shipment_cost: return # Convert the shipping cost to sale currency from USD shipment_cost = Currency.compute(Currency(currency_id), shipment_cost, self.currency) Sale.write( [self], { 'lines': [ ( 'create', [{ 'type': 'line', 'product': self.carrier.carrier_product.id, 'description': self.ups_service_type.name, 'quantity': 1, # XXX 'unit': self.carrier.carrier_product.sale_uom.id, 'unit_price': shipment_cost, 'shipment_cost': shipment_cost, 'amount': shipment_cost, 'taxes': [], 'sequence': 9999, # XXX }]), ('delete', [line for line in self.lines if line.shipment_cost]), ] }) @classmethod def quote(cls, sales): res = super(Sale, cls).quote(sales) cls.update_ups_shipment_cost(sales) return res @classmethod @ModelView.button def update_ups_shipment_cost(cls, sales): "Updates the shipping line with new value if any" for sale in sales: sale.apply_ups_shipping() def _update_ups_shipments(self): """ Update shipments with ups data """ Shipment = Pool().get('stock.shipment.out') assert self.is_ups_shipping shipments = list(self.shipments) Shipment.write( shipments, { 'ups_service_type': self.ups_service_type.id, 'ups_package_type': self.ups_package_type, 'ups_saturday_delivery': self.ups_saturday_delivery, }) def create_shipment(self, shipment_type): """ Create shipments for sale """ with Transaction().set_context(ignore_carrier_computation=True): # disable `carrier cost computation`(default behaviour) as cost # should only be computed after updating service_type else error may # occur, with improper ups service_type. shipments = super(Sale, self).create_shipment(shipment_type) if shipment_type == 'out' and shipments and self.is_ups_shipping: self._update_ups_shipments() return shipments def _get_ups_packages(self): """ Return UPS Packages XML """ UPSConfiguration = Pool().get('ups.configuration') ups_config = UPSConfiguration(1) package_type = RatingService.packaging_type(Code=self.ups_package_type) weight = self._get_package_weight(ups_config.weight_uom).quantize( Decimal('.01'), rounding=ROUND_UP) package_weight = RatingService.package_weight_type( Weight=str(weight), Code=ups_config.weight_uom_code, ) package_service_options = RatingService.package_service_options_type( RatingService.insured_value_type(MonetaryValue='0')) package_container = RatingService.package_type( package_type, package_weight, package_service_options) return [package_container] def _get_ship_from_address(self): """ Usually the warehouse from which you ship """ return self.warehouse.address def _get_rate_request_xml(self, mode='rate'): """ Return the E builder object with the rate fetching request :param mode: 'rate' - to fetch rate of current shipment and selected package type 'shop' - to get a rates list """ UPSConfiguration = Pool().get('ups.configuration') ups_config = UPSConfiguration(1) assert mode in ('rate', 'shop'), "Mode should be 'rate' or 'shop'" if mode == 'rate' and not self.ups_service_type: self.raise_user_error('ups_service_type_missing') shipment_args = self._get_ups_packages() shipment_args.extend([ self.warehouse.address.to_ups_shipper(), # Shipper self.shipment_address.to_ups_to_address(), # Ship to self._get_ship_from_address().to_ups_from_address(), # Ship from ]) if ups_config.negotiated_rates: shipment_args.append( RatingService.rate_information_type(negotiated=True)) if mode == 'rate': # TODO: handle ups_saturday_delivery shipment_args.append( RatingService.service_type(Code=self.ups_service_type.code)) request_option = E.RequestOption('Rate') else: request_option = E.RequestOption('Shop') return RatingService.rating_request_type(E.Shipment(*shipment_args), RequestOption=request_option) def _get_ups_rate_from_rated_shipment(cls, rated_shipment): """ The rated_shipment is an xml container in the response which has the standard rates and negotiated rates. This method should extract the value and return it with the currency """ Currency = Pool().get('currency.currency') UPSConfiguration = Pool().get('ups.configuration') ups_config = UPSConfiguration(1) currency, = Currency.search([ ('code', '=', str(rated_shipment.TotalCharges.CurrencyCode)) ]) if ups_config.negotiated_rates and \ hasattr(rated_shipment, 'NegotiatedRates'): # If there are negotiated rates return that instead charges = rated_shipment.NegotiatedRates.NetSummaryCharges charges = currency.round( Decimal(str(charges.GrandTotal.MonetaryValue))) else: charges = currency.round( Decimal(str(rated_shipment.TotalCharges.MonetaryValue))) return charges, currency def get_ups_shipping_cost(self): """Returns the calculated shipping cost as sent by ups :returns: The shipping cost with currency """ UPSConfiguration = Pool().get('ups.configuration') Carrier = Pool().get('carrier') ups_config = UPSConfiguration(1) carrier, = Carrier.search(['carrier_cost_method', '=', 'ups']) rate_request = self._get_rate_request_xml() rate_api = ups_config.api_instance(call="rate") # Instead of shopping for rates, just get a price for the given # service and package type to the destination we know. rate_api.RequestOption = E.RequestOption('Rate') # Logging. logger.debug('Making Rate API Request for shipping cost of' 'Sale ID: {0} and Carrier ID: {1}'.format( self.id, carrier.id)) logger.debug('--------RATE API REQUEST--------') logger.debug(str(rate_request)) logger.debug('--------END REQUEST--------') try: response = rate_api.request(rate_request) except PyUPSException, e: self.raise_user_error(unicode(e[0])) # Logging. logger.debug('--------RATE API RESPONSE--------') logger.debug(str(response)) logger.debug('--------END RESPONSE--------') shipment_cost, currency = self._get_ups_rate_from_rated_shipment( response.RatedShipment) return shipment_cost, currency.id
class Connection(ModelSingleton, ModelSQL, ModelView): "LDAP Connection" __name__ = 'ldap.connection' _rec_name = 'server' server = fields.Char('Server', required=True, help='LDAP server name') port = fields.Integer('Port', required=True, help='LDAP server port') secure = fields.Selection([ ('never', 'Never'), ('ssl', 'SSL'), ('tls', 'TLS'), ], 'Secure', required=True, help='LDAP secure connection') bind_dn = fields.Char('Bind DN', help='LDAP DN used to bind') bind_pass = fields.Char('Bind Pass', states={ 'required': Bool(Eval('bind_dn')), 'readonly': ~Eval('bind_dn'), }, help='LDAP password used to bind', depends=['bind_dn']) uri = fields.Function(fields.Char('URI'), 'get_uri') active_directory = fields.Boolean('Active Directory') @classmethod def __setup__(cls): super(Connection, cls).__setup__() cls._buttons.update({ 'test_connection': {}, }) @staticmethod def default_port(): return 389 @staticmethod def default_secure(): return 'never' @staticmethod def default_active_directory(): return False @fields.depends('secure') def on_change_secure(self): res = {} if self.secure in ('never', 'tls'): res['port'] = self.default_port() elif self.secure == 'ssl': res['port'] = 636 return res def get_uri(self, name): return ((self.secure == 'ssl' and 'ldaps' or 'ldap') + '://%s:%s/' % (self.server, self.port)) @classmethod @ModelView.button_action('ldap_connection.wizard_test_connection') def test_connection(cls, connections): pass @classmethod def write(cls, *args): actions = iter(args) args = [] for connections, values in zip(actions, actions): if 'bind_dn' in values and not values['bind_dn']: values = values.copy() values['bind_pass'] = None args.extend((connections, values)) return super(Connection, cls).write(*args)
class StockShipmentOut: """Stock Shipment Out Add a field for tracking number """ __name__ = 'stock.shipment.out' #: Indicates if the tracking information has been exported #: to magento. Tracking info means carrier and tracking number info #: which is different from exporting shipment status to magento is_tracking_exported_to_magento = fields.Boolean( 'Is Tracking Info Exported To Magento' ) #: The magento increment id for this shipment. This is filled when a #: shipment is created corresponding to the shipment to tryton #: in magento. magento_increment_id = fields.Char( "Magento Increment ID", readonly=True ) @staticmethod def default_is_tracking_exported_to_magento(): return False def export_tracking_info_to_magento(self): """ Export tracking info to magento for the specified shipment. :param shipment: Browse record of shipment :return: Shipment increment ID """ MagentoCarrier = Pool().get('magento.instance.carrier') Channel = Pool().get('sale.channel') Shipment = Pool().get('stock.shipment.out') channel = Channel.get_current_magento_channel() assert self.tracking_number assert self.carrier try: carrier, = MagentoCarrier.search([ ('channel', '=', channel.id), ('carrier', '=', self.carrier.id) ]) except ValueError: # No mapping carrier found use custom code, title = 'custom', self.carrier.rec_name else: code, title = carrier.get_magento_mapping() # Add tracking info to the shipment on magento with magento.Shipment( channel.magento_url, channel.magento_api_user, channel.magento_api_key ) as shipment_api: shipment_increment_id = shipment_api.addtrack( self.magento_increment_id, code, title, self.tracking_number ) Shipment.write([self], { 'is_tracking_exported_to_magento': True }) return shipment_increment_id
class HrContract(ModelSQL, ModelView): """Employee Contract""" __name__ = 'hr.contract' salary_code = fields.Char('Salary Code', required=True) employee = fields.Many2One('company.employee', 'Employee', required=True) name = fields.Char('Salary detail Reference', required=True) center = fields.Many2One('gnuhealth.institution', 'Center') department = fields.Many2One('company.department', 'Department', required=True) designation = fields.Many2One('employee.designation', 'Designation', required=True) date_start = fields.Date('Start Date', required=True) date_end = fields.Date('End Date') notes = fields.Text('Notes') is_active = fields.Boolean('Active') basic = fields.Float('Basic Pay', required=True) approve_date = fields.Date('Date of Approval') approve_by = fields.Many2One('res.user', 'Approved By') @classmethod def validate(cls, records): super(HrContract, cls).validate(records) for record in records: record.valid_date() contracts = cls.search([('id', '!=', record.id), ('employee', '=', record.employee), ('date_start', '<=', record.date_start), ('date_end', '>=', record.date_start)]) if contracts: cls.raise_user_error('Contract for this user already\ exists for the given period') def valid_date(self): if self.date_end and self.date_end < self.date_start: self.raise_user_error('Not a valid date') @fields.depends('date_end') def on_change_with_is_active(self): present_date = datetime.date.today() if self.date_end and (present_date > self.date_end): return False return True @classmethod def set_approvedby(cls, hrcontract): ''' Fill the approved by and approve date field ''' approve_date = datetime.datetime.now().date() pool = Pool() User = pool.get('res.user') user = User(Transaction().user) for contract in hrcontract: contract.approve_date = approve_date contract.approve_by = user.id cls.save(hrcontract) @fields.depends('employee') def on_change_employee(self): if self.employee: self.salary_code = self.employee.salary_code self.designation = self.employee.designation self.department = self.employee.department self.center = self.employee.center @staticmethod def default_employee(): pool = Pool() User = pool.get('res.user') user = User(Transaction().user) employee = user.employee return employee.id if employee else None @classmethod def default_date_start(cls): start_date = datetime.date.today().replace(day=1) return start_date
class JournalPeriod(ModelSQL, ModelView): 'Journal - Period' __name__ = 'account.journal.period' name = fields.Char('Name', size=None, required=True) journal = fields.Many2One('account.journal', 'Journal', required=True, ondelete='CASCADE', states=STATES, depends=DEPENDS) period = fields.Many2One('account.period', 'Period', required=True, ondelete='CASCADE', states=STATES, depends=DEPENDS) icon = fields.Function(fields.Char('Icon'), 'get_icon') active = fields.Boolean('Active', select=True, states=STATES, depends=DEPENDS) state = fields.Selection([ ('open', 'Open'), ('close', 'Close'), ], 'State', readonly=True, required=True) @classmethod def __setup__(cls): super(JournalPeriod, cls).__setup__() t = cls.__table__() cls._sql_constraints += [ ('journal_period_uniq', Unique(t, t.journal, t.period), 'You can only open one journal per period.'), ] cls._order.insert(0, ('name', 'ASC')) cls._error_messages.update({ 'modify_del_journal_period': ('You can not modify/delete ' 'journal - period "%s" because it has moves.'), 'create_journal_period': ('You can not create a ' 'journal - period on closed period "%s".'), 'open_journal_period': ('You can not open ' 'journal - period "%(journal_period)s" because period ' '"%(period)s" is closed.'), }) @staticmethod def default_active(): return True @staticmethod def default_state(): return 'open' def get_icon(self, name): return _ICONS.get(self.state, '') @classmethod def _check(cls, periods): Move = Pool().get('account.move') for period in periods: moves = Move.search([ ('journal', '=', period.journal.id), ('period', '=', period.period.id), ], limit=1) if moves: cls.raise_user_error('modify_del_journal_period', (period.rec_name, )) @classmethod def create(cls, vlist): Period = Pool().get('account.period') for vals in vlist: if vals.get('period'): period = Period(vals['period']) if period.state == 'close': cls.raise_user_error('create_journal_period', (period.rec_name, )) return super(JournalPeriod, cls).create(vlist) @classmethod def write(cls, *args): actions = iter(args) for journal_periods, values in zip(actions, actions): if (values != {'state': 'close'} and values != {'state': 'open'}): cls._check(journal_periods) if values.get('state') == 'open': for journal_period in journal_periods: if journal_period.period.state == 'close': cls.raise_user_error( 'open_journal_period', { 'journal_period': journal_period.rec_name, 'period': journal_period.period.rec_name, }) super(JournalPeriod, cls).write(*args) @classmethod def delete(cls, periods): cls._check(periods) super(JournalPeriod, cls).delete(periods) @classmethod def close(cls, periods): ''' Close journal - period ''' cls.write(periods, { 'state': 'close', }) @classmethod def open_(cls, periods): "Open journal - period" cls.write(periods, { 'state': 'open', })
class Equipment(ModelSQL, ModelView): 'Equipment' __name__ = 'lims.equipment' template = fields.Many2One('lims.equipment.template', 'Template', required=True) name = fields.Char('Name', required=True) type = fields.Function(fields.Many2One('lims.equipment.type', 'Type'), 'get_type', searcher='search_type') brand = fields.Function(fields.Many2One('lims.brand', 'Brand'), 'get_brand', searcher='search_brand') model = fields.Char('Model', required=True) power = fields.Char('Power') voltage = fields.Char('Primary Voltage') voltage_secondary = fields.Char('Secondary Voltage') amperage = fields.Char('Secondary Amperage') serial_number = fields.Char('Serial number') internal_id = fields.Char('Internal ID Code') latitude = fields.Numeric('Latitude', digits=(3, 14)) longitude = fields.Numeric('Longitude', digits=(4, 14)) plant = fields.Many2One( 'lims.plant', 'Plant', required=True, select=True, domain=[ If( Eval('context', {}).contains('party'), ('party', '=', Eval('context', {}).get('party', -1)), ()) ]) components = fields.One2Many('lims.component', 'equipment', 'Components') year_manufacturing = fields.Integer('Year of manufacturing') year_service_start = fields.Integer('Year of service start') internal_location = fields.Char('Internal location') contacts = fields.One2Many('party.address', 'equipment', 'Contacts', domain=[('party', '=', Eval('party'))], context={'plant': Eval('plant')}, depends=['party', 'plant']) party = fields.Function(fields.Many2One('party.party', 'Party'), 'get_party', searcher='search_party') missing_data = fields.Boolean('Missing data') @classmethod def __setup__(cls): super().__setup__() cls._order.insert(0, ('template', 'ASC')) cls._order.insert(1, ('name', 'ASC')) t = cls.__table__() cls._sql_constraints = [ ('name_unique', Unique(t, t.plant, t.name), 'lims_industry.msg_equipment_name_unique'), ] @classmethod def create(cls, vlist): TaskTemplate = Pool().get('lims.administrative.task.template') equipments = super().create(vlist) TaskTemplate.create_tasks('equipment_missing_data', cls._for_task_missing_data(equipments)) return equipments @classmethod def _for_task_missing_data(cls, equipments): AdministrativeTask = Pool().get('lims.administrative.task') res = [] for equipment in equipments: if not equipment.missing_data: continue if AdministrativeTask.search([ ('type', '=', 'equipment_missing_data'), ('origin', '=', '%s,%s' % (cls.__name__, equipment.id)), ('state', 'not in', ('done', 'discarded')), ]): continue res.append(equipment) return res def get_rec_name(self, name): res = '%s [%s]' % (self.name, self.plant.name) return res @classmethod def search_rec_name(cls, name, clause): return [ 'OR', ('name', ) + tuple(clause[1:]), ('serial_number', ) + tuple(clause[1:]), ('brand.name', ) + tuple(clause[1:]), ('plant.name', ) + tuple(clause[1:]), ('components.customer_description', ) + tuple(clause[1:]), ] @fields.depends('plant', '_parent_plant.party') def on_change_with_party(self, name=None): return self.get_party([self], name)[self.id] @classmethod def get_party(cls, equipments, name): result = {} for e in equipments: result[e.id] = e.plant and e.plant.party.id or None return result @classmethod def search_party(cls, name, clause): return [('plant.party', ) + tuple(clause[1:])] @fields.depends('template', '_parent_template.type') def on_change_with_type(self, name=None): return self.get_type([self], name)[self.id] @classmethod def get_type(cls, equipments, name): result = {} for e in equipments: result[e.id] = e.template and e.template.type.id or None return result @classmethod def search_type(cls, name, clause): return [('template.type', ) + tuple(clause[1:])] @fields.depends('template', '_parent_template.brand') def on_change_with_brand(self, name=None): return self.get_brand([self], name)[self.id] @classmethod def get_brand(cls, equipments, name): result = {} for e in equipments: result[e.id] = e.template and e.template.brand.id or None return result @classmethod def search_brand(cls, name, clause): return [('template.brand', ) + tuple(clause[1:])] @fields.depends('template', 'components') def on_change_template(self): pool = Pool() Component = pool.get('lims.component') if not self.template: return current_components_ids = [ (component.kind.id, component.location and component.location.id or None) for component in self.components ] components = list(self.components) for record in self.template.component_kinds: kind_id = record.kind.id location_id = record.location and record.location.id or None if (kind_id, location_id) in current_components_ids: continue value = Component(**Component.default_get( list(Component._fields.keys()), with_rec_name=False)) value.kind = kind_id value.location = location_id components.append(value) self.model = self.template.model self.power = self.template.power self.components = components @classmethod def copy(cls, records, default=None): if default is None: default = {} current_default = default.copy() new_records = [] for record in records: current_default['name'] = '%s (copy)' % record.name new_record, = super().copy([record], default=current_default) new_records.append(new_record) return new_records
class MoveLineTemplate(ModelSQL, ModelView): 'Account Move Line Template' __name__ = 'account.move.line.template' move = fields.Many2One('account.move.template', 'Move', required=True) operation = fields.Selection([ ('debit', 'Debit'), ('credit', 'Credit'), ], 'Operation', required=True) amount = fields.Char( 'Amount', required=True, help="A python expression that will be evaluated with the keywords.") account = fields.Many2One('account.account', 'Account', required=True, domain=[ ('kind', '!=', 'view'), ('company', '=', Eval('_parent_move', {}).get('company', -1)), ]) party = fields.Char('Party', states={ 'required': Eval('party_required', False), 'invisible': ~Eval('party_required', False), }, depends=['party_required'], help="The name of the 'Party' keyword.") party_required = fields.Function(fields.Boolean('Party Required'), 'on_change_with_party_required') description = fields.Char( 'Description', help="Keywords values substitutions are identified " "by braces ('{' and '}').") taxes = fields.One2Many('account.tax.line.template', 'line', 'Taxes') @fields.depends('account') def on_change_with_party_required(self, name=None): if self.account: return self.account.party_required return False def get_line(self, values): 'Return the move line for the keyword values' pool = Pool() Line = pool.get('account.move.line') Keyword = pool.get('account.move.template.keyword') line = Line() amount = simple_eval(decistmt(self.amount), functions={'Decimal': Decimal}, names=values) amount = self.move.company.currency.round(amount) if self.operation == 'debit': line.debit = amount else: line.credit = amount line.account = self.account if self.party: line.party = values.get(self.party) if self.description: line.description = self.description.format( **dict(Keyword.format_values(self.move, values))) line.tax_lines = [t.get_line(values) for t in self.taxes] return line
class Ambulance (ModelSQL, ModelView): 'Ambulance' __name__ = 'gnuhealth.ambulance' vehicle_identifier = fields.Char('ID', required=True, help="Ambulance license number or other type of ID") vehicle_brand = fields.Char('Brand', help="Ambulance maker") vehicle_model = fields.Char('Model', help="Ambulance model") vehicle_odometer = fields.Integer('Odometer', help="Current odometer reading") vehicle_type = fields.Selection([ (None, ''), ('van', 'Van'), ('car', 'Car'), ('boat', 'Boat'), ('helicopter', 'Helicopter'), ('airplane', 'Airplane'), ('motorcycle', 'Motorcycle'), ('bicycle', 'Bicycle'), ('drone', 'Drone'), ], 'Type', required=True, help="Type of vehicle",sort=False) vehicle_function = fields.Selection([ (None, ''), ('patient_transport', 'Type A - Patient Transport'), ('emergency', 'Type B - Emergency'), ('icu', 'Type C - Mobile ICU'), ], 'Function',sort=False, required=True, help="Vehicle main functionality") vehicle_station = fields.Many2One( 'gnuhealth.institution', 'Station', help="Station / Base of the vehicle") state = fields.Selection([ (None, ''), ('available', 'Available / At Station'), ('dispatched', 'Dispatched'), ('en_route', 'En Route'), ('on_location', 'On Location'), ('to_hospital', 'To Hospital'), ('at_hospital', 'At Hospital'), ('returning', 'Returning'), ('out_of_service', 'Out of service'), ], 'Status',sort=False, readonly=True, help="Vehicle status") vehicle_remarks = fields.Text('Remarks') active = fields.Boolean('Active', select=True) @staticmethod def default_active(): return True @staticmethod def default_state(): return 'available' @classmethod def __setup__(cls): super(Ambulance, cls).__setup__() t = cls.__table__() cls._sql_constraints = [ ('vehicle_uniq', Unique(t,t.vehicle_identifier), 'This vehicle ID already exists'), ] def get_rec_name(self, name): return (self.vehicle_identifier + ' : ' + self.vehicle_type)
class MoveTemplateKeyword(sequence_ordered(), ModelSQL, ModelView): 'Account Move Template Keyword' __name__ = 'account.move.template.keyword' name = fields.Char('Name', required=True) string = fields.Char('String', required=True, translate=True) move = fields.Many2One('account.move.template', 'Move', required=True) type_ = fields.Selection([ ('char', 'Char'), ('numeric', 'Numeric'), ('date', 'Date'), ('party', 'Party'), ], 'Type') required = fields.Boolean('Required') digits = fields.Integer('Digits', states={ 'invisible': Eval('type_') != 'numeric', 'required': Eval('type_') == 'numeric', }, depends=['type_']) @staticmethod def default_required(): return False def get_field(self): field = getattr(self, '_get_field_%s' % self.type_)() field.update({ 'name': self.name, 'string': self.string, 'required': self.required, }) return field def _get_field_char(self): return {'type': 'char'} def _get_field_numeric(self): return {'type': 'numeric', 'digits': (16, self.digits)} def _format_numeric(self, lang, value): if value: return lang.format('%.*f', (self.digits, value), True) else: return '' def _get_field_date(self): return {'type': 'date'} def _format_date(self, lang, value): if value: return lang.strftime(value) else: return '' def _get_field_party(self): return { 'type': 'many2one', 'relation': 'party.party', } def _format_party(self, lang, value): pool = Pool() Party = pool.get('party.party') if value: return Party(value).rec_name else: return '' @staticmethod def format_values(template, values): "Yield key and formatted value" pool = Pool() Lang = pool.get('ir.lang') lang, = Lang.search([ ('code', '=', Transaction().language), ]) keywords = {k.name: k for k in template.keywords} for k, v in values.iteritems(): keyword = keywords[k] func = getattr(keyword, '_format_%s' % keyword.type_, None) if func: yield k, func(lang, v) else: yield k, v
class ResultsReportVersionDetailSample(metaclass=PoolMeta): __name__ = 'lims.results_report.version.detail.sample' plant = fields.Function(fields.Many2One('lims.plant', 'Plant'), 'get_notebook_field') equipment = fields.Function(fields.Many2One('lims.equipment', 'Equipment'), 'get_notebook_field') equipment_template = fields.Function( fields.Many2One('lims.equipment.template', 'Equipment Template'), 'get_notebook_field') equipment_model = fields.Function(fields.Char('Equipment Model'), 'get_notebook_field') equipment_serial_number = fields.Function( fields.Char('Equipment Serial Number'), 'get_notebook_field') equipment_name = fields.Function(fields.Char('Equipment Name'), 'get_notebook_field') component = fields.Function(fields.Many2One('lims.component', 'Component'), 'get_notebook_field') comercial_product = fields.Function( fields.Many2One('lims.comercial.product', 'Comercial Product'), 'get_notebook_field') precedent1 = fields.Many2One( 'lims.notebook', 'Precedent 1', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent2 = fields.Many2One( 'lims.notebook', 'Precedent 2', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent3 = fields.Many2One( 'lims.notebook', 'Precedent 3', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent4 = fields.Many2One( 'lims.notebook', 'Precedent 4', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent5 = fields.Many2One( 'lims.notebook', 'Precedent 5', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent6 = fields.Many2One( 'lims.notebook', 'Precedent 6', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent7 = fields.Many2One( 'lims.notebook', 'Precedent 7', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) precedent8 = fields.Many2One( 'lims.notebook', 'Precedent 8', domain=[ If(~Eval('free_precedents'), [('component', '=', Eval('component')), ('fraction.sample.state', '!=', 'annulled')], [('fraction.sample.state', '!=', 'annulled')]) ], depends=['free_precedents', 'component']) free_precedents = fields.Boolean('Free precedents') precedent1_diagnosis = fields.Function( fields.Text('Diagnosis Precedent 1'), 'on_change_with_precedent1_diagnosis') precedent2_diagnosis = fields.Function( fields.Text('Diagnosis Precedent 2'), 'on_change_with_precedent2_diagnosis') precedent3_diagnosis = fields.Function( fields.Text('Diagnosis Precedent 3'), 'on_change_with_precedent3_diagnosis') precedent1_diagnosis_states = fields.Function( fields.Dict('lims.diagnosis.state', 'Diagnosis States Precedent 1'), 'get_precedent_diagnosis') precedent2_diagnosis_states = fields.Function( fields.Dict('lims.diagnosis.state', 'Diagnosis States Precedent 2'), 'get_precedent_diagnosis') precedent3_diagnosis_states = fields.Function( fields.Dict('lims.diagnosis.state', 'Diagnosis States Precedent 3'), 'get_precedent_diagnosis') @staticmethod def default_free_precedents(): return False @classmethod def view_attributes(cls): missing_diagnosis = True if 'diagnosis' not in cls._fields else False return super().view_attributes() + [ ('//group[@id="diagnosis"]', 'states', { 'invisible': missing_diagnosis, }), ] @fields.depends('precedent1') def on_change_with_precedent1_diagnosis(self, name=None): if self.precedent1: result = self.get_precedent_diagnosis((self, ), ('precedent1_diagnosis', )) return result['precedent1_diagnosis'][self.id] return None @fields.depends('precedent2') def on_change_with_precedent2_diagnosis(self, name=None): if self.precedent2: result = self.get_precedent_diagnosis((self, ), ('precedent2_diagnosis', )) return result['precedent2_diagnosis'][self.id] return None @fields.depends('precedent3') def on_change_with_precedent3_diagnosis(self, name=None): if self.precedent3: result = self.get_precedent_diagnosis((self, ), ('precedent3_diagnosis', )) return result['precedent3_diagnosis'][self.id] return None @classmethod def get_precedent_diagnosis(cls, samples, names): result = {} missing_diagnosis = True if 'diagnosis' not in cls._fields else False if missing_diagnosis: for name in names: result[name] = {} for s in samples: result[name][s.id] = None else: for name in names: result[name] = {} if 'precedent1' in name: for s in samples: result[name][s.id] = cls._get_precedent_diagnosis( s.precedent1, name) elif 'precedent2' in name: for s in samples: result[name][s.id] = cls._get_precedent_diagnosis( s.precedent2, name) else: # name == 'precedent3_diagnosis': for s in samples: result[name][s.id] = cls._get_precedent_diagnosis( s.precedent3, name) return result @classmethod def _get_precedent_diagnosis(cls, precedent, name): if not precedent: return None precedent_sample = cls.search([ ('notebook', '=', precedent), ]) if not precedent_sample: return None return (precedent_sample[0].diagnosis_states if 'states' in name else precedent_sample[0].diagnosis) @classmethod def _get_fields_from_sample(cls, sample, only_accepted=True): sample_default = super()._get_fields_from_sample(sample, only_accepted) sample_default['precedent1'] = (sample.precedent1 and sample.precedent1 or None) sample_default['precedent2'] = (sample.precedent2 and sample.precedent2 or None) sample_default['precedent3'] = (sample.precedent3 and sample.precedent3 or None) sample_default['precedent4'] = (sample.precedent4 and sample.precedent4 or None) sample_default['precedent5'] = (sample.precedent5 and sample.precedent5 or None) sample_default['precedent6'] = (sample.precedent6 and sample.precedent6 or None) sample_default['precedent7'] = (sample.precedent7 and sample.precedent7 or None) sample_default['precedent8'] = (sample.precedent8 and sample.precedent8 or None) return sample_default @classmethod def create(cls, vlist): samples = super().create(vlist) for sample in samples: if not sample.precedent1: precedents = cls.get_default_precedents(sample) if not precedents: continue for i in range(0, min(3, len(precedents))): setattr(sample, 'precedent%s' % str(i + 1), precedents[i]) sample.save() cls.update_precedent_lines(sample) return samples @classmethod def write(cls, *args): super().write(*args) update_precedent_lines = False if not update_precedent_lines: return actions = iter(args) for samples, vals in zip(actions, actions): change_precedents = False for field in [ 'precedent1', 'precedent2', 'precedent3', 'precedent4', 'precedent5', 'precedent6', 'precedent7', 'precedent8' ]: if field in vals: change_precedents = True if change_precedents: for sample in samples: cls.update_precedent_lines(sample) @staticmethod def get_default_precedents(sample): pool = Pool() Notebook = pool.get('lims.notebook') if not sample.component: return [] precedents = Notebook.search([ ('id', '!=', sample.notebook.id), ('component', '=', sample.component), ('fraction.sample.state', '!=', 'annulled'), ('invoice_party', '=', sample.notebook.invoice_party), ], order=[ ('fraction.sample.number', 'DESC'), ], limit=3) return precedents @classmethod def update_precedent_lines(cls, sample): pool = Pool() ResultsLine = pool.get('lims.results_report.version.detail.line') NotebookLine = pool.get('lims.notebook.line') precedent_lines = ResultsLine.search([ ('detail_sample', '=', sample.id), ('notebook_line', '=', None), ]) if precedent_lines: ResultsLine.delete(precedent_lines) result_lines = ResultsLine.search([ ('detail_sample', '=', sample.id), ]) analysis = [rl.notebook_line.analysis.id for rl in result_lines] lines_to_create = [] for precedent in [ sample.precedent1, sample.precedent2, sample.precedent3 ]: if not precedent: continue precedent_lines = NotebookLine.search([ ('notebook', '=', precedent), ('analysis', 'not in', analysis), ('accepted', '=', True), ]) for line in precedent_lines: lines_to_create.append({ 'detail_sample': sample.id, 'precedent_analysis': line.analysis.id, }) analysis.append(line.analysis.id) if lines_to_create: ResultsLine.create(lines_to_create)
class DictSchemaMixin(object): _rec_name = 'string' name = fields.Char('Name', required=True) string = fields.Char('String', translate=True, required=True) type_ = fields.Selection([ ('boolean', 'Boolean'), ('integer', 'Integer'), ('char', 'Char'), ('float', 'Float'), ('numeric', 'Numeric'), ('date', 'Date'), ('datetime', 'DateTime'), ('selection', 'Selection'), ], 'Type', required=True) digits = fields.Integer('Digits', states={ 'invisible': ~Eval('type_').in_(['float', 'numeric']), }, depends=['type_']) selection = fields.Text( 'Selection', states={ 'invisible': Eval('type_') != 'selection', }, translate=True, depends=['type_'], help='A couple of key and label separated by ":" per line') selection_sorted = fields.Boolean( 'Selection Sorted', states={ 'invisible': Eval('type_') != 'selection', }, depends=['type_'], help='If the selection must be sorted on label') selection_json = fields.Function( fields.Char('Selection JSON', states={ 'invisible': Eval('type_') != 'selection', }, depends=['type_']), 'get_selection_json') @classmethod def __setup__(cls): super(DictSchemaMixin, cls).__setup__() cls.__rpc__.update({ 'get_keys': RPC(instantiate=0), }) @staticmethod def default_digits(): return 2 @staticmethod def default_selection_sorted(): return True def get_selection_json(self, name): db_selection = self.selection or '' selection = [[w.strip() for w in v.split(':', 1)] for v in db_selection.splitlines() if v] return json.dumps(selection, separators=(',', ':')) @classmethod def get_keys(cls, records): pool = Pool() Config = pool.get('ir.configuration') keys = [] for record in records: new_key = { 'id': record.id, 'name': record.name, 'string': record.string, 'type_': record.type_, } if record.type_ == 'selection': with Transaction().set_context(language=Config.get_language()): english_key = cls(record.id) selection = OrderedDict( json.loads(english_key.selection_json)) selection.update(dict(json.loads(record.selection_json))) new_key['selection'] = selection.items() new_key['sorted'] = record.selection_sorted elif record.type_ in ('float', 'numeric'): new_key['digits'] = (16, record.digits) keys.append(new_key) return keys
class Location(DeactivableMixin, tree(), ModelSQL, ModelView): "Stock Location" __name__ = 'stock.location' _default_warehouse_cache = Cache('stock.location.default_warehouse', context=False) name = fields.Char("Name", size=None, required=True, translate=True) code = fields.Char( "Code", size=None, select=True, help="The internal identifier used for the location.") address = fields.Many2One( 'party.address', "Address", states={ 'invisible': Eval('type') != 'warehouse', }, depends=['type']) type = fields.Selection([ ('supplier', 'Supplier'), ('customer', 'Customer'), ('lost_found', 'Lost and Found'), ('warehouse', 'Warehouse'), ('storage', 'Storage'), ('production', 'Production'), ('drop', 'Drop'), ('view', 'View'), ], "Location type") type_string = type.translated('type') parent = fields.Many2One("stock.location", "Parent", select=True, left="left", right="right", states={ 'invisible': Eval('type') == 'warehouse', }, depends=['type'], help="Used to add structure above the location.") left = fields.Integer('Left', required=True, select=True) right = fields.Integer('Right', required=True, select=True) childs = fields.One2Many("stock.location", "parent", "Children", help="Used to add structure below the location.") flat_childs = fields.Boolean( "Flat Children", help="Check to enforce a single level of children with no " "grandchildren.") warehouse = fields.Function(fields.Many2One('stock.location', 'Warehouse'), 'get_warehouse') input_location = fields.Many2One( "stock.location", "Input", states={ 'invisible': Eval('type') != 'warehouse', 'required': Eval('type') == 'warehouse', }, domain=[ ('type', '=', 'storage'), ['OR', ('parent', 'child_of', [Eval('id')]), ('parent', '=', None), ], ], depends=['type', 'id'], help="Where incoming stock is received.") output_location = fields.Many2One( "stock.location", "Output", states={ 'invisible': Eval('type') != 'warehouse', 'required': Eval('type') == 'warehouse', }, domain=[ ('type', '=', 'storage'), ['OR', ('parent', 'child_of', [Eval('id')]), ('parent', '=', None)]], depends=['type', 'id'], help="Where outgoing stock is sent from.") storage_location = fields.Many2One( "stock.location", "Storage", states={ 'invisible': Eval('type') != 'warehouse', 'required': Eval('type') == 'warehouse', }, domain=[ ('type', 'in', ['storage', 'view']), ['OR', ('parent', 'child_of', [Eval('id')]), ('parent', '=', None)]], depends=['type', 'id'], help="The top level location where stock is stored.") picking_location = fields.Many2One( 'stock.location', 'Picking', states={ 'invisible': Eval('type') != 'warehouse', }, domain=[ ('type', '=', 'storage'), ('parent', 'child_of', [Eval('storage_location', -1)]), ], depends=['type', 'storage_location'], help="Where stock is picked from.\n" "Leave empty to use the storage location.") lost_found_location = fields.Many2One( 'stock.location', "Lost and Found", states={ 'invisible': Eval('type') != 'warehouse', 'readonly': ~Eval('active'), }, domain=[ ('type', '=', 'lost_found'), ], depends=['type', 'active'], help="Used, by inventories, when correcting stock levels " "in the warehouse.") waste_locations = fields.Many2Many( 'stock.location.waste', 'warehouse', 'location', "Waste Locations", states={ 'invisible': Eval('type') != 'warehouse', }, domain=[ ('type', '=', 'lost_found'), ], depends=['type'], help="The locations used for waste products from the warehouse.") waste_warehouses = fields.Many2Many( 'stock.location.waste', 'location', 'warehouse', "Waste Warehouses", states={ 'invisible': Eval('type') != 'lost_found', }, domain=[ ('type', '=', 'warehouse'), ], depends=['type'], help="The warehouses that use the location for waste products.") quantity = fields.Function( fields.Float('Quantity', help="The amount of stock in the location."), 'get_quantity', searcher='search_quantity') forecast_quantity = fields.Function( fields.Float('Forecast Quantity', help="The amount of stock expected to be in the location."), 'get_quantity', searcher='search_quantity') cost_value = fields.Function(fields.Numeric('Cost Value', help="The value of the stock in the location."), 'get_cost_value') @classmethod def __setup__(cls): super(Location, cls).__setup__() cls._order.insert(0, ('name', 'ASC')) parent_domain = [ ['OR', ('parent.flat_childs', '=', False), ('parent', '=', None), ] ] childs_domain = [ If(Eval('flat_childs', False), ('childs', '=', None), ()), ] childs_mapping = cls._childs_domain() for type_, allowed_parents in cls._parent_domain().items(): parent_domain.append(If(Eval('type') == type_, ('type', 'in', allowed_parents), ())) childs_domain.append(If(Eval('type') == type_, ('type', 'in', childs_mapping[type_]), ())) cls.parent.domain = parent_domain cls.childs.domain = childs_domain cls.childs.depends.extend(['flat_childs', 'type']) @classmethod def _parent_domain(cls): '''Returns a dict with location types as keys and a list of allowed parent location types as values''' return { 'customer': ['customer'], 'supplier': ['supplier'], 'production': ['production'], 'lost_found': ['lost_found'], 'view': ['warehouse', 'view', 'storage'], 'storage': ['warehouse', 'view', 'storage'], 'warehouse': [''], } @classmethod def _childs_domain(cls): childs_domain = {} for type_, allowed_parents in cls._parent_domain().items(): for parent in allowed_parents: childs_domain.setdefault(parent, []) childs_domain[parent].append(type_) return childs_domain @classmethod def __register__(cls, module_name): super(Location, cls).__register__(module_name) table = cls.__table_handler__(module_name) table.index_action(['left', 'right'], 'add') @classmethod def validate(cls, locations): super(Location, cls).validate(locations) inactives = [] for location in locations: location.check_type_for_moves() if not location.active: inactives.append(location) cls.check_inactive(inactives) def check_type_for_moves(self): """ Check locations with moves have types compatible with moves. """ invalid_move_types = ['warehouse', 'view'] Move = Pool().get('stock.move') if self.type in invalid_move_types: # Use root to compute for all companies with Transaction().set_user(0): moves = Move.search([ ['OR', ('to_location', '=', self.id), ('from_location', '=', self.id), ], ('state', 'not in', ['staging', 'draft']), ]) if moves: raise LocationValidationError( gettext('stock.msg_location_invalid_type_for_moves', location=self.rec_name, type=self.type_string)) @classmethod def check_inactive(cls, locations): "Check inactive location are empty" assert all(not l.active for l in locations) empty = cls.get_empty_locations(locations) non_empty = set(locations) - set(empty) if non_empty: raise LocationValidationError( gettext('stock.msg_location_inactive_not_empty', location=next(iter(non_empty)).rec_name)) @classmethod def get_empty_locations(cls, locations=None): pool = Pool() Move = pool.get('stock.move') if locations is None: locations = cls.search([]) if not locations: return [] location_ids = list(map(int, locations)) # Use root to compute for all companies # and ensures inactive locations are in the query with Transaction().set_user(0), \ Transaction().set_context(active_test=False): query = Move.compute_quantities_query( location_ids, with_childs=True) quantities = Move.compute_quantities( query, location_ids, with_childs=True) empty = set(location_ids) for (location_id, product), quantity in quantities.items(): if quantity: empty.discard(location_id) for sub_ids in grouped_slice(list(empty)): sub_ids = list(sub_ids) moves = Move.search([ ('state', 'not in', ['done', 'cancelled']), ['OR', ('from_location', 'in', sub_ids), ('to_location', 'in', sub_ids), ], ]) for move in moves: for location in [move.from_location, move.to_location]: empty.discard(location.id) return cls.browse(empty) @staticmethod def default_left(): return 0 @staticmethod def default_right(): return 0 @classmethod def default_flat_childs(cls): return False @staticmethod def default_type(): return 'storage' @classmethod def check_xml_record(cls, records, values): return True def get_warehouse(self, name): # Order by descending left to get the first one in the tree with Transaction().set_context(active_test=False): locations = self.search([ ('parent', 'parent_of', [self.id]), ('type', '=', 'warehouse'), ], order=[('left', 'DESC')]) if locations: return locations[0].id @classmethod def get_default_warehouse(cls): warehouse = Transaction().context.get('warehouse') if warehouse: return warehouse warehouse = cls._default_warehouse_cache.get(None, -1) if warehouse == -1: warehouses = cls.search([ ('type', '=', 'warehouse'), ], limit=2) if len(warehouses) == 1: warehouse = warehouses[0].id else: warehouse = None cls._default_warehouse_cache.set(None, warehouse) return warehouse @property def lost_found_used(self): if self.warehouse: return self.warehouse.lost_found_location @classmethod def search_rec_name(cls, name, clause): if clause[1].startswith('!') or clause[1].startswith('not '): bool_op = 'AND' else: bool_op = 'OR' return [bool_op, (cls._rec_name,) + tuple(clause[1:]), ('code',) + tuple(clause[1:]), ] @classmethod def _get_quantity_grouping(cls): context = Transaction().context grouping, grouping_filter, key = (), (), [] if context.get('product') is not None: grouping = ('product',) grouping_filter = ([context['product']],) key = (context['product'],) elif context.get('product_template') is not None: grouping = ('product.template',) grouping_filter = ([context['product_template']],) key = (context['product_template'],) return grouping, grouping_filter, key @classmethod def get_quantity(cls, locations, name): pool = Pool() Product = pool.get('product.product') Date_ = pool.get('ir.date') trans_context = Transaction().context def valid_context(name): return (trans_context.get(name) is not None and isinstance(trans_context[name], int)) context = {} if (name == 'quantity' and (trans_context.get('stock_date_end', datetime.date.max) > Date_.today())): context['stock_date_end'] = Date_.today() if name == 'forecast_quantity': context['forecast'] = True if not trans_context.get('stock_date_end'): context['stock_date_end'] = datetime.date.max grouping, grouping_filter, key = cls._get_quantity_grouping() if not grouping: return {loc.id: None for loc in locations} pbl = {} for sub_locations in grouped_slice(locations): location_ids = [l.id for l in sub_locations] with Transaction().set_context(context): pbl.update(Product.products_by_location( location_ids, grouping=grouping, grouping_filter=grouping_filter, with_childs=trans_context.get('with_childs', True))) return dict((loc.id, pbl.get((loc.id,) + key, 0)) for loc in locations) @classmethod def search_quantity(cls, name, domain): _, operator_, operand = domain operator_ = { '=': operator.eq, '>=': operator.ge, '>': operator.gt, '<=': operator.le, '<': operator.lt, '!=': operator.ne, 'in': lambda v, l: v in l, 'not in': lambda v, l: v not in l, }.get(operator_, lambda v, l: False) ids = [] for location in cls.search([]): if operator_(getattr(location, name), operand): ids.append(location.id) return [('id', 'in', ids)] @classmethod def get_cost_value(cls, locations, name): pool = Pool() Product = pool.get('product.product') Template = pool.get('product.template') trans_context = Transaction().context cost_values = {l.id: None for l in locations} def valid_context(name): return (trans_context.get(name) is not None and isinstance(trans_context[name], int)) if not any(map(valid_context, ['product', 'product_template'])): return cost_values def get_record(): if trans_context.get('product') is not None: return Product(trans_context['product']) else: return Template(trans_context['product_template']) context = {} if 'stock_date_end' in trans_context: # Use the last cost_price of the day context['_datetime'] = datetime.datetime.combine( trans_context['stock_date_end'], datetime.time.max) # The date could be before the product creation record = get_record() if record.create_date > context['_datetime']: return cost_values with Transaction().set_context(context): cost_price = get_record().cost_price # The template may have more than one product if cost_price is not None: for location in locations: cost_values[location.id] = ( Decimal(str(location.quantity)) * cost_price) return cost_values @classmethod def _set_warehouse_parent(cls, locations): ''' Set the parent of child location of warehouse if not set ''' to_update = set() to_save = [] for location in locations: if location.type == 'warehouse': if not location.input_location.parent: to_update.add(location.input_location) if not location.output_location.parent: to_update.add(location.output_location) if not location.storage_location.parent: to_update.add(location.storage_location) if to_update: for child_location in to_update: child_location.parent = location to_save.append(child_location) to_update.clear() cls.save(to_save) @classmethod def create(cls, vlist): locations = super(Location, cls).create(vlist) cls._set_warehouse_parent(locations) cls._default_warehouse_cache.clear() return locations @classmethod def write(cls, *args): super(Location, cls).write(*args) locations = sum(args[::2], []) cls._set_warehouse_parent(locations) cls._default_warehouse_cache.clear() ids = [l.id for l in locations] warehouses = cls.search([ ('type', '=', 'warehouse'), ['OR', ('storage_location', 'in', ids), ('input_location', 'in', ids), ('output_location', 'in', ids), ]]) fields = ('storage_location', 'input_location', 'output_location') wh2childs = {} for warehouse in warehouses: in_out_sto = (getattr(warehouse, f).id for f in fields) for location in locations: if location.id not in in_out_sto: continue childs = wh2childs.setdefault(warehouse.id, cls.search([ ('parent', 'child_of', warehouse.id), ])) if location not in childs: raise LocationValidationError( gettext('stock.msg_location_child_of_warehouse', location=location.rec_name, warehouse=warehouse.rec_name)) @classmethod def delete(cls, *args): super().delete(*args) cls._default_warehouse_cache.clear() @classmethod def copy(cls, locations, default=None): if default is None: default = {} else: default = default.copy() res = [] for location in locations: if location.type == 'warehouse': wh_default = default.copy() wh_default['type'] = 'view' wh_default['input_location'] = None wh_default['output_location'] = None wh_default['storage_location'] = None wh_default['childs'] = None new_location, = super(Location, cls).copy([location], default=wh_default) with Transaction().set_context( cp_warehouse_locations={ 'input_location': location.input_location.id, 'output_location': location.output_location.id, 'storage_location': location.storage_location.id, }, cp_warehouse_id=new_location.id): cls.copy(location.childs, default={'parent': new_location.id}) cls.write([new_location], { 'type': 'warehouse', }) else: new_location, = super(Location, cls).copy([location], default=default) warehouse_locations = Transaction().context.get( 'cp_warehouse_locations') or {} if location.id in warehouse_locations.values(): cp_warehouse = cls( Transaction().context['cp_warehouse_id']) for field, loc_id in warehouse_locations.items(): if loc_id == location.id: cls.write([cp_warehouse], { field: new_location.id, }) res.append(new_location) return res @classmethod def view_attributes(cls): storage_types = Eval('type').in_(['storage', 'warehouse', 'view']) return super().view_attributes() + [ ('/tree/field[@name="quantity"]', 'visual', If( storage_types & (Eval('quantity', 0) < 0), 'danger', ''), ['type']), ('/tree/field[@name="forecast_quantity"]', 'visual', If( storage_types & (Eval('forecast_quantity', 0) < 0), 'warning', ''), ['type']), ]
class Asset(): __metaclass__ = PoolMeta __name__ = 'account.asset' rate = fields.Numeric( 'Rate', digits=(14, 10), states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), 'invisible': (Eval('depreciation_method') != 'declining_balance'), 'required': (Eval('depreciation_method') == 'declining_balance'), }, depends=['state', 'depreciation_method']) half_year_rule = fields.Boolean( 'Half Year Rule', states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), 'invisible': (Eval('depreciation_method') != 'declining_balance'), }, depends=['state', 'depreciation_method']) @classmethod def __setup__(cls): super(Asset, cls).__setup__() cls.depreciation_method.selection.append( ('declining_balance', 'Declining Balance')) def compute_depreciation(self, date, dates): """ Returns the depreciation amount for an asset on a certain date. """ # calculate remaining amount to depreciate. amount = (self.value - self.get_depreciated_amount() - self.residual_value) if self.depreciation_method == 'linear': super(Asset, cls).compute_depreciation(self, date, dates) elif self.depreciation_method == 'declining_balance': # V = P * (1 - R) ** N # V is the depreciated value after N periods # P is value to depreciate # R is rate of depreciation at each period start_date = max( [self.start_date - relativedelta.relativedelta(days=1)] + [l.date for l in self.lines]) # Calculate number of periods in depreciation range. delta = relativedelta.relativedelta(date, start_date) if self.frequency == 'monthly': period = delta.months + 12 * delta.years if delta.days > 0: period += 1 elif self.frequency == 'yearly': period = delta.years if delta.months > 0: period += 1 if self.get_depreciated_amount() > 0: # Asset has some depreciation posted. # Half Year Rule irrelevant here. # print "Depreciating unposted remainder." else: # Asset has not posted depreciation yet. balance = self.value for n in range(0, period): depreciation = (balance - self.residual_value) * self.rate if n == 0 and self.half_year_rule: depreciation *= Decimal(0.5) balance -= depreciation return self.company.currency.round(depreciation)
class New_products(ModelSQL, ModelView): "New_product" __name__ = "hrp_product.new_products" _order_name = 'rec_name' manufacturers_code = fields.Function(fields.Char('Manufacturers_code'), 'get_manufacturers_code', 'set_manufacturers_code') medical_insurance_code = fields.Function( fields.Char('Medical_insurance_code', select=True), 'get_medical_insurance_code', 'set_medical_insurance_code') attach = fields.Function(fields.Char('Attach', select=True), 'get_attach', 'set_attach') concentration = fields.Function(fields.Char('Concentration', select=True), 'get_concentration', 'set_concentration') concentration_unit = fields.Function( fields.Char('Concentration_unit', select=True), 'get_concentration_unit', 'set_concentration_unit') dose = fields.Function(fields.Char('Dose', select=True), 'get_dose', 'set_dose') dose_unit = fields.Function(fields.Char('Dose_unit', select=True), 'get_dose_unit', 'set_dose_unit') retrieve_the_code = fields.Function( fields.Char('Retrieve_the_code', select=True, required=True), 'get_retrieve_the_code', 'set_retrieve_the_code', 'search_retrieve_the_code') capacity = fields.Function(fields.Char('Capacity', select=True), 'get_capacity', 'set_capacity') drug_specifications = fields.Function( fields.Char('Drug_specifications', select=True, readonly=True), 'get_drug_specifications', 'set_drug_specifications') is_direct_sending = fields.Function( fields.Boolean('Is_direct_sending', select=True), 'get_is_direct_sending', 'set_is_direct_sending') is_antimicrobials = fields.Function( fields.Boolean('Is_antimicrobials', select=True), 'get_is_antimicrobials', 'set_is_antimicrobials') a_charge = fields.Function( fields.Integer('A_charge', select=True, readonly=False), 'get_a_charge', 'set_a_charge') drup_level = fields.Function(fields.Char('Drup_level', select=True), 'get_drup_level', 'set_drup_level') compound_dose = fields.Function(fields.Float('Compound_dose', select=True), 'get_compound_dose', 'set_compound_dose') purchase_code = fields.Function( fields.Char('purchase_code', select=True, required=True), 'get_purchase_code', 'set_purchase_code') retail_package = fields.Function( fields.Many2One('product.uom', 'Retail Package'), 'get_retail_package', 'set_retail_package') medical_insurance_description = fields.Function( fields.Char('Medical_insurance_description', select=True), 'get_medical_insurance_description', 'set_medical_insurance_description') new_term = fields.Function(fields.Char('new_term', select=True), 'get_new_term', 'set_new_term') state = fields.Function(fields.Char('State', select=True, required=False), 'get_state', 'set_state') min_Package = fields.Function( fields.Many2One('product.uom', 'Min_Package', required=False), 'get_min_Package', 'set_min_Package') uom_min_Package = fields.Function( fields.Char('Uom Min Package', required=True), 'get_uom_min_Package', 'set_uom_min_Package') dosage_form_description = fields.Function( fields.Char('Dosage_form_description', select=True), 'get_dosage_form_description', 'set_dosage_form_description') manufacturers_describtion = fields.Function( fields.Char('Manufacturers_describtion', required=False, select=True), 'get_manufacturers_describtion', 'set_manufacturers_describtion') poison_hemp_spirit = fields.Function( fields.Selection([('common', u'普通'), ('narcosis', u'麻醉'), ('spirit_one', u'精神1'), ('spirit_two', u'精神2')], 'Poison_hemp_spirit', select=True), 'get_poison_hemp_spirit', 'set_poison_hemp_spirit') national_essential_medicines = fields.Function( fields.Boolean('National_essential_medicines', select=True), 'get_national_essential_medicines', 'set_national_essential_medicines') interim = fields.Function( fields.Selection([('1', u''), ('2', u'是')], 'interim', select=True), 'get_interim', 'set_interim') ############################# mark = fields.Many2One('product.template', 'Mark', select=True) name = fields.Function( fields.Char("Name", size=None, required=True, translate=True, select=True, states=STATES, depends=DEPENDS), "get_name", "set_name", "searcher_name") active = fields.Function( fields.Boolean('Active', select=False, readonly=True), "get_active", "set_active", 'seacher_active') type = fields.Function( fields.Selection(TYPES, 'Type', required=True, states=STATES, depends=DEPENDS), "get_type", "set_type") default_uom = fields.Function( fields.Many2One('product.uom', 'Default UOM', required=False, states=STATES, depends=DEPENDS), 'get_default_uom', 'set_default_uom') uom_default_uom = fields.Function( fields.Char('Uom Default UOM', required=True, states=STATES, depends=DEPENDS), 'get_uom_default_uom', 'set_uom_default_uom') code = fields.Function( fields.Char("Code", size=None, required=True, states=STATES, depends=DEPENDS), 'get_code', 'set_code') list_price = fields.Function( fields.Property( fields.Numeric( 'List Price', states={ 'readonly': False # Eval('drug_specifications') != '', }, digits=price_digits, depends=['active', 'default_uom'], required=True)), 'get_list_price', 'set_list_price') cost_price = fields.Function( fields.Property( fields.Numeric( 'Cost Price', states={ 'readonly': False # Eval('drug_specifications') != '', }, digits=price_digits, depends=DEPENDS, required=True)), 'get_list_price', 'set_cost_price') cost_price_method = fields.Function( fields.Property( fields.Selection(COST_PRICE_METHODS, 'Cost Method', required=True, states=STATES, depends=DEPENDS)), 'get_cost_price_method', 'set_cost_price_method') categories = fields.Function( fields.Many2One('product.category', 'Categories', required=True, states=STATES, depends=DEPENDS), 'get_categories', 'set_categories', 'search_categories') salable = fields.Function( fields.Boolean('Salable', states={ 'readonly': ~Eval('active', True), }, depends=['active']), 'get_salable', 'set_salable') purchasable = fields.Function( fields.Boolean('Purchasable', states={ 'readonly': ~Eval('active', True), }, depends=['active']), 'get_purchasable', 'set_purchasable') consumable = fields.Function( fields.Boolean('Consumable', states={ 'readonly': True, 'invisible': Eval('type', 'goods') != 'goods', }, depends=['active', 'type']), 'get_consumable', 'set_consumable') party = fields.Function( fields.Many2One('party.party', 'Supplier', domain=[('type_', '=', 'supplier')], required=True, ondelete='CASCADE', select=True), 'get_party', 'set_party') approval_number = fields.Function(fields.Char('Approval number'), 'get_approa_number', 'set_approval_number') is_atict = fields.Function(fields.Boolean('is_atict'), 'get_is_atict', 'set_is_atict') homemade = fields.Function(fields.Boolean('homemade'), 'get_homemade', 'set_homemade') @classmethod def searcher_name(cls, name, clause): New_products = Pool().get("hrp_product.new_products") product = New_products.search([]) ids = [] for i in product: if clause[2].strip('%') in i.name: ids.append(i.id) return [('id', 'in', ids)] @classmethod def search_categories(cls, name, clause): New_products = Pool().get("hrp_product.new_products") product = New_products.search([]) ids = [] for i in product: if clause[2].strip('%') == i.categories.name: ids.append(i.id) return [('id', 'in', ids)] @classmethod def search_retrieve_the_code(cls, name, clause): pass def get_homemade(self, name): return self.mark.homemade @classmethod def set_homemade(cls, homemade, name, value): pass def get_is_atict(self, name): return self.mark.is_atict @classmethod def set_is_atict(cls, set_is_atict, name, value): pass def get_approa_number(self, name): return self.mark.approval_number @classmethod def set_approval_number(cls, set_approval_number, name, value): pass def get_manufacturers_code(self, name): return self.mark.manufacturers_code def get_party(self, name): party = self.mark.product_suppliers if party: party = party[0].party.id return party def get_national_essential_medicines(self, name): return self.mark.national_essential_medicines def get_interim(self, name): return self.mark.interim def get_poison_hemp_spirit(self, name): return self.mark.poison_hemp_spirit def get_manufacturers_describtion(self, name): return self.mark.manufacturers_describtion def get_dosage_form_description(self, name): return self.mark.dosage_form_description def get_uom_min_Package(self, name): return self.mark.min_Package.name def get_min_Package(self, name): return self.mark.min_Package.id def get_state(self, name): return self.mark.state def get_medical_insurance_description(self, name): return self.mark.medical_insurance_description def get_new_term(self, name): return self.mark.new_term def get_retail_package(self, name): return self.mark.retail_package.id def get_purchase_code(self, name): return self.mark.purchase_code def get_compound_dose(self, name): return self.mark.compound_dose def get_drup_level(self, name): return self.mark.drup_level def get_a_charge(self, name): return self.mark.a_charge def get_is_direct_sending(self, name): return self.mark.is_direct_sending def get_is_antimicrobials(self, name): return self.mark.is_antimicrobials def get_drug_specifications(self, name): return self.mark.drug_specifications def get_capacity(self, name): return self.mark.capacity def get_retrieve_the_code(self, name): return self.mark.retrieve_the_code def get_dose_unit(self, name): return self.mark.dose_unit def get_dose(self, name): return self.mark.dose def get_concentration_unit(self, name): return self.mark.concentration_unit def get_concentration(self, name): return self.mark.concentration def get_attach(self, name): return self.mark.attach def get_medical_insurance_code(self, name): return self.mark.medical_insurance_code def get_name(self, name): return self.mark.name def get_active(self, name): return self.mark.active def get_type(self, name): return self.mark.type def get_uom_default_uom(self, name): return self.mark.default_uom.name def get_default_uom(self, name): ### return self.mark.default_uom.id def get_code(self, name): return self.mark.products[0].code def get_list_price(self, name): list_price = self.mark.list_price list_prices = self.mark.cost_price if name == 'cost_price': return list_prices if name == 'list_price': return list_price def get_cost_price(self, name): return self.mark.cost_price def get_cost_price_method(self, name): return self.mark.cost_price_method def get_categories(self, name): categories = self.mark.categories if categories: ids = categories[0].id return ids def get_salable(self, name): return self.mark.salable def get_purchasable(self, name): return self.mark.purchasable def get_consumable(self, name): return self.mark.consumable @classmethod def set_name(cls, set_name, name, value): pass @classmethod def set_is_antimicrobials(cls, set_is_antimicrobials, name, value): pass @classmethod def set_party(cls, set_party, name, value): pass @classmethod def set_manufacturers_code(cls, set_manufacturers_code, name, value): pass @classmethod def set_medical_insurance_code(cls, set_medical_insurance_code, name, value): pass @classmethod def set_get_manufacturers_code(cls, get_manufacturers_code, name, value): pass @classmethod def set_attach(cls, set_attach, name, value): pass @classmethod def set_concentration(cls, set_concentration, name, value): pass @classmethod def set_concentration_unit(cls, set_concentration_unit, name, value): pass @classmethod def set_dose(cls, set_dose, name, value): pass @classmethod def set_dose_unit(cls, set_dose_unit, name, value): pass @classmethod def set_retrieve_the_code(cls, set_retrieve_the_code, name, value): pass @classmethod def set_capacity(cls, set_capacity, name, value): pass @classmethod def set_drug_specifications(cls, set_drug_specifications, name, value): pass @classmethod def set_is_direct_sending(cls, set_is_direct_sending, name, value): pass @classmethod def set_a_charge(cls, set_a_charge, name, value): pass @classmethod def set_drup_level(cls, set_drup_level, name, value): pass @classmethod def set_compound_dose(cls, set_compound_dose, name, value): pass @classmethod def set_purchase_code(cls, set_purchase_code, name, value): pass @classmethod def set_retail_package(cls, set_retail_package, name, value): pass @classmethod def set_medical_insurance_description(cls, set_medical_insurance_description, name, value): pass @classmethod def set_new_term(cls, set_new_term, name, value): pass @classmethod def set_state(cls, set_state, name, value): pass @classmethod def set_min_Package(cls, set_min_Package, name, value): pass @classmethod def set_uom_min_Package(cls, set_min_Package, name, value): pass @classmethod def set_dosage_form_description(cls, set_dosage_form_description, name, value): pass @classmethod def set_manufacturers_describtion(cls, set_manufacturers_describtion, name, value): pass @classmethod def set_poison_hemp_spirit(cls, set_poison_hemp_spirit, name, value): pass @classmethod def set_national_essential_medicines(cls, set_national_essential_medicines, name, value): pass @classmethod def set_interim(cls, set_interim, name, value): pass @classmethod def set_code(cls, set_code, name, value): pass @classmethod def set_active(cls, set_active, name, value): pass @classmethod def set_type(cls, set_type, name, value): pass @classmethod def set_default_uom(cls, set_default_uom, name, value): pass @classmethod def set_uom_default_uom(cls, set_default_uom, name, value): pass @classmethod def set_list_price(cls, set_list_price, name, value): pass @classmethod def set_cost_price(cls, set_cost_price, name, value): pass @classmethod def set_cost_price_method(cls, set_cost_price_method, name, value): pass @classmethod def set_categories(cls, set_categories, name, value): pass @classmethod def set_salable(cls, set_salable, name, value): pass @classmethod def set_purchasable(cls, set_purchasable, name, value): pass @classmethod def set_consumable(cls, set_consumable, name, value): pass @classmethod def seacher_active(cls, name, clause): pass @staticmethod def default_salable(): return True @staticmethod def default_cost_price_method(): return 'fixed' @staticmethod def default_purchasable(): return True @staticmethod def default_approval_number(): return '' @staticmethod def default_interim(): return '00' @staticmethod def default_active(): return True @staticmethod def default_consumable(): return False @staticmethod def default_type(): return 'goods' @fields.depends('code') def on_change_code(self, name=None): if self.code: for ch in self.code: if u'\u4e00' <= ch <= u'\u9fff': raise TypeError("'Can't be Chinese") @classmethod def create(cls, vlist): pool = Pool() UomCategory = Pool().get('product.uom.category') Uom = Pool().get('product.uom') product_templates = pool.get("product.template") product_products = pool.get("product.product") product_supplier = pool.get("purchase.product_supplier") product_configurations = pool.get("product.configuration") product_configuration = product_configurations.search([]) for value in vlist: product_codes = product_products.search([('name', '=', value['code'])]) if product_codes: cls.raise_user_error(u'%s已经存在!') % value['name'] uom_category = UomCategory.create([{ 'name': value['name'] + '/' + value['code'] }]) uom_min_Package = Uom.create([{ u'category': uom_category[0].id, u'digits': 2, u'name': value['uom_min_Package'], u'rounding': 1, u'symbol': value['uom_min_Package'], u'rate': 1.0, u'factor': 1.0, u'active': True }]) uom_default_uom = Uom.create([{ u'category': uom_category[0].id, u'digits': 2, u'name': value['uom_default_uom'], u'rounding': 0.01, u'symbol': value['uom_default_uom'], u'rate': round(1 / float(int(value['capacity'])), 12), u'factor': float(value['capacity']), u'active': True }]) print value['name'] is_antimicrobials = value['is_antimicrobials'] party = value['party'] medical_insurance_code = value['medical_insurance_code'] attach = value['attach'] concentration = value['concentration'] concentration_unit = value['concentration_unit'] dose_unit = value['dose_unit'] retrieve_the_code = value['retrieve_the_code'] capacity = value['capacity'] is_direct_sending = value['is_direct_sending'] approval_number = value['approval_number'] a_charge = value['a_charge'] drup_level = value['drup_level'] compound_dose = value['compound_dose'] purchase_code = value['purchase_code'] retail_package = uom_min_Package[0].id # value['retail_package'] medical_insurance_description = value[ 'medical_insurance_description'] new_term = value['new_term'] state = value['state'] min_Package = Uom(uom_min_Package[0].id) dosage_form_description = value['dosage_form_description'] interim = value['interim'] national_essential_medicines = value[ 'national_essential_medicines'] poison_hemp_spirit = value['poison_hemp_spirit'] manufacturers_describtion = value['manufacturers_describtion'] name = value['name'] active = True type = value['type'] default_uom = uom_default_uom[0].id code = value['code'] list_price = value['list_price'] cost_price = value['cost_price'] cost_price_method = value['cost_price_method'] categories = [[u'add', [value['categories']]]] salable = value['salable'] purchasable = value['purchasable'] manufacturers_code = value['manufacturers_code'] consumable = value['consumable'] dose = value['dose'] if 'dose' in value and dose != None: drug_specificationss = str( value['dose'].encode('utf-8')) + str( (value['dose_unit']).encode('utf-8')) + '*' + str( (value['capacity'] ).encode('utf-8')) + str(min_Package) elif 'concentration' in value and concentration != None: drug_specificationss = str( value['concentration']).encode('utf-8') + ( value['concentration_unit'] ).encode('utf-8') + '*' + str( (value['capacity']).encode('utf-8')) + str(min_Package) else: drug_specificationss = '*' + str( (value['capacity']).encode('utf-8')) + str(min_Package) value['drug_specifications'] = str(drug_specificationss) add_each = [{ 'is_antimicrobials': is_antimicrobials, 'medical_insurance_code': medical_insurance_code, 'attach': attach, 'concentration': concentration, 'concentration_unit': concentration_unit, 'dose': dose, 'dose_unit': dose_unit, 'retrieve_the_code': retrieve_the_code, 'capacity': capacity, 'is_direct_sending': is_direct_sending, 'a_charge': a_charge, 'drup_level': drup_level, 'compound_dose': compound_dose, 'purchase_code': purchase_code, 'retail_package': retail_package, 'medical_insurance_description': medical_insurance_description, 'new_term': new_term, 'state': state, 'manufacturers_code': manufacturers_code, 'min_Package': min_Package, 'dosage_form_description': dosage_form_description, 'manufacturers_describtion': manufacturers_describtion, 'poison_hemp_spirit': poison_hemp_spirit, 'national_essential_medicines': national_essential_medicines, 'interim': interim, 'drug_specifications': drug_specificationss, 'name': name, 'active': active, 'type': type, 'default_uom': default_uom, 'list_price': list_price, 'cost_price': cost_price, 'cost_price_method': cost_price_method, 'categories': categories, 'approval_number': approval_number, 'salable': salable, 'purchasable': purchasable, 'consumable': consumable, 'sale_uom': default_uom, 'purchase_uom': default_uom, }] account_revenue = product_configuration[0].account_revenue shelf_life_state = product_configuration[0].shelf_life_state account_expense = product_configuration[0].account_expense expiration_state = product_configuration[0].expiration_state if account_revenue == None: raise ValueError('Please fill in the product configuration') else: add_each[0]['account_revenue'] = int(account_revenue) if shelf_life_state == None: raise ValueError('Please fill in the product configuration') else: add_each[0]['shelf_life_state'] = str(shelf_life_state) if account_expense == None: raise ValueError('Please fill in the product configuration') else: add_each[0]['account_expense'] = int(account_expense) if expiration_state == None: raise ValueError('Please fill in the product configuration') else: add_each[0]['expiration_state'] = str(expiration_state) product_template = product_templates.create(add_each) marks = int(product_template[0].id) product_product = product_products.create([{ 'template': marks, 'code': code, 'active': True }]) product_supplier.create([{'product': marks, 'party': party}]) value['mark'] = marks return super(New_products, cls).create(vlist) @classmethod def delete(cls, records): pool = Pool() UomCategory = Pool().get('product.uom.category') Uom = Pool().get('product.uom') product_templates = pool.get("product.template") product_products = pool.get("product.product") for value in records: mark = int(value.mark) product_template = product_templates.search([('id', '=', mark)]) uom_uom_default_uom = Uom.search([ ('category', '=', product_template[0].default_uom.category.id) ]) uom_category = UomCategory.search([ ('id', '=', product_template[0].default_uom.category.id) ]) product_templates.delete(product_template) Uom.delete(uom_uom_default_uom) # UomCategory.delete(uom_category) return super(New_products, cls).delete(records) @classmethod def write(cls, records, values, *args): pool = Pool() UomCategory = Pool().get('product.uom.category') Uom = Pool().get('product.uom') product_templates = pool.get("product.template") product_products = pool.get("product.product") product_suppliers = pool.get("purchase.product_supplier") for ids in records: mark = int(ids.mark) product_template = product_templates.search([('id', '=', mark)]) product_product = product_products.search([('template', '=', mark) ]) product_supplier = product_suppliers.search([('product', '=', mark) ]) uom_uom_min_Package = Uom.search([ ('id', '=', product_template[0].min_Package.id) ]) uom_uom_default_uom = Uom.search([ ('id', '=', product_template[0].default_uom.id) ]) uom_category = UomCategory.search([ ('id', '=', product_template[0].default_uom.category.id) ]) lv = {} lvc = {} uom_min_Package = {} uom_default_uom = {} if 'name' in values: UomCategory.write(uom_category, { 'name': values['name'] + uom_category[0].name.split('/')[1] }) if 'uom_min_Package' in values: uom_min_Package['name'] = values['uom_min_Package'] uom_min_Package['symbol'] = values['uom_min_Package'] values.pop('uom_min_Package') Uom.write(uom_uom_min_Package, uom_min_Package) if 'uom_default_uom' in values: uom_default_uom['name'] = values['uom_default_uom'] uom_default_uom['symbol'] = values['uom_default_uom'] values.pop('uom_default_uom') if 'capacity' in values: Uom.write(uom_uom_default_uom, {'active': False}) uom_uom_default_uom = Uom.create([{ u'category': uom_category[0].id, u'digits': 2, u'name': uom_uom_default_uom[0].name, u'rounding': 0.01, u'symbol': uom_uom_default_uom[0].name, u'rate': round(1 / float(values['capacity']), 12), u'factor': float(values['capacity']), u'active': True }]) values['sale_uom'] = uom_uom_default_uom[0].id values['purchase_uom'] = uom_uom_default_uom[0].id values['default_uom'] = uom_uom_default_uom[0].id if uom_default_uom: Uom.write(uom_uom_default_uom, uom_default_uom) if 'party' in values: lvc['party'] = values['party'] values.pop('party') if lvc != {}: product_suppliers.write(product_supplier, lvc) if 'code' in values: UomCategory.write(uom_category, { 'name': uom_category[0].name.split('/')[0] + values['name'] }) lv['code'] = values['code'] values.pop('code') if 'template' in values: lv['template'] = values[mark] values.pop('template') if lv != {}: product_products.write(product_product, lv) if 'mark' in values: values.pop('mark') if 'categories' in values: values['categories'] = [[ u'add', [values['categories']] ], [u'remove', [product_template[0].categories[0].id]]] if values != {}: product_templates.write(product_template, values) if 'sale_uom' in values: values.pop('sale_uom') if 'purchase_uom' in values: values.pop('purchase_uom') return super(New_products, cls).write(records, values)
class Sale: "Sale" __name__ = 'sale.sale' is_international_shipping = fields.Function( fields.Boolean("Is International Shipping"), 'on_change_with_is_international_shipping') weight = fields.Function( fields.Float( "Weight", digits=(16, Eval('weight_digits', 2)), depends=['weight_digits'], ), 'get_weight') weight_uom = fields.Function(fields.Many2One('product.uom', 'Weight UOM'), 'get_weight_uom') weight_digits = fields.Function(fields.Integer('Weight Digits'), 'on_change_with_weight_digits') available_carrier_services = fields.Function( fields.One2Many("carrier.service", None, 'Available Carrier Services'), getter="on_change_with_available_carrier_services") carrier_service = fields.Many2One( "carrier.service", "Carrier Service", domain=[('id', 'in', Eval('available_carrier_services'))], states={ "invisible": Not(Bool(Eval('carrier'))), "readonly": Eval('state') != 'draft', }, depends=['carrier', 'available_carrier_services', 'state']) carrier_cost_method = fields.Function( fields.Char('Carrier Cost Method'), "on_change_with_carrier_cost_method") @classmethod @ModelView.button_action('shipping.wizard_sale_apply_shipping') def apply_shipping(cls, sales): pass @fields.depends("carrier") def on_change_with_carrier_cost_method(self, name=None): if self.carrier: return self.carrier.carrier_cost_method def on_change_lines(self): """Pass a flag in context which indicates the get_sale_price method of carrier not to calculate cost on each line change """ with Transaction().set_context({'ignore_carrier_computation': True}): super(Sale, self).on_change_lines() @fields.depends("carrier") def on_change_with_available_carrier_services(self, name=None): if self.carrier: return map(int, self.carrier.services) return [] @classmethod def __setup__(cls): super(Sale, cls).__setup__() cls._error_messages.update({ 'warehouse_address_missing': 'Warehouse address is missing', }) cls._buttons.update({ 'apply_shipping': { "invisible": ~Eval('state').in_(['draft', 'quotation', 'confirmed']) } }) cls.__rpc__.update({'apply_shipping_rate': RPC(instantiate=0)}) @fields.depends('weight_uom') def on_change_with_weight_digits(self, name=None): if self.weight_uom: return self.weight_uom.digits return 2 def get_weight_uom(self, name): """ Returns weight uom for the sale """ ModelData = Pool().get('ir.model.data') return ModelData.get_id('product', 'uom_pound') def get_weight(self, name): """ Returns sum of weight associated with each line """ return sum( map(lambda line: line.get_weight(self.weight_uom, silent=True), self.lines)) @fields.depends('party', 'shipment_address', 'warehouse') def on_change_with_is_international_shipping(self, name=None): """ Return True if international shipping """ from_address = self._get_ship_from_address(silent=True) if self.shipment_address and from_address and \ from_address.country and self.shipment_address.country and \ from_address.country != self.shipment_address.country: return True return False def _get_ship_from_address(self, silent=False): """ Usually the warehouse from which you ship """ if not self.warehouse.address and not silent: return self.raise_user_error('warehouse_address_missing') return self.warehouse and self.warehouse.address def add_shipping_line(self, shipment_cost, description, carrier=None, carrier_service=None): """ This method takes shipping_cost and description as arguments and writes a shipping line. It deletes any previous shipping lines which have a shipment_cost. :param shipment_cost: The shipment cost calculated according to carrier :param description: Shipping line description """ Sale = Pool().get('sale.sale') carrier = carrier or self.carrier carrier_service = carrier_service and self.carrier_service Sale.write( [self], { 'lines': [ ( 'create', [{ 'type': 'line', 'product': carrier.carrier_product.id, 'description': description, 'quantity': 1, # XXX 'unit': carrier.carrier_product.sale_uom.id, 'unit_price': shipment_cost, 'shipment_cost': shipment_cost, 'amount': shipment_cost, 'taxes': [], 'sequence': 9999, # XXX }]), ('delete', [ line for line in self.lines if line.shipment_cost is not None ]), ], # reset the amount caches or function # fields will continue to return cached values 'untaxed_amount_cache': None, 'tax_amount_cache': None, 'total_amount_cache': None, }) # reset the order total cache if self.state not in ('draft', 'quote'): Sale.store_cache([self]) def _get_carrier_context(self): "Pass sale in the context" context = super(Sale, self)._get_carrier_context() context = context.copy() context['sale'] = self.id return context def apply_shipping_rate(self, rate): """ This method applies shipping rate. Rate is a dictionary with following minimum keys: { 'display_name': Name to display, 'carrier_service': carrier.service active record, 'cost': cost, 'cost_currency': currency.currency active record, 'carrier': carrier active record, } It also creates a shipment line by deleting all existing ones. The rate could optionally have integer ids of service, carrier and currency. """ Currency = Pool().get('currency.currency') Carrier = Pool().get('carrier') CarrierService = Pool().get('carrier.service') self.carrier = Carrier(int(rate['carrier'])) self.carrier_service = CarrierService(int(rate['carrier_service'])) \ if rate['carrier_service'] else None self.save() cost_currency = Currency(int(rate['cost_currency'])) shipment_cost = cost_currency.round(rate['cost']) if self.currency != cost_currency: shipment_cost = Currency.compute(cost_currency, shipment_cost, self.currency) self.add_shipping_line( shipment_cost, rate['display_name'], self.carrier, self.carrier_service, ) def get_shipping_rates(self, carriers=None, silent=False): """ Gives a list of rates from carriers provided. If no carriers provided, return rates from all the carriers. List contains dictionary with following minimum keys: [ { 'display_name': Name to display, 'carrier_service': carrier.service active record, 'cost': cost, 'cost_currency': currency.currency active repord, 'carrier': carrier active record, }.. ] """ Carrier = Pool().get('carrier') if carriers is None: carriers = Carrier.search([]) rates = [] for carrier in carriers: rates.extend(self.get_shipping_rate(carrier, silent=silent)) return rates def get_shipping_rate(self, carrier, carrier_service=None, silent=False): """ Gives a list of rates from provided carrier and carrier service. List contains dictionary with following minimum keys: [ { 'display_name': Name to display, 'carrier_service': carrier.service active record, 'cost': cost, 'cost_currency': currency.currency active repord, 'carrier': carrier active record, }.. ] """ Company = Pool().get('company.company') if carrier.carrier_cost_method == 'product': currency = Company(Transaction().context['company']).currency rate_dict = { 'carrier_service': carrier_service, 'cost': carrier.carrier_product.list_price, 'cost_currency': currency, 'carrier': carrier, } display_name = carrier.rec_name rate_dict['display_name'] = display_name return [rate_dict] return [] @classmethod def get_allowed_carriers_domain(cls): """This method returns domain to seach allowed carriers Downstream modules can inherit and update customize this domain. """ return [] def create_shipment(self, shipment_type): with Transaction().set_context(ignore_carrier_computation=True): shipments = super(Sale, self).create_shipment(shipment_type) if shipment_type == 'out' and shipments: for shipment in shipments: if shipment.carrier: continue shipment.carrier = self.carrier shipment.carrier_service = self.carrier_service shipment.save() return shipments
class ActionReport(ActionMixin, ModelSQL, ModelView): "Action report" __name__ = 'ir.action.report' _action_name = 'report_name' model = fields.Char('Model') report_name = fields.Char('Internal Name', required=True) report = fields.Char("Path", states={ 'invisible': Eval('is_custom', False), }, depends=['is_custom']) report_content_custom = fields.Binary('Content') is_custom = fields.Function(fields.Boolean("Is Custom"), 'get_is_custom') report_content = fields.Function(fields.Binary( 'Content', filename='report_content_name'), 'get_report_content', setter='set_report_content') report_content_name = fields.Function( fields.Char('Content Name'), 'on_change_with_report_content_name') report_content_html = fields.Function(fields.Binary( "Content HTML", states={ 'invisible': ~Eval('template_extension').in_(['html', 'xhtml']), }, depends=['template_extension']), 'get_report_content_html', setter='set_report_content_html') action = fields.Many2One('ir.action', 'Action', required=True, ondelete='CASCADE') direct_print = fields.Boolean('Direct Print') single = fields.Boolean( "Single", help="Check if the template works only for one record.") translatable = fields.Boolean( "Translatable", help="Uncheck to disable translations for this report.") template_extension = fields.Selection([ ('odt', 'OpenDocument Text'), ('odp', 'OpenDocument Presentation'), ('ods', 'OpenDocument Spreadsheet'), ('odg', 'OpenDocument Graphics'), ('txt', 'Plain Text'), ('xml', 'XML'), ('html', 'HTML'), ('xhtml', 'XHTML'), ], string='Template Extension', required=True, translate=False) extension = fields.Selection( [ ('', ''), ('bib', 'BibTex'), ('bmp', 'Windows Bitmap'), ('csv', 'Text CSV'), ('dbf', 'dBase'), ('dif', 'Data Interchange Format'), ('doc', 'Microsoft Word 97/2000/XP'), ('doc6', 'Microsoft Word 6.0'), ('doc95', 'Microsoft Word 95'), ('docbook', 'DocBook'), ('docx', 'Microsoft Office Open XML Text'), ('docx7', 'Microsoft Word 2007 XML'), ('emf', 'Enhanced Metafile'), ('eps', 'Encapsulated PostScript'), ('gif', 'Graphics Interchange Format'), ('html', 'HTML Document'), ('jpg', 'Joint Photographic Experts Group'), ('met', 'OS/2 Metafile'), ('ooxml', 'Microsoft Office Open XML'), ('pbm', 'Portable Bitmap'), ('pct', 'Mac Pict'), ('pdb', 'AportisDoc (Palm)'), ('pdf', 'Portable Document Format'), ('pgm', 'Portable Graymap'), ('png', 'Portable Network Graphic'), ('ppm', 'Portable Pixelmap'), ('ppt', 'Microsoft PowerPoint 97/2000/XP'), ('psw', 'Pocket Word'), ('pwp', 'PlaceWare'), ('pxl', 'Pocket Excel'), ('ras', 'Sun Raster Image'), ('rtf', 'Rich Text Format'), ('latex', 'LaTeX 2e'), ('sda', 'StarDraw 5.0 (OpenOffice.org Impress)'), ('sdc', 'StarCalc 5.0'), ('sdc4', 'StarCalc 4.0'), ('sdc3', 'StarCalc 3.0'), ('sdd', 'StarImpress 5.0'), ('sdd3', 'StarDraw 3.0 (OpenOffice.org Impress)'), ('sdd4', 'StarImpress 4.0'), ('sdw', 'StarWriter 5.0'), ('sdw4', 'StarWriter 4.0'), ('sdw3', 'StarWriter 3.0'), ('slk', 'SYLK'), ('svg', 'Scalable Vector Graphics'), ('svm', 'StarView Metafile'), ('swf', 'Macromedia Flash (SWF)'), ('sxc', 'OpenOffice.org 1.0 Spreadsheet'), ('sxi', 'OpenOffice.org 1.0 Presentation'), ('sxd', 'OpenOffice.org 1.0 Drawing'), ('sxd3', 'StarDraw 3.0'), ('sxd5', 'StarDraw 5.0'), ('sxw', 'Open Office.org 1.0 Text Document'), ('text', 'Text Encoded'), ('tiff', 'Tagged Image File Format'), ('txt', 'Plain Text'), ('wmf', 'Windows Metafile'), ('xhtml', 'XHTML Document'), ('xls', 'Microsoft Excel 97/2000/XP'), ('xls5', 'Microsoft Excel 5.0'), ('xls95', 'Microsoft Excel 95'), ('xlsx', 'Microsoft Excel 2007/2010 XML'), ('xpm', 'X PixMap'), ], translate=False, string='Extension', help='Leave empty for the same as template, ' 'see LibreOffice documentation for compatible format.') module = fields.Char('Module', readonly=True, select=True) _template_cache = MemoryCache('ir.action.report.template', context=False) @classmethod def __register__(cls, module_name): super(ActionReport, cls).__register__(module_name) transaction = Transaction() cursor = transaction.connection.cursor() table = cls.__table_handler__(module_name) action_report = cls.__table__() # Migration from 3.4 remove report_name_module_uniq constraint table.drop_constraint('report_name_module_uniq') # Migration from 4.4 replace plain extension by txt cursor.execute( *action_report.update([action_report.extension], ['txt'], where=action_report.extension == 'plain')) @staticmethod def default_type(): return 'ir.action.report' @staticmethod def default_report_content(): return None @staticmethod def default_direct_print(): return False @classmethod def default_single(cls): return False @classmethod def default_translatable(cls): return True @staticmethod def default_template_extension(): return 'odt' @staticmethod def default_extension(): return '' @staticmethod def default_module(): return Transaction().context.get('module') or '' def get_is_custom(self, name): return bool(self.report_content_custom) @classmethod def get_report_content(cls, reports, name): contents = {} converter = fields.Binary.cast default = None format_ = Transaction().context.get('%s.%s' % (cls.__name__, name), '') if format_ == 'size': converter = len default = 0 for report in reports: data = getattr(report, name + '_custom') if not data and getattr(report, name[:-8]): try: with file_open(getattr(report, name[:-8]).replace('/', os.sep), mode='rb') as fp: data = fp.read() except Exception: data = None contents[report.id] = converter(data) if data else default return contents @classmethod def set_report_content(cls, records, name, value): cls.write(records, {'%s_custom' % name: value}) @classmethod def get_report_content_html(cls, reports, name): return cls.get_report_content(reports, name[:-5]) @classmethod def set_report_content_html(cls, reports, name, value): if value is not None: value = value.encode('utf-8') cls.set_report_content(reports, name[:-5], value) @fields.depends('name', 'template_extension') def on_change_with_report_content_name(self, name=None): return ''.join( filter(None, [self.name, os.extsep, self.template_extension])) @classmethod def get_pyson(cls, reports, name): pysons = {} field = name[6:] defaults = {} for report in reports: pysons[report.id] = (getattr(report, field) or defaults.get(field, 'null')) return pysons @classmethod def copy(cls, reports, default=None): if default is None: default = {} default = default.copy() default.setdefault('module', None) new_reports = [] for report in reports: if report.report: default['report_content'] = None default['report'] = None default['report_name'] = report.report_name new_reports.extend( super(ActionReport, cls).copy([report], default=default)) return new_reports @classmethod def write(cls, reports, values, *args): context = Transaction().context if 'module' in context: actions = iter((reports, values) + args) args = [] for reports, values in zip(actions, actions): values = values.copy() values['module'] = context['module'] args.extend((reports, values)) reports, values = args[:2] args = args[2:] cls._template_cache.clear() super(ActionReport, cls).write(reports, values, *args) def get_template_cached(self): return self._template_cache.get(self.id) def set_template_cached(self, template): self._template_cache.set(self.id, template)
class ShipmentOut(): __metaclass__ = PoolMeta __name__ = 'stock.shipment.out' remision = fields.Boolean(u'Enviar Guía de Remisión al SRI', states={ 'readonly': Eval('state') == 'done', }) placa = fields.Char('Placa del medio de Transporte', states={ 'invisible': ~Eval('remision', False), 'required': Eval('remision', True), 'readonly': Eval('state') == 'done', }) cod_estab_destino = fields.Char(u'Código de establecimiento de Destino', size=3, states={ 'invisible': ~Eval('remision', False), 'required': Eval('remision', True), 'readonly': Eval('state') == 'done', }) ruta = fields.Char('Ruta', states={ 'invisible': ~Eval('remision', False), 'required': Eval('remision', True), 'readonly': Eval('state') == 'done', }) partida = fields.Char('Direccion de partida', states={ 'invisible': ~Eval('remision', False), 'required': Eval('remision', True), 'readonly': Eval('state') == 'done', }) estado_sri = fields.Char(u'Estado Comprobante-Electrónico', size=24, readonly=True, states={ 'invisible': ~Eval('remision', False), 'required': Eval('remision', True), 'readonly': Eval('state') == 'done', }) number_c = fields.Char(u'Número del Documento de Sustento', size=17, states={ 'invisible': ~Eval('remision', False), 'required': Eval('remision', True), 'readonly': Eval('state') == 'done', }) path_xml = fields.Char(u'Path archivo xml de comprobante', readonly=True) path_pdf = fields.Char(u'Path archivo pdf de factura', readonly=True) numero_autorizacion = fields.Char(u'Número de Autorización', readonly=True) transporte = fields.Many2One('carrier', 'Transportista', states={ 'invisible': ~Eval('remision', False), 'required': Eval('remision', True), 'readonly': Eval('state') == 'done', }) @classmethod def __setup__(cls): super(ShipmentOut, cls).__setup__() cls.effective_date.states['required'] = Eval('remision', True) cls.planned_date.states['required'] = Eval('remision', True) #metodo para asignar impuesto @fields.depends('moves', 'remision', 'number_c') def on_change_remision(self): venta = None invoices = None invoice = None if self.remision == True: for s in self.moves: venta = s.sale pool = Pool() Invoice = pool.get('account.invoice') invoices = Invoice.search([('description', '=', venta.reference)]) if invoices: for i in invoices: invoice = i if invoice: self.number_c = invoice.number else: self.number_c = None @classmethod @ModelView.button @Workflow.transition('done') def done(cls, shipments): pool = Pool() Move = pool.get('stock.move') Date = pool.get('ir.date') for shipment in shipments: if shipment.remision == True: shipment.get_tax_element() shipment.get_shipment_element() shipment.get_destinatarios() shipment.generate_xml_shipment() shipment.action_generate_shipment() shipment.connect_db() Move.do([m for s in shipments for m in s.outgoing_moves]) cls.write([s for s in shipments if not s.effective_date], { 'effective_date': Date.today(), }) def web_service(self): CONEXION = 'UD NO HA CONFIGURADO LOS DATOS DE CONEXION CON EL WS, \nCOMUNIQUESE CON EL ADMINISTRADOR DEL SISTEMA' pool = Pool() conexions = pool.get('res.user') conexion = conexions.search([('id', '=', 1)]) if conexion: for c in conexion: if c.direccion: address = c.cabecera + "://" + base64.decodestring( c.usuario ) + ":" + base64.decodestring( c.pass_db ) + "@" + c.direccion + ":" + c.puerto + "/" + base64.decodestring( c.name_db) return address else: self.raise_user_error(CONEXION) def connect_db(self): address_xml = self.web_service() s = xmlrpclib.ServerProxy(address_xml) pool = Pool() nombre = self.customer.name cedula = self.customer.vat_number ruc = self.company.party.vat_number nombre_e = self.company.party.name tipo = 'out_shipment' fecha = str(self.effective_date) empresa = self.company.party.name numero = self.code path_xml = self.path_xml path_pdf = self.path_pdf estado = self.estado_sri auth = self.numero_autorizacion correos = pool.get('party.contact_mechanism') correo = correos.search([('type', '=', 'email')]) for c in correo: if c.party == self.customer: to_email = c.value if c.party == self.company.party: to_email_2 = c.value email_e = to_email_2 email = to_email total = '' s.model.nodux_electronic_invoice_auth.conexiones.connect_db( nombre, cedula, ruc, nombre_e, tipo, fecha, empresa, numero, path_xml, path_pdf, estado, auth, email, email_e, total, {}) def send_mail_invoice(self, xml_element, access_key, send_m, s, server="localhost"): MAIL = u"Ud no ha configurado el correo del cliente. Diríjase a: \nTerceros->General->Medios de Contacto" pool = Pool() empresa = self.elimina_tildes(self.company.party.name) #empresa = unicode(empresa, 'utf-8') empresa = str(self.elimina_tildes(empresa)) empresa = empresa.replace(' ', '_') empresa = empresa.lower() ahora = datetime.datetime.now() year = str(ahora.year) client = self.customer.name client = client.upper() empresa_ = self.company.party.name ruc = self.company.party.vat_number if ahora.month < 10: month = '0' + str(ahora.month) else: month = str(ahora.month) tipo = 'rem_' n_tipo = "GUIA DE REMISION" ruc = access_key[10:23] est = access_key[24:27] emi = access_key[27:30] sec = access_key[30:39] num_fac = est + '-' + emi + '-' + sec numero = ruc + '_' + num_fac name_pdf = tipo + numero + '.pdf' name_xml = tipo + numero + '.xml' #nuevaruta =os.getcwd() +'/comprobantes/'+empresa+'/'+year+'/'+month +'/' nr = s.model.nodux_electronic_invoice_auth.conexiones.path_files( ruc, {}) nuevaruta = nr + empresa + '/' + year + '/' + month + '/' new_save = 'comprobantes/' + empresa + '/' + year + '/' + month + '/' self.write( [self], { 'estado_sri': 'AUTORIZADO', 'path_xml': new_save + name_xml, 'numero_autorizacion': access_key, 'path_pdf': new_save + name_pdf }) correos = pool.get('party.contact_mechanism') correo = correos.search([('type', '=', 'email')]) InvoiceReport = Pool().get('stock.shipment.out.print_shipment_e', type='report') report = InvoiceReport.execute([self.id], {}) email = '' cont = 0 for c in correo: if c.party == self.customer: email = c.value if c.party == self.company.party: f_e = c.value if email != '': to_email = email else: self.raise_user_error(MAIL) if send_m == '1': from_email = f_e else: from_email = "*****@*****.**" name = access_key + ".xml" reporte = xmlrpclib.Binary(report[1]) xml_element = unicode(xml_element, 'utf-8') xml_element = self.elimina_tildes(xml_element) xml = xmlrpclib.Binary(xml_element.replace('><', '>\n<')) save_files = s.model.nodux_electronic_invoice_auth.conexiones.save_file( empresa, name_pdf, name_xml, reporte, xml, {}) p_xml = nuevaruta + name_xml p_pdf = nuevaruta + name_pdf s.model.nodux_electronic_invoice_auth.conexiones.send_mail( name_pdf, name, p_xml, p_pdf, from_email, to_email, n_tipo, num_fac, client, empresa_, ruc, {}) return True def elimina_tildes(self, s): return ''.join((c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn')) def generate_access_key(self): fecha = self.create_date.strftime('%d%m%Y') tipo_cbte = '06' ruc = self.company.party.vat_number tipo_amb = '1' num_cbte = self.code cod_num = "13245768" tipo_emision = self.company.emission_code numero_cbte = num_cbte.replace('-', '') #unimos todos los datos en una sola cadena clave_inicial = fecha + tipo_cbte + ruc + tipo_amb + numero_cbte + cod_num + tipo_emision #recorremos la cadena para ir guardando en una lista de enteros clave = [] for c in clave_inicial: clave.append(int(c)) clave.reverse() factor = [2, 3, 4, 5, 6, 7] etapa1 = sum([n * factor[i % 6] for i, n in enumerate(clave)]) etapa2 = etapa1 % 11 digito = 11 - (etapa2) if digito == 11: digito = 0 if digito == 10: digito = 1 digito = str(digito) clave_acceso = clave_inicial + digito return clave_acceso def get_tax_element(self): """ """ company = self.company number = self.code #auth = self.journal_id.auth_id infoTributaria = etree.Element('infoTributaria') etree.SubElement(infoTributaria, 'ambiente').text = "1" #SriService.get_active_env() etree.SubElement(infoTributaria, 'tipoEmision').text = self.company.emission_code etree.SubElement(infoTributaria, 'razonSocial').text = self.company.party.name if self.company.party.commercial_name: etree.SubElement( infoTributaria, 'nombreComercial').text = self.company.party.commercial_name else: etree.SubElement(infoTributaria, 'nombreComercial').text = self.company.party.name etree.SubElement(infoTributaria, 'ruc').text = self.company.party.vat_number etree.SubElement(infoTributaria, 'claveAcceso').text = self.generate_access_key() etree.SubElement(infoTributaria, 'codDoc').text = "06" etree.SubElement(infoTributaria, 'estab').text = number[0:3] etree.SubElement(infoTributaria, 'ptoEmi').text = number[4:7] etree.SubElement(infoTributaria, 'secuencial').text = number[8:17] if self.company.party.addresses: etree.SubElement( infoTributaria, 'dirMatriz').text = self.company.party.addresses[0].street return infoTributaria def get_shipment_element(self): company = self.company customer = self.customer infoGuiaRemision = etree.Element('infoGuiaRemision') if self.company.party.addresses: etree.SubElement(infoGuiaRemision, 'dirEstablecimiento' ).text = self.company.party.addresses[0].street if self.company.party.addresses: etree.SubElement(infoGuiaRemision, 'dirPartida').text = self.partida etree.SubElement( infoGuiaRemision, 'razonSocialTransportista').text = self.transporte.party.name etree.SubElement( infoGuiaRemision, 'tipoIdentificacionTransportista').text = tipoIdentificacion[ self.transporte.party.type_document] etree.SubElement( infoGuiaRemision, 'rucTransportista').text = self.transporte.party.vat_number etree.SubElement(infoGuiaRemision, 'rise').text = "No obligatorios" if self.company.party.mandatory_accounting: etree.SubElement(infoGuiaRemision, 'obligadoContabilidad' ).text = self.company.party.mandatory_accounting else: etree.SubElement(infoGuiaRemision, 'obligadoContabilidad').text = 'NO' if self.company.party.contribuyente_especial_nro: etree.SubElement( infoGuiaRemision, 'contribuyenteEspecial' ).text = self.company.party.contribuyente_especial_nro etree.SubElement( infoGuiaRemision, 'fechaIniTransporte').text = self.planned_date.strftime('%d/%m/%Y') etree.SubElement( infoGuiaRemision, 'fechaFinTransporte').text = self.planned_date.strftime('%d/%m/%Y') etree.SubElement(infoGuiaRemision, 'placa').text = self.placa return infoGuiaRemision def get_destinatarios(self): ERROR = u"No existe factura registrada con el número que ingresó" num_mod = self.number_c pool = Pool() Invoices = pool.get('account.invoice') invoice = Invoices.search([('number', '=', num_mod)]) if invoice: pass else: self.raise_user_error(ERROR) for i in invoice: date_mod = i.invoice_date.strftime('%d/%m/%Y') num_aut = i.numero_autorizacion company = self.company customer = self.customer destinatarios = etree.Element('destinatarios') destinatario = etree.Element('destinatario') etree.SubElement( destinatario, 'identificacionDestinatario').text = self.customer.vat_number etree.SubElement(destinatario, 'razonSocialDestinatario').text = self.customer.name etree.SubElement(destinatario, 'dirDestinatario').text = self.dir_destinatario etree.SubElement(destinatario, 'motivoTraslado').text = self.motivo_traslado etree.SubElement(destinatario, 'docAduaneroUnico').text = " " etree.SubElement(destinatario, 'codEstabDestino').text = self.cod_estab_destino etree.SubElement(destinatario, 'ruta').text = self.ruta etree.SubElement(destinatario, 'codDocSustento').text = "01" etree.SubElement(destinatario, 'numDocSustento').text = num_mod if num_aut: #etree.SubElement(destinatario, 'numAutDocSustento').text = num_aut print "Si hay autorizacion" etree.SubElement( destinatario, 'fechaEmisionDocSustento' ).text = date_mod #self.create_date.strftime('%d/%m/%Y') detalles = etree.Element('detalles') def fix_chars(code): if code: code.replace(u'%', ' ').replace(u'º', ' ').replace(u'Ñ', 'N').replace(u'ñ', 'n') return code return '1' detalle = etree.Element('detalle') for move in self.moves: move = move etree.SubElement(detalle, 'codigoInterno').text = fix_chars(move.product.code) etree.SubElement(detalle, 'descripcion').text = fix_chars(move.product.name) etree.SubElement(detalle, 'cantidad').text = str(move.quantity) detalles.append(detalle) destinatario.append(detalles) destinatarios.append(destinatario) return destinatarios def generate_xml_shipment(self): guiaRemision = etree.Element('guiaRemision') guiaRemision.set("id", "comprobante") guiaRemision.set("version", "1.1.0") #generar infoTributaria infoTributaria = self.get_tax_element() guiaRemision.append(infoTributaria) #generar infoGuiaRemision infoGuiaRemision = self.get_shipment_element() guiaRemision.append(infoGuiaRemision) #generar destinatarios destinatarios = self.get_destinatarios() guiaRemision.append(destinatarios) return guiaRemision def check_before_sent(self): """ """ sql = "select autorizado_sri, number from account_invoice where state='open' and number < '%s' order by number desc limit 1" % self.number self.execute(sql) res = self.fetchone() return res[0] and True or False def action_generate_shipment(self): PK12 = u'No ha configurado los datos de la empresa. Dirijase a: \n Empresa -> NODUX WS' AUTHENTICATE_ERROR = u'Error en datos de ingreso verifique: \nUSARIO Y CONTRASEÑA' ACTIVE_ERROR = u"Ud. no se encuentra activo, verifique su pago. \nComuníquese con NODUX" WAIT_FOR_RECEIPT = 3 TITLE_NOT_SENT = u'No se puede enviar el comprobante electronico al SRI' MESSAGE_SEQUENCIAL = u'Los comprobantes electrónicos deben ser enviados al SRI en orden secuencial' MESSAGE_TIME_LIMIT = u'Se ha excedido el límite de tiempo. Los comprobantes electrónicos deben \nser enviados al SRI, en un plazo máximo de 24 horas' #Validar que el envio del comprobante electronico se realice dentro de las 24 horas posteriores a su emision pool = Pool() Date = pool.get('ir.date') date_f = self.effective_date date = Date.today() limit = (date - date_f).days #if limit > 1: # self.raise_user_error(MESSAGE_TIME_LIMIT) # Validar que el envio de los comprobantes electronicos sea secuencial #if not self.check_before_sent(): #self.raise_user_error(TITLE_NOT_SENT, MESSAGE_SEQUENCIAL) usuario = self.company.user_ws password_u = self.company.password_ws access_key = self.generate_access_key() address_xml = self.web_service() s = xmlrpclib.ServerProxy(address_xml) name = self.company.party.name name_l = name.lower() name_r = name_l.replace(' ', '_').replace(u'á', 'a').replace( u'é', 'e').replace(u'í', 'i').replace(u'ó', 'o').replace(u'ú', 'u') name_c = name_r + '.p12' """ if self.company.file_pk12: archivo = self.company.file_pk12 else : self.raise_user_error(PK12) f = open(name_c, 'wb') f.write(archivo) f.close() """ authenticate, send_m, active = s.model.nodux_electronic_invoice_auth.conexiones.authenticate( usuario, password_u, {}) if authenticate == '1': pass else: self.raise_user_error(AUTHENTICATE_ERROR) if active == '1': self.raise_user_error(ACTIVE_ERROR) else: pass nuevaruta = s.model.nodux_electronic_invoice_auth.conexiones.save_pk12( name_l, {}) """ shutil.copy2(name_c, nuevaruta) os.remove(name_c) print etree.tostring(guiaRemision1,pretty_print=True ,xml_declaration=True, encoding="utf-8") """ guiaRemision1 = self.generate_xml_shipment() guiaRemision = etree.tostring(guiaRemision1, encoding='utf8', method='xml') #validacion del xml (llama metodo validate xml de sri) a = s.model.nodux_electronic_invoice_auth.conexiones.validate_xml( guiaRemision, 'out_shipment', {}) if a: self.raise_user_error(a) file_pk12 = base64.encodestring(nuevaruta + '/' + name_c) file_check = (nuevaruta + '/' + name_c) password = self.company.password_pk12 error = s.model.nodux_electronic_invoice_auth.conexiones.check_digital_signature( file_check, {}) if error == '1': self.raise_user_error( 'No se ha encontrado el archivo de firma digital (.p12)') signed_document = s.model.nodux_electronic_invoice_auth.conexiones.apply_digital_signature( guiaRemision, file_pk12, password, {}) #envio al sri para recepcion del comprobante electronico result = s.model.nodux_electronic_invoice_auth.conexiones.send_receipt( signed_document, {}) if result != True: self.raise_user_error(result) time.sleep(WAIT_FOR_RECEIPT) # solicitud al SRI para autorizacion del comprobante electronico doc_xml, m, auth, path, numero, num = s.model.nodux_electronic_invoice_auth.conexiones.request_authorization( access_key, name_r, 'out_shipment', signed_document, {}) if doc_xml is None: msg = ' '.join(m) raise m if auth == 'NO AUTORIZADO': self.write([self], {'estado_sri': 'NO AUTORIZADO'}) else: pass self.send_mail_invoice(doc_xml, access_key, send_m, s) return access_key def action_generate_lote_shipment(self): """ """ LIMIT_TO_SEND = 5 WAIT_FOR_RECEIPT = 3 TITLE_NOT_SENT = u'No se puede enviar el comprobante electronico al SRI' MESSAGE_SEQUENCIAL = u'Los comprobantes electronicos deben ser enviados al SRI en orden secuencial' MESSAGE_TIME_LIMIT = u'Los comprobantes electronicos deben ser enviados al SRI para su autorizacion, en un plazo maximo de 24 horas' if not self.type in ['out_shipment']: print "no disponible para otros documentos" pass access_key = self.generate_access_key_lote() if self.type == 'out_shipment': # XML del comprobante electronico: factura lote = self.generate_xml_lote_shipment() #validacion del xml (llama metodo validate xml de sri) inv_xml = DocumentXML(lote, 'lote') inv_xml.validate_xml() # solicitud de autorizacion del comprobante electronico xmlstr = etree.tostring(lote, encoding='utf8', method='xml') inv_xml.send_receipt(xmlstr) time.sleep(WAIT_FOR_RECEIPT) doc_xml, m, auth = inv_xml.request_authorization_lote(access_key) print "esta es la auth", auth if doc_xml is None: msg = ' '.join(m) raise m if auth == False: vals = { 'estado_sri': 'NO AUTORIZADO', } else: vals = { 'estado_sri': auth.estado, } self.write([self], vals) time.sleep(WAIT_FOR_RECEIPT) self.send_mail_invoice(doc_xml) if auth == False: self.raise_user_error('error', (m, )) return access_key def generate_xml_lote_shipment(self): pool = Pool() usuario = self.company.user_ws password_u = self.company.password_ws address_xml = self.web_service() s = xmlrpclib.ServerProxy(address_xml) name = self.company.party.name name_r = name.replace(' ', '_') name_l = name_r.lower() name_c = name_l + '.p12' if self.company.file_pk12: archivo = self.company.file_pk12 else: self.raise_user_error(PK12) f = open(name_c, 'wb') f.write(archivo) f.close() authenticate, send_m = s.model.nodux_electronic_invoice_auth.conexiones.authenticate( usuario, password_u, {}) if authenticate == '1': pass else: self.raise_user_error(AUTHENTICATE_ERROR) nuevaruta = s.model.nodux_electronic_invoice_auth.conexiones.save_pk12( name_l, {}) shutil.copy2(name_c, nuevaruta) os.remove(name_c) file_pk12 = base64.encodestring(nuevaruta + '/' + name_c) password = base64.encodestring(self.company.password_pk12) Invoice = pool.get('account.invoice') invoices = Invoice.browse(Transaction().context['active_ids']) print invoices lote = etree.Element('lote') lote.set("version", "1.0.0") etree.SubElement(lote, 'claveAcceso').text = self.generate_access_key_lote() etree.SubElement(lote, 'ruc').text = self.company.party.vat_number comprobantes = etree.Element('comprobantes') for invoice in invoices: print "Factura ", invoice factura1 = invoice.generate_xml_invoice() factura = etree.tostring(factura1, encoding='utf8', method='xml') #print etree.tostring(factura1, pretty_print = True, xml_declaration=True, encoding="utf-8") signed_document = s.model.nodux_electronic_invoice_auth.conexiones.apply_digital_signature( factura, file_pk12, password, {}) etree.SubElement(comprobantes, 'comprobante').text = etree.CDATA(signed_document) lote.append(comprobantes) print etree.tostring(lote, pretty_print=True, xml_declaration=True, encoding="utf-8") return lote pool = Pool() xades = Xades() file_pk12 = base64.encodestring(self.company.electronic_signature) password = base64.encodestring(self.company.password_hash) Shipment = Pool().get('stock.shipment.out') shipments = Shipment.browse(Transaction().context['active_ids']) lote = etree.Element('lote') lote.set("version", "1.0.0") etree.SubElement(lote, 'claveAcceso').text = self.generate_access_key_lote() etree.SubElement(lote, 'ruc').text = self.company.party.vat_number comprobantes = etree.Element('comprobantes') for shipment in shipment: guiaRemision = self.generate_xml_shipment() signed_document = xades.apply_digital_signature( guiaRemision, file_pk12, password) print etree.tostring(guiaRemision, pretty_print=True, xml_declaration=True, encoding="utf-8") etree.SubElement(comprobantes, 'comprobante').text = etree.CDATA(signed_document) lote.append(comprobantes) return lote
class Newborn(ModelSQL, ModelView): 'Newborn Information' __name__ = 'gnuhealth.newborn' name = fields.Char('Newborn ID') patient = fields.Many2One('gnuhealth.patient', 'Baby', required=True, help="Patient associated to this newborn") mother = fields.Many2One('gnuhealth.patient', 'Mother') newborn_name = fields.Char('Name at Birth') birth_date = fields.DateTime('DoB', required=True, help="Date and Time of birth") photo = fields.Binary('Picture') newborn_sex = fields.Function( fields.Selection([ ('m', 'Male'), ('f', 'Female'), ], 'Sex'), 'get_newborn_sex') # Sex / Gender at birth. sex = fields.Selection([ ('m', 'Male'), ('f', 'Female'), ], 'Sex',sort=False, required=True, help="Sex at birth. It might differ from the current patient" \ " sex") cephalic_perimeter = fields.Integer( 'CP', help="Cephalic Perimeter in centimeters (cm)") length = fields.Integer('Length', help="Length in centimeters (cm)") weight = fields.Integer('Weight', help="Weight in grams (g)") apgar1 = fields.Integer('APGAR 1st minute') apgar5 = fields.Integer('APGAR 5th minute') apgar_scores = fields.One2Many('gnuhealth.neonatal.apgar', 'name', 'APGAR scores') meconium = fields.Boolean('Meconium') congenital_diseases = fields.One2Many('gnuhealth.patient.disease', 'newborn_id', 'Congenital diseases') reanimation_stimulation = fields.Boolean('Stimulation') reanimation_aspiration = fields.Boolean('Aspiration') reanimation_intubation = fields.Boolean('Intubation') reanimation_mask = fields.Boolean('Mask') reanimation_oxygen = fields.Boolean('Oxygen') test_vdrl = fields.Boolean('VDRL') test_toxo = fields.Boolean('Toxoplasmosis') test_chagas = fields.Boolean('Chagas') test_billirubin = fields.Boolean('Billirubin') test_audition = fields.Boolean('Audition') test_metabolic = fields.Boolean( 'Metabolic ("heel stick screening")', help="Test for Fenilketonuria, Congenital Hypothyroidism, " "Quistic Fibrosis, Galactosemia") neonatal_ortolani = fields.Boolean('Positive Ortolani') neonatal_barlow = fields.Boolean('Positive Barlow') neonatal_hernia = fields.Boolean('Hernia') neonatal_ambiguous_genitalia = fields.Boolean('Ambiguous Genitalia') neonatal_erbs_palsy = fields.Boolean('Erbs Palsy') neonatal_hematoma = fields.Boolean('Hematomas') neonatal_talipes_equinovarus = fields.Boolean('Talipes Equinovarus') neonatal_polydactyly = fields.Boolean('Polydactyly') neonatal_syndactyly = fields.Boolean('Syndactyly') neonatal_moro_reflex = fields.Boolean('Moro Reflex') neonatal_grasp_reflex = fields.Boolean('Grasp Reflex') neonatal_stepping_reflex = fields.Boolean('Stepping Reflex') neonatal_babinski_reflex = fields.Boolean('Babinski Reflex') neonatal_blink_reflex = fields.Boolean('Blink Reflex') neonatal_sucking_reflex = fields.Boolean('Sucking Reflex') neonatal_swimming_reflex = fields.Boolean('Swimming Reflex') neonatal_tonic_neck_reflex = fields.Boolean('Tonic Neck Reflex') neonatal_rooting_reflex = fields.Boolean('Rooting Reflex') neonatal_palmar_crease = fields.Boolean('Transversal Palmar Crease') medication = fields.One2Many('gnuhealth.patient.medication', 'newborn_id', 'Medication') responsible = fields.Many2One('gnuhealth.healthprofessional', 'Doctor in charge', help="Signed by the health professional") dismissed = fields.DateTime('Discharged') bd = fields.Boolean('Stillbirth') died_at_delivery = fields.Boolean('Died at delivery room') died_at_the_hospital = fields.Boolean('Died at the hospital') died_being_transferred = fields.Boolean( 'Died being transferred', help="The baby died being transferred to another health institution") tod = fields.DateTime('Time of Death') cod = fields.Many2One('gnuhealth.pathology', 'Cause of death') notes = fields.Text('Notes') @classmethod def __setup__(cls): super(Newborn, cls).__setup__() cls._sql_constraints = [ ('name_uniq', 'unique(name)', 'The Newborn ID must be unique !'), ] def get_newborn_sex(self, name): if self.patient: return self.patient.sex
class CrmCaseSection(ModelSQL, ModelView): _name = "ekd.crm.case.section" _description = "Case Section" name = fields.Char('Case Section',size=64, required=True, translate=True) code = fields.Char('Section Code',size=8) active = fields.Boolean('Active') allow_unlink = fields.Boolean('Allow Delete', help="Allows to delete non draft cases") sequence = fields.Integer('Sequence') user = fields.Many2One('res.user', 'Responsible User') reply_to = fields.Char('Reply-To', size=64, help="The email address put in the 'Reply-To' of all emails sent by Open ERP about cases in this section") parent = fields.Many2One('ekd.crm.case.section', 'Parent Section') child_ids = fields.One2Many('ekd.crm.case.section', 'parent', 'Child Sections') def __init__(self): super(CrmCaseSection, self).__init__() self._constraints += [ ('_check_recursion', 'Error ! You cannot create recursive sections.') ] self._sql_constraints += [ ('code_uniq', 'unique (code)', 'The code of the section must be unique !') ] self._error_messages.update({ 'recursive_accounts': 'You can not create recursive accounts!', }) self._order.insert(0, ('code', 'ASC')) self._order.insert(1, ('name', 'ASC')) def default_active(self): return True def default_allow_unlink(self): return True def _check_recursion(self, ids): cr = Transaction().cursor level = 100 while len(ids): cr.execute('SELECT DISTINCT parent FROM ekd_crm_case_section '\ 'WHERE id IN %s', (tuple(ids),)) ids = filter(None, map(lambda x:x[0], cr.fetchall())) if not level: return False level -= 1 return True # Mainly used by the wizard def menu_create_data(self, data, menu_lst): menus = {} menus[0] = data['menu_parent'] section = self.browse(data['section']) for (index, mname, mdomain, latest, view_mode) in menu_lst: view_mode = data['menu'+str(index)+'_option'] if view_mode=='no': menus[index] = data['menu_parent'] continue icon = icon_lst.get(view_mode.split(',')[0], 'STOCK_JUSTIFY_FILL') menu_id=self.pool.get('ir.ui.menu').create( { 'name': data['menu'+str(index)], 'parent': menus[latest], 'icon': icon }) menus[index] = menu_id action_id = self.pool.get('ir.actions.act_window').create( { 'name': data['menu'+str(index)], 'res_model': 'ekd.crm.case', 'domain': mdomain.replace('SECTION_ID', str(data['section'])), 'view_type': 'form', 'view_mode': view_mode, }) seq = 0 for mode in view_mode.split(','): self.pool.get('ir.actions.act_window.view').create( { 'sequence': seq, 'view_id': data['view_'+mode], 'view_mode': mode, 'act_window_id': action_id, 'multi': True }) seq+=1 self.pool.get('ir.values').create( { 'name': data['menu'+str(index)], 'key2': 'tree_but_open', 'model': 'ir.ui.menu', 'res_id': menu_id, 'value': 'ir.actions.act_window,%d'%action_id, 'object': True }) return True # # Used when called from .XML file # def menu_create(self, ids, name, menu_parent_id=False): menus = {} menus[-1] = menu_parent_id for section in self.browse( ids): for (index, mname, mdomain, latest) in [ (0,'',"[('section','=',"+str(section.id)+")]", -1), (1,'My ',"[('section','=',"+str(section.id)+"),('user','=',uid)]", 0), (2,'My Unclosed ',"[('section','=',"+str(section.id)+"),('user','=',uid), ('state','<>','cancel'), ('state','<>','done')]", 1), (5,'My Open ',"[('section','=',"+str(section.id)+"),('user','=',uid), ('state','=','open')]", 2), (6,'My Pending ',"[('section','=',"+str(section.id)+"),('user','=',uid), ('state','=','pending')]", 2), (7,'My Draft ',"[('section','=',"+str(section.id)+"),('user','=',uid), ('state','=','draft')]", 2), (3,'My Late ',"[('section','=',"+str(section.id)+"),('user','=',uid), ('date_deadline','<=',time.strftime('%Y-%m-%d')), ('state','<>','cancel'), ('state','<>','done')]", 1), (4,'My Canceled ',"[('section','=',"+str(section.id)+"),('user','=',uid), ('state','=','cancel')]", 1), (8,'All ',"[('section','=',"+str(section.id)+"),]", 0), (9,'Unassigned ',"[('section','=',"+str(section.id)+"),('user','=',False)]", 8), (10,'Late ',"[('section','=',"+str(section.id)+"),('user','=',uid), ('date_deadline','<=',time.strftime('%Y-%m-%d')), ('state','<>','cancel'), ('state','<>','done')]", 8), (11,'Canceled ',"[('section','=',"+str(section.id)+"),('state','=','cancel')]", 8), (12,'Unclosed ',"[('section','=',"+str(section.id)+"),('state','<>','cancel'), ('state','<>','done')]", 8), (13,'Open ',"[('section','=',"+str(section.id)+"),('state','=','open')]", 12), (14,'Pending ',"[('section','=',"+str(section.id)+"),('state','=','pending')]", 12), (15,'Draft ',"[('section','=',"+str(section.id)+"),('state','=','draft')]", 12), (16,'Unassigned ',"[('section','=',"+str(section.id)+"),('user','=',False),('state','<>','cancel'),('state','<>','done')]", 12), ]: view_mode = 'tree,form' icon = 'STOCK_JUSTIFY_FILL' if index==0: view_mode = 'form,tree' icon = 'STOCK_NEW' menu_id=self.pool.get('ir.ui.menu').create( { 'name': mname+name, 'parent_id': menus[latest], 'icon': icon }) menus[index] = menu_id action_id = self.pool.get('ir.actions.act_window').create({ 'name': mname+name+' Cases', 'res_model': 'ekd.crm.case', 'domain': mdomain, 'view_type': 'form', 'view_mode': view_mode, }) self.pool.get('ir.values').create({ 'name': 'Open Cases', 'key2': 'tree_but_open', 'model': 'ir.ui.menu', 'res_id': menu_id, 'value': 'ir.actions.act_window,%d'%action_id, 'object': True }) return True def name_get(self, ids): if not len(ids): return [] reads = self.read( ids, ['name','parent_id']) res = [] for record in reads: name = record['name'] if record['parent_id']: name = record['parent_id'][1]+' / '+name res.append((record['id'], name)) return res