def _update_models(self, cr, models={}): if not models: checklist_ids = self.search(cr, 1, []) models = dict([(checklist['model_id'][0], {'checklist_id': checklist['id'], 'active_field': checklist['active_field']}) for checklist in self.read(cr, 1, checklist_ids, ['model_id', 'active_field'])]) for model in self.pool.get('ir.model').read(cr, 1, models.keys(), ['model']): if self.pool.get(model['model']): model_columns = self.pool.get(model['model'])._columns checklist_id = models[model['id']] and models[model['id']].get('checklist_id', False) if checklist_id: model_columns.update({ 'checklist_task_instance_ids': fields.function(self._get_checklist_task_instances, type='one2many', relation='checklist.task.instance', string='Checklist Task Instances', store=False), 'total_progress_rate': fields.float('Progress Rate', digits=(16, 2)), }) columns_to_add = {'total_progress_rate': 'NUMERIC(16,2)'} if models[model['id']].get('active_field', False): if 'active' not in model_columns or model_columns['active']._type != 'boolean': model_columns.update({'active': fields.boolean('Active')}) model_columns.update({'total_progress_rate_mandatory': fields.float('Mandatory Progress Rate', digits=(16, 2))}) columns_to_add.update({'active': 'BOOLEAN', 'total_progress_rate_mandatory': 'NUMERIC(16,2)'}) for column in columns_to_add: cr.execute("""SELECT c.relname FROM pg_class c, pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid""", (model['model'].replace('.', '_'), column)) if not cr.rowcount: cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (model['model'].replace('.', '_'), column, columns_to_add[column])) cr.commit() else: if 'checklist_task_instance_ids' in model_columns: del model_columns['checklist_task_instance_ids'] if 'total_progress_rate' in model_columns: del model_columns['total_progress_rate'] return self._update_checklists_cache(cr)
def __init__(self, pool, cr): """ Dynamically add columns.""" super(report_prompt_class, self).__init__(pool, cr) for counter in range(0, MAX_PARAMS): field_name = PARAM_XXX_STRING_VALUE % counter self._columns[field_name] = fields.char('String Value', size=64) field_name = PARAM_XXX_BOOLEAN_VALUE % counter self._columns[field_name] = fields.boolean('Boolean Value') field_name = PARAM_XXX_INTEGER_VALUE % counter self._columns[field_name] = fields.integer('Integer Value') field_name = PARAM_XXX_NUMBER_VALUE % counter self._columns[field_name] = fields.float('Number Value') field_name = PARAM_XXX_DATE_VALUE % counter self._columns[field_name] = fields.date('Date Value') field_name = PARAM_XXX_TIME_VALUE % counter self._columns[field_name] = fields.datetime('Time Value') self.paramfile = False
def __init__(self, pool, cr): """ Dynamically add columns.""" super(report_prompt_class, self).__init__(pool, cr) for counter in range(0, MAX_PARAMS): field_name = PARAM_XXX_STRING_VALUE % counter self._columns[field_name] = fields.char('String Value', size=64) field_name = PARAM_XXX_BOOLEAN_VALUE % counter self._columns[field_name] = fields.boolean('Boolean Value') field_name = PARAM_XXX_INTEGER_VALUE % counter self._columns[field_name] = fields.integer('Integer Value') field_name = PARAM_XXX_NUMBER_VALUE % counter self._columns[field_name] = fields.float('Number Value') field_name = PARAM_XXX_DATE_VALUE % counter self._columns[field_name] = fields.date('Date Value') field_name = PARAM_XXX_TIME_VALUE % counter self._columns[field_name] = fields.datetime('Time Value') field_name = PARAM_XXX_2M_VALUE % counter self._columns[field_name] = fields.function(self._multi_select_values.im_func, arg={"entry_num": counter}, fnct_inv=self._multi_select_values_store.im_func, fnct_inv_arg={"entry_num": counter}, method=False, type='many2many', relation='ir.actions.report.multivalues.promptwizard', string='Multi-Select')
"""Get the picking ids of the given Stock Packages.""" result = {} for line in self.pool.get('stock.packages').browse(cr, uid, ids, context=None): result[line.pick_id.id] = True return result.keys() def _get_company_code(self, cr, user, context=None): <<<<<<< HEAD return super(stock_picking, self)._get_company_code(cr, user, context=context) ======= return [] >>>>>>> c1979f64b3360c86d60e00c92be0271d89f97f2d _columns = { 'logis_company': fields.many2one('logistic.company', 'Logistics Company', help='Name of the Logistics company providing the shipper services.'), 'freight': fields.boolean('Shipment', help='Indicates if the shipment is a freight shipment.'), 'sat_delivery': fields.boolean('Saturday Delivery', help='Indicates is it is appropriate to send delivery on Saturday.'), 'package_type': fields.selection([ ('01', 'Letter'), ('02', 'Customer Supplied Package'), ('03', 'Tube'), ('04', 'PAK'), ('21', 'ExpressBox'), ('24', '25KG Box'), ('25', '10KG Box'), ('30', 'Pallet'), ('2a', 'Small Express Box'), ('2b', 'Medium Express Box'), ('2c', 'Large Express Box') ], 'Package Type', help='Indicates the type of package'), 'bill_shipping': fields.selection([
class Product(osv.osv): def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): model_obj = self.pool.get('ir.model.data') if context is None: context = {} if ('product' in context) and (context['product'] == 'membership_product'): model_data_ids_form = model_obj.search( cr, user, [('model', '=', 'ir.ui.view'), ('name', 'in', ['membership_products_form', 'membership_products_tree'])], context=context) resource_id_form = model_obj.read(cr, user, model_data_ids_form, fields=['res_id', 'name'], context=context) dict_model = {} for i in resource_id_form: dict_model[i['name']] = i['res_id'] if view_type == 'form': view_id = dict_model['membership_products_form'] else: view_id = dict_model['membership_products_tree'] return super(Product, self).fields_view_get(cr, user, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu) '''Product''' _inherit = 'product.template' _columns = { 'membership': fields.boolean( 'Membership', help='Check if the product is eligible for membership.'), 'membership_date_from': fields.date('Membership Start Date', help='Date from which membership becomes active.'), 'membership_date_to': fields.date('Membership End Date', help='Date until which membership remains active.'), } _sql_constraints = [ ('membership_date_greater', 'check(membership_date_to >= membership_date_from)', 'Error ! Ending Date cannot be set before Beginning Date.') ] _defaults = { 'membership': False, }
def _save_file(self, path, b64_file): """Save a file encoded in base 64""" self._check_filestore(path) with open(path, 'w') as ofile: ofile.write(base64.b64decode(b64_file)) return True def _set_image(self, cr, uid, id, name, value, arg, context=None): image = self.browse(cr, uid, id, context=context) full_path = self._image_path(cr, uid, image, context=context) if full_path: return self._save_file(full_path, value) return self.write(cr, uid, id, {'file_db_store' : value}, context=context) _columns = { 'name':fields.char('Image Title', size=100, required=True), 'extention': fields.char('file extention', size=6), 'link':fields.boolean('Link?', help="Images can be linked from files on your file system or remote (Preferred)"), 'file_db_store':fields.binary('Image stored in database'), 'file':fields.function(_get_image, fnct_inv=_set_image, type="binary", filters='*.png,*.jpg,*.gif'), 'url':fields.char('File Location', size=250), 'comments':fields.text('Comments'), 'product_id':fields.many2one('product.product', 'Product') } _defaults = { 'link': lambda *a: False, } _sql_constraints = [('uniq_name_product_id', 'UNIQUE(product_id, name)', _('A product can have only one image with the same name'))]
save_cron.update({'interval_type':vals.get('interval_type')}) if save_cron: self.pool.get('currency.rate.update').save_cron( cr, uid, save_cron ) return super(res_company, self).write(cr, uid, ids, vals, context=context) _inherit = "res.company" _columns = { ### activate the currency update 'auto_currency_up': fields.boolean('Automatical update of the currency this company'), 'services_to_use' : fields.one2many( 'currency.rate.update.service', 'company_id', 'Currency update services' ), ###predifine cron frequence 'interval_type': fields.selection( [ ('days','Day(s)'), ('weeks', 'Week(s)'), ('months', 'Month(s)') ], 'Currency update frequence', help="""changing this value will also affect other compagnies"""
- The 'Invoice On Order After Delivery' choice will generate the draft invoice based on sales order after all picking lists have been finished. - The 'Invoice From The Picking' choice is used to create an invoice during the picking process."""), 'state': fields.selection([ ('draft', 'Draft Quotation'), ('sent', 'Quotation Sent'), ('cancel', 'Cancelled'), ('waiting_date', 'Waiting Schedule'), ('progress', 'Sales Order'), ('cc_auth', 'Draft Authorized'), ('manual', 'Sale to Invoice'), ('invoice_except', 'Invoice Exception'), ('shipping_except', 'Shipping Exception'), ('done', 'Done') ], 'Status', readonly=True, track_visibility='onchange', help="Gives the status of the quotation or sales order. \nThe exception status is automatically set when a cancel operation occurs in the processing of a document linked to the sales order. \nThe 'Waiting Schedule' status is set when the invoice is confirmed but waiting for the scheduler to run on the order date.", select=True), 'cc_pre_auth':fields.boolean('Creditcard Pre-authorised'), 'rel_account_voucher_id':fields.many2one('account.voucher', 'Related Payment'), 'invoiced': fields.function(_invoiced, method=True, string='Paid', type='boolean', help="It indicates that an invoice has been paid.", store={ 'account.invoice' : (_get_invoice, ['state'], 20), 'sale.order' : (lambda self, cr, uid, ids, c={}: ids, ['state'], 20), 'account.voucher' : (_get_voucher, ['state'], 20), }), 'cc_ship_refund' : fields.boolean('Ship Refunded', readonly=True), } def copy(self, cr, uid, id, default=None, context=None): if default is None: default = {} if context is None:
'scheduler':fields.many2one('ir.cron', 'Scheduler',help="Scheduler that will execute the cron task"), 'search_filter': fields.char('Search Filter', size=256), 'filename': fields.char('Filename', size=128, help="Filename will be used to generate the output file name or to read the incoming file. It is possible to use variables (check in sequence for syntax)", require=True), 'folder_path': fields.char('Folder Path', size=128, help="folder that containt the incomming or the outgoing file"), 'archive_folder_path': fields.char('Archive Folder Path', size=128, help="if a path is set when a file is imported the file will be automatically moved to this folder"), 'encoding': fields.selection(_get_encoding, 'Encoding', require=True), 'field_ids': fields.one2many('file.fields', 'file_id', 'Fields'), 'action_before_all': fields.text('Action Before All', help="This python code will executed after the import/export"), 'action_after_all': fields.text('Action After All', help="This python code will executed after the import/export"), 'action_before_each': fields.text('Action Before Each', help="This python code will executed after each element of the import/export"), 'action_after_each': fields.text('Action After Each', help="This python code will executed after each element of the import/export"), 'check_if_import': fields.text('Check If Import', help="This python code will be executed before each element of the import"), 'delimiter':fields.char('Fields delimiter', size=64, help="Delimiter used in the CSV file"), 'lang': fields.many2one('res.lang', 'Language'), 'import_default_fields':fields.one2many('file.default.import.values', 'file_id', 'Default Field'), 'do_not_update':fields.boolean('Do Not Update'), 'pre_processing': fields.text('Pre-Processing', help="This python code will be executed before merge of elements of the import"), 'mapping_template_id':fields.many2one('external.mapping.template', 'External Mapping Template', require="True"), 'notes': fields.text('Notes'), 'related_mapping_ids': fields.function(_get_related_mapping_ids, type="many2many", relation="external.mapping", string='Related Mappings'), 'synchronize_from': fields.selection([('referential', 'Referential'), ('pop_up', 'Pop Up')], string='Synchronize From'), 'linked_task': fields.many2one('file.exchange', 'Linked Task'), } def get_absolute_id(self, cr, uid, id, context=None): if isinstance(id,list): id = id[0] file_exchange = self.browse(cr, uid, id, context=context) file_exchange_id = file_exchange.get_external_id(context=context)[file_exchange.id] if not file_exchange_id: file_exchange_id = file_exchange.name.replace(' ','_').replace('.','_')
class social_programs_program_delivery(osv.osv, format_address): _description = "Entrega Programas sociales" _name = 'social.programs.program.delivery' def action_delivery_product(self, cr, uid, ids, context=None): """ Muestra una ventana con la informacion del documento a entregar """ partner_id = self.browse(cr, uid, ids[0], context=context).partner_id.id mod_obj = self.pool.get('ir.model.data') res = mod_obj.get_object_reference( cr, uid, 'social_programs', 'view_social_programs_program_delivery_form') res_id = res and res[1] or False #~ Redirecciona al formulario de Entrega return { 'name': "Entrega", 'view_type': 'form', 'view_mode': 'form', 'view_id': [res_id], 'res_model': 'social.programs.program.delivery', # object name 'type': 'ir.actions.act_window', 'nodestroy': True, 'target': 'new', 'res_id': ids[0], # id of the object to which to redirected } def action_delivery(self, cr, uid, ids, context=None): """ Pone el documento como entregado """ date = fields.date.context_today(self, cr, uid, context=context) self.write(cr, uid, ids, { 'delivery': True, 'date': date }, context=context) print "*************** cambio de estado **************** " #~ Cierra la ventana return {'type': 'ir.actions.act_window_close'} def action_delivery_and_print(self, cr, uid, ids, context=None): """ Entrega el documento e imprime el reporte """ self.action_delivery(cr, uid, ids, context=context) res = self.action_print_report(cr, uid, ids, context=context) return res def action_print_report(self, cr, uid, ids, context=None): datas = {} if context is None: context = {} print "************** active_ids ************* ", context.get( 'active_ids', []) data = self.read(cr, uid, ids)[0] datas = { 'ids': ids, 'model': 'social.programs.program.delivery', 'form': data } return { 'type': 'ir.actions.report.xml', 'report_name': 'report.social.programs.program.delivery.webkit', 'datas': datas, } def onchange_delivery(self, cr, uid, ids, delivery, context=None): """ Cierra la ventana si cambia el estado a entregado """ if delivery == True: return {'type': 'ir.actions.act_window_close'} return True _columns = { 'program_id': fields.many2one('social.programs.program', 'Programa', readonly=True), 'partner_id': fields.many2one('res.partner', 'Beneficiario', readonly=True), 'product_id': fields.many2one('product.product', 'Producto', readonly=True), 'qty': fields.float('Cantidad a entregar', readonly=True), 'date': fields.date('Entregado', readonly=True), 'delivery': fields.boolean('Entregado', readonly=True), # Campos relacionados con el beneficiario 'name': fields.related('partner_id', 'name', type="char", string="Nombre", readonly=True), 'curp': fields.related('partner_id', 'curp', type="char", string="curp", readonly=True), 'image': fields.related('partner_id', 'image', type="binary", string="Image", readonly=True), 'image_medium': fields.related('partner_id', 'image_medium', type="binary", string="Image medium", readonly=True), 'category_id': fields.many2many("res.partner.category", 'partner_id', 'category_id', string="Etiquetas", readonly=True), 'street': fields.related('partner_id', 'street', type="char", string="Calle", readonly=True), 'settlement_id': fields.related('partner_id', 'settlement_id', type='many2one', relation='social.programs.res.settlement', string='Colonia', readonly=True), 'city_id': fields.related('partner_id', 'city_id', type='many2one', relation='social.programs.res.city', string='Ciudad', readonly=True), 'state_id': fields.related('partner_id', 'state_id', type='many2one', relation='res.country.state', string='Estado', readonly=True), 'zip': fields.related('partner_id', 'zip', type="char", string="C.P.", readonly=True), 'sector_id': fields.related('partner_id', 'sector_id', type='many2one', relation='social.programs.res.sector', string='Sector', readonly=True), 'area_id': fields.related('partner_id', 'area_id', type='many2one', relation='social.programs.res.area', string='Area', readonly=True), 'country_id': fields.related('partner_id', 'country_id', type='many2one', relation='res.country', string='Estado', readonly=True), 'phone': fields.related('partner_id', 'phone', type="char", string="Telefono", readonly=True), 'mobile': fields.related('partner_id', 'mobile', type="char", string="Celular", readonly=True), 'email': fields.related('partner_id', 'email', type="char", string="Correo", readonly=True), } _defaults = {'delivery': False} _order = 'delivery,program_id,partner_id,product_id,qty'
class AccountStatementProfil(Model): _inherit = "account.statement.profile" def get_import_type_selection(self, cr, uid, context=None): """This is the method to be inherited for adding the parser""" return [('generic_csvxls_so', 'Generic .csv/.xls based on SO Name')] def _get_import_type_selection(self, cr, uid, context=None): return self.get_import_type_selection(cr, uid, context=context) _columns = { 'launch_import_completion': fields.boolean( "Launch completion after import", help="Tic that box to automatically launch the completion " "on each imported file using this profile."), 'last_import_date': fields.datetime("Last Import Date"), # we remove deprecated as it floods logs in standard/warning level sob... 'rec_log': fields.text('log', readonly=True), # Deprecated 'import_type': fields.selection( _get_import_type_selection, 'Type of import', required=True, help="Choose here the method by which you want to import bank" "statement for this profile."), } _defaults = { 'import_type': 'generic_csvxls_so' } def _write_extra_statement_lines( self, cr, uid, parser, result_row_list, profile, statement_id, context): """Insert extra lines after the main statement lines. After the main statement lines have been created, you can override this method to create extra statement lines. :param: browse_record of the current parser :param: result_row_list: [{'key':value}] :param: profile: browserecord of account.statement.profile :param: statement_id: int/long of the current importing statement ID :param: context: global context """ pass def write_logs_after_import(self, cr, uid, ids, statement_id, num_lines, context): """ Write the log in the logger :param int/long statement_id: ID of the concerned account.bank.statement :param int/long num_lines: Number of line that have been parsed :return: True """ self.message_post(cr, uid, ids, body=_('Statement ID %s have been imported with %s lines.') % (statement_id, num_lines), context=context) return True #Deprecated remove on V8 def prepare_statetement_lines_vals(self, *args, **kwargs): return self.prepare_statement_lines_vals(*args, **kwargs) def prepare_statement_lines_vals( self, cr, uid, parser_vals, statement_id, context): """ Hook to build the values of a line from the parser returned values. At least it fullfill the statement_id. Overide it to add your own completion if needed. :param dict of vals from parser for account.bank.statement.line (called by parser.get_st_line_vals) :param int/long statement_id: ID of the concerned account.bank.statement :return: dict of vals that will be passed to create method of statement line. """ statement_line_obj = self.pool['account.bank.statement.line'] values = parser_vals values['statement_id'] = statement_id date = values.get('date') period_memoizer = context.get('period_memoizer') if not period_memoizer: period_memoizer = {} context['period_memoizer'] = period_memoizer if period_memoizer.get(date): values['period_id'] = period_memoizer[date] else: # This is awfully slow... periods = self.pool.get('account.period').find(cr, uid, dt=values.get('date'), context=context) values['period_id'] = periods[0] period_memoizer[date] = periods[0] values = statement_line_obj._add_missing_default_values(cr, uid, values, context) return values def prepare_statement_vals(self, cr, uid, profile_id, result_row_list, parser, context): """ Hook to build the values of the statement from the parser and the profile. """ vals = {'profile_id': profile_id} vals.update(parser.get_st_vals()) return vals def multi_statement_import(self, cr, uid, ids, profile_id, file_stream, ftype="csv", context=None): """ Create multiple bank statements from values given by the parser for the givenprofile. :param int/long profile_id: ID of the profile used to import the file :param filebuffer file_stream: binary of the providen file :param char: ftype represent the file exstension (csv by default) :return: list: list of ids of the created account.bank.statemênt """ prof_obj = self.pool['account.statement.profile'] if not profile_id: raise osv.except_osv(_("No Profile!"), _("You must provide a valid profile to import a bank statement!")) prof = prof_obj.browse(cr, uid, profile_id, context=context) parser = new_bank_statement_parser(prof.import_type, ftype=ftype) res = [] for result_row_list in parser.parse(file_stream): statement_id = self._statement_import(cr, uid, ids, prof, parser, file_stream, ftype=ftype, context=context) res.append(statement_id) return res def _statement_import(self, cr, uid, ids, prof, parser, file_stream, ftype="csv", context=None): """ Create a bank statement with the given profile and parser. It will fullfill the bank statement with the values of the file providen, but will not complete data (like finding the partner, or the right account). This will be done in a second step with the completion rules. :param prof : The profile used to import the file :param parser: the parser :param filebuffer file_stream: binary of the providen file :param char: ftype represent the file exstension (csv by default) :return: ID of the created account.bank.statemênt """ statement_obj = self.pool.get('account.bank.statement') statement_line_obj = self.pool.get('account.bank.statement.line') attachment_obj = self.pool.get('ir.attachment') result_row_list = parser.result_row_list # Check all key are present in account.bank.statement.line!! if not result_row_list: raise osv.except_osv(_("Nothing to import"), _("The file is empty")) parsed_cols = parser.get_st_line_vals(result_row_list[0]).keys() for col in parsed_cols: if col not in statement_line_obj._columns: raise osv.except_osv(_("Missing column!"), _("Column %s you try to import is not " "present in the bank statement line!") % col) statement_vals = self.prepare_statement_vals(cr, uid, prof.id, result_row_list, parser, context) statement_id = statement_obj.create(cr, uid, statement_vals, context=context) try: # Record every line in the bank statement statement_store = [] for line in result_row_list: parser_vals = parser.get_st_line_vals(line) values = self.prepare_statement_lines_vals( cr, uid, parser_vals, statement_id, context) statement_store.append(values) # Hack to bypass ORM poor perfomance. Sob... statement_line_obj._insert_lines(cr, uid, statement_store, context=context) self._write_extra_statement_lines( cr, uid, parser, result_row_list, prof, statement_id, context) # Trigger store field computation if someone has better idea start_bal = statement_obj.read( cr, uid, statement_id, ['balance_start'], context=context) start_bal = start_bal['balance_start'] statement_obj.write(cr, uid, [statement_id], {'balance_start': start_bal}) attachment_data = { 'name': 'statement file', 'datas': file_stream, 'datas_fname': "%s.%s" % (datetime.datetime.now().date(), ftype), 'res_model': 'account.bank.statement', 'res_id': statement_id, } attachment_obj.create(cr, uid, attachment_data, context=context) # If user ask to launch completion at end of import, do it! if prof.launch_import_completion: statement_obj.button_auto_completion(cr, uid, [statement_id], context) # Write the needed log infos on profile self.write_logs_after_import(cr, uid, prof.id, statement_id, len(result_row_list), context) except Exception: error_type, error_value, trbk = sys.exc_info() st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value) st += ''.join(traceback.format_tb(trbk, 30)) #TODO we should catch correctly the exception with a python #Exception and only re-catch some special exception. #For now we avoid re-catching error in debug mode if config['debug_mode']: raise raise osv.except_osv(_("Statement import error"), _("The statement cannot be created: %s") % st) return statement_id
class social_programs_category(osv.osv): def name_get(self, cr, uid, ids, context=None): """Return the categories' display name, including their direct parent by default. :param dict context: the ``partner_category_display`` key can be used to select the short version of the category name (without the direct parent), when set to ``'short'``. The default is the long version.""" if context is None: context = {} if context.get('partner_category_display') == 'short': return super(res_partner_category, self).name_get(cr, uid, ids, context=context) if isinstance(ids, (int, long)): ids = [ids] reads = self.read(cr, uid, ids, ['name', 'parent_id'], context=context) 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 def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100): if not args: args = [] if not context: context = {} if name: # Be sure name_search is symetric to name_get name = name.split(' / ')[-1] ids = self.search(cr, uid, [('name', operator, name)] + args, limit=limit, context=context) else: ids = self.search(cr, uid, args, limit=limit, context=context) return self.name_get(cr, uid, ids, context) def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None): res = self.name_get(cr, uid, ids, context=context) return dict(res) _description = 'Programas Categorias' _name = 'social.programs.category' _columns = { 'name': fields.char('Nombre de Categoria', required=True, size=64, translate=True), 'parent_id': fields.many2one('social.programs.category', 'Categoria Padre', select=True, ondelete='cascade'), 'complete_name': fields.function(_name_get_fnc, type="char", string='Full Name'), 'child_ids': fields.one2many('social.programs.category', 'parent_id', 'Child Categories'), 'active': fields.boolean( 'Active', help= "El campo activo permite ocultar la categoria sin tener que eliminarla." ), 'parent_left': fields.integer('Left parent', select=True), 'parent_right': fields.integer('Right parent', select=True), 'program_ids': fields.many2many('social.programs.program', id1='category_id', id2='program_id', string='Programas'), } _constraints = [ (osv.osv._check_recursion, 'Error ! You can not create recursive categories.', ['parent_id']) ] _defaults = { 'active': 1, } _parent_store = True _parent_order = 'name' _order = 'parent_left'
class social_programs_direction(osv.osv): _description = "Direcciones de programas sociales" _name = 'social.programs.direction' def onchange_state(self, cr, uid, ids, state_id, context=None): """ Obtiene la informacion del pais en base a el estado seleccioando """ if state_id: country_id = self.pool.get('res.country.state').browse( cr, uid, state_id, context).country_id.id return {'value': {'country_id': country_id}} return {} def onchange_settlement(self, cr, uid, ids, settlement_id, context=None): """ Obtiene la informacion de ciudad, estado, pais, CP, etc... en base a la colonia seleccionada """ if settlement_id: settlement = self.pool.get( 'social.programs.res.settlement').browse( cr, uid, settlement_id, context) if settlement.id: state = self.pool.get('res.country.state').browse( cr, uid, settlement.city_id.state_id.id, context) return { 'value': { 'country_id': state.country_id.id, 'state_id': state.id, 'city_id': settlement.city_id.id, 'city': settlement.city_id.name, 'area_id': settlement.area_id.id, 'sector_id': settlement.sector_id.id, 'zip': settlement.zip } } return {} _columns = { 'name': fields.char('Nombre', size=128, required=True, select=True), # Datos de ubicacion 'state_id': fields.many2one('res.country.state', 'Estado'), 'country_id': fields.related('state_id', 'country_id', type="many2one", relation="res.country", string="Country", store=True), 'city_id': fields.many2one("social.programs.res.city", 'Ciudad'), 'settlement_id': fields.many2one('social.programs.res.settlement', 'Colonia'), 'area_id': fields.related('settlement_id', 'area_id', type="many2one", relation="social.programs.res.area", string="Area", readonly=True), 'sector_id': fields.related('settlement_id', 'sector_id', type="many2one", relation="social.programs.res.sector", string="Sector", readonly=True), 'street': fields.char('Calle', size=128), 'zip': fields.char('C.P.', change_default=True, size=24), 'email': fields.char('Email', size=240), 'phone': fields.char('Phone', size=64), 'fax': fields.char('Fax', size=64), 'mobile': fields.char('Mobile', size=64), # Datos complemento 'user_id': fields.many2one('res.users', 'Director', help='Encargado de la direccion.'), 'comment': fields.text('Notas'), 'active': fields.boolean('Active'), } _order = 'name'
class address_address(orm.Model): _name = 'address.address' _inherit = ['mozaik.abstract.model'] _description = 'Address' # private methods def _get_technical_name(self, cr, uid, values, context=None): """ This method produces a technical name with the content of values. :type values: dictionary :param values: used to create a technical address name ``country_id`` ``address_local_zip`` ``zip_man`` ``town_man`` ``address_local_street_id`` ``street_man`` ``number`` ``box`` :rparam: formated values of ``values`` join wit a `#`. 0 if value is null """ technical_value = [] for field in values.keys(): value = values[field] or u'0' technical_value.append(format_value(value)) return '#'.join(technical_value) def _get_linked_coordinates(self, cr, uid, ids, context=None): return self.pool['postal.coordinate'].search( cr, uid, [('address_id', 'in', ids)], context=context) def _get_integral_address(self, cr, uid, ids, name, args, context=None): result = { i: {key: False for key in ['name', 'technical_name']} for i in ids } adrs_recs = self.browse(cr, uid, ids, context=context) for adrs in adrs_recs: elts = [ adrs.street or False, adrs.sequence and '[%s]' % adrs.sequence or False, adrs.street and '-' or adrs.sequence and '-' or False, (adrs.country_code == 'BE') and adrs.zip or False, adrs.city or False, (adrs.country_code != 'BE') and '-' or False, (adrs.country_code != 'BE') and adrs.country_id.name or False, ] adr = ' '.join([el for el in elts if el]) values = KEY_FIELDS.copy() for field in KEY_FIELDS.keys(): to_evaluate = field if not KEY_FIELDS[field] else '%s.%s' % ( field, KEY_FIELDS[field]) real_value = eval('adrs.%s' % to_evaluate) values[field] = real_value technical_name = self._get_technical_name(cr, uid, values, context=context) result[adrs.id] = { 'name': adr or False, 'technical_name': technical_name or False, } return result def _get_street(self, cr, uid, ids, name, args, context=None): result = {i: False for i in ids} adrs_recs = self.browse(cr, uid, ids, context=context) for adrs in adrs_recs: number = adrs.number or '-' number = adrs.box and '%s/%s' % (number, adrs.box) or \ adrs.number or False if adrs.address_local_street_id: street = adrs.select_alternative_address_local_street and \ adrs.address_local_street_id.local_street_alternative or \ adrs.address_local_street_id.local_street else: street = adrs.street_man or False result[adrs.id] = ' '.join([el for el in [street, number] if el]) return result def _get_zip(self, cr, uid, ids, name, args, context=None): result = {i: {key: False for key in [ 'zip', 'city', ]} for i in ids} adrs_recs = self.browse(cr, uid, ids, context=context) for adrs in adrs_recs: result[adrs.id] = { 'zip': adrs.address_local_zip_id and adrs.address_local_zip_id.local_zip or adrs.zip_man or False, 'city': adrs.address_local_zip_id and adrs.address_local_zip_id.town or adrs.town_man or False, } return result _address_store_triggers = { # this MUST be executed in last for consistency: sequence is greater # than other 'address.address': (lambda self, cr, uid, ids, context=None: ids, TRIGGER_FIELDS, 20), 'address.local.zip': (lambda self, cr, uid, ids, context=None: self.pool[ 'address.local.zip']._get_linked_addresses( cr, uid, ids, context=context).ids, ['local_zip', 'town'], 15), 'address.local.street': (lambda self, cr, uid, ids, context=None: self.pool[ 'address.local.street']._get_linked_addresses( cr, uid, ids, context=context), ['local_street', 'local_street_alternative'], 15), 'res.country': (lambda self, cr, uid, ids, context=None: self.pool['res.country']. _get_linked_addresses(cr, uid, ids, context=context), ['name'], 15), } _zip_store_triggers = { 'address.address': (lambda self, cr, uid, ids, context=None: ids, ['address_local_zip_id', 'zip_man', 'town_man'], 10), 'address.local.zip': (lambda self, cr, uid, ids, context=None: self.pool[ 'address.local.zip']._get_linked_addresses( cr, uid, ids, context=context).ids, ['local_zip', 'town'], 10), } _street_store_triggers = { 'address.address': (lambda self, cr, uid, ids, context=None: ids, [ 'address_local_street_id', 'select_alternative_address_local_street', 'street_man', 'number', 'box' ], 10), 'address.local.street': (lambda self, cr, uid, ids, context=None: self.pool[ 'address.local.street']._get_linked_addresses( cr, uid, ids, context=context), ['local_street', 'local_street_alternative'], 10), } _columns = { 'id': fields.integer('ID', readonly=True), 'name': fields.function(_get_integral_address, string='Address', type='char', select=True, multi='display_and_technical', store=_address_store_triggers), 'technical_name': fields.function(_get_integral_address, string='Technical Name', type='char', select=True, multi='display_and_technical', store=_address_store_triggers), 'country_id': fields.many2one('res.country', 'Country', required=True, select=True, track_visibility='onchange'), 'country_code': fields.related('country_id', 'code', string='Country Code', type='char'), 'zip': fields.function(_get_zip, string='Zip', type='char', multi='ZipAndCity', store=_zip_store_triggers), 'address_local_zip_id': fields.many2one('address.local.zip', string='City', track_visibility='onchange'), 'zip_man': fields.char(string='Zip', track_visibility='onchange'), 'city': fields.function(_get_zip, string='City', type='char', multi='ZipAndCity', store=_zip_store_triggers), 'town_man': fields.char(string='Town', track_visibility='onchange'), 'street': fields.function(_get_street, string='Street', type='char', store=_street_store_triggers), 'address_local_street_id': fields.many2one('address.local.street', string='Reference Street', track_visibility='onchange'), 'select_alternative_address_local_street': fields.boolean('Use Alternative Reference Street', track_visibility='onchange'), 'street_man': fields.char(string='Street', track_visibility='onchange'), 'street2': fields.char(string='Street2', track_visibility='onchange'), 'number': fields.char(string='Number', track_visibility='onchange'), 'box': fields.char(string='Box', track_visibility='onchange'), 'sequence': fields.integer('Sequence', track_visibility='onchange', group_operator='min'), 'postal_coordinate_ids': fields.one2many('postal.coordinate', 'address_id', string='Postal Coordinates', domain=[('active', '=', True)], context={'force_recompute': True}), 'postal_coordinate_inactive_ids': fields.one2many('postal.coordinate', 'address_id', string='Postal Coordinates', domain=[('active', '=', False)]), } _defaults = { 'country_id': lambda self, cr, uid, c: self.pool.get('res.country'). _country_default_get(cr, uid, COUNTRY_CODE, context=c), 'country_code': COUNTRY_CODE, 'sequence': 0, } _order = 'country_id, zip, name' # constraints _unicity_keys = 'technical_name, sequence' # orm methods def copy_data(self, cr, uid, ids, default=None, context=None): """ Increase sequence value when duplicating address """ adr_id = isinstance(ids, (long, int)) and [ids] or ids technical_name = self.read(cr, uid, adr_id[0], ['technical_name'], context=context)['technical_name'] cr.execute( 'SELECT MAX(sequence) FROM %s WHERE technical_name=%%s' % (self._table, ), (technical_name, )) sequence = cr.fetchone() sequence = sequence and sequence[0] or False if not sequence: raise orm.except_orm( _('Error'), _('An Address without sequence number cannot be duplicated!')) default = dict(default or {}) default.update({ 'sequence': sequence + 1, 'postal_coordinate_ids': [], 'postal_coordinate_inactive_ids': [], }) res = super(address_address, self).copy_data(cr, uid, ids, default=default, context=context) return res # view methods: onchange, button def onchange_country_id(self, cr, uid, ids, country_id, context=None): return { 'value': { 'country_code': self.pool.get('res.country').read( cr, uid, [country_id], ['code'], context=context)[0]['code'] if country_id else False, 'address_local_zip_id': False, } } def onchange_local_zip_id(self, cr, uid, ids, local_zip_id, context=None): _zip, city = False, False if local_zip_id: zip_city = self.pool.get('address.local.zip').read( cr, uid, [local_zip_id], [], context=context)[0] _zip, city = zip_city['local_zip'], zip_city['town'] return { 'value': { 'zip': _zip, 'city': city, 'zip_man': False, 'town_man': False, } } def onchange_zip(self, cr, uid, ids, _zip, context=None): return { 'value': { 'address_local_street_id': False, } } def onchange_local_street_id(self, cr, uid, ids, local_street_id, context=None): vals = {} if local_street_id else { 'select_alternative_address_local_street': False } vals.update({'street_man': False}) return {'value': vals} # public methods def get_linked_partners(self, cr, uid, ids, context=None): """ Return all partners ids linked to addresses ids :param: ids :type: list of addresses ids :rparam: partner_ids :rtype: list of ids """ coord_ids = self._get_linked_coordinates(cr, uid, ids, context=context) return self.pool['postal.coordinate'].get_linked_partners( cr, uid, coord_ids, context=context)
class kg_mail_queue(osv.osv): _name = "kg.mail.queue" _description = "Mail Queue" _order = "crt_date desc" _columns = { 'source': fields.char('Source'), 'created_date':fields.date('Date'), 'state': fields.selection([('pending','Pending'),('sent','Sent')],'Status', readonly=True), 'entry_mode': fields.selection([('auto','Auto'),('manual','Manual')],'Entry Mode', readonly=True), 'company_id': fields.many2one('res.company', 'Company Name',readonly=True), 'active': fields.boolean('Active'), 'crt_date': fields.datetime('Creation Date',readonly=True), 'user_id': fields.many2one('res.users', 'Created By', readonly=True), 'mail_from': fields.char('From'), 'mail_to': fields.char('To'), 'mail_cc': fields.char('Cc'), 'mail_bcc': fields.char('Bcc'), 'attachment': fields.binary('Attachments'), 'transaction_id': fields.integer('Transaction ID'), 'subject': fields.char('Subject'), 'body': fields.html('Body'), 'sent_time': fields.datetime('Sent Time'), 'content_type': fields.selection([('mail','Mail'),('sms','SMS')],'Content Type'), 'body_1': fields.char('Body 1'), } _defaults = { 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'kg.mail.queue', context=c), 'active': True, 'state': 'pending', 'user_id': lambda obj, cr, uid, context: uid, 'crt_date': lambda * a: time.strftime('%Y-%m-%d %H:%M:%S'), 'created_date': lambda * a: time.strftime('%Y-%m-%d'), 'entry_mode': 'auto', 'content_type': 'mail', 'mail_from': '*****@*****.**', } ### Basic Needs def entry_cancel(self,cr,uid,ids,context=None): rec = self.browse(cr,uid,ids[0]) if rec.cancel_remark: self.write(cr, uid, ids, {'state': 'cancel','cancel_user_id': uid, 'cancel_date': time.strftime('%Y-%m-%d %H:%M:%S')}) else: raise osv.except_osv(_('Cancel remark is must !!'), _('Enter the remarks in Cancel remarks field !!')) return True def entry_confirm(self,cr,uid,ids,context=None): self.write(cr, uid, ids, {'state': 'confirmed','confirm_user_id': uid, 'confirm_date': time.strftime('%Y-%m-%d %H:%M:%S')}) return True def entry_draft(self,cr,uid,ids,context=None): self.write(cr, uid, ids, {'state': 'draft'}) return True def entry_approve(self,cr,uid,ids,context=None): self.write(cr, uid, ids, {'state': 'approved','ap_rej_user_id': uid, 'ap_rej_date': time.strftime('%Y-%m-%d %H:%M:%S')}) return True def entry_reject(self,cr,uid,ids,context=None): rec = self.browse(cr,uid,ids[0]) if rec.remark: self.write(cr, uid, ids, {'state': 'reject','ap_rej_user_id': uid, 'ap_rej_date': time.strftime('%Y-%m-%d %H:%M:%S')}) else: raise osv.except_osv(_('Rejection remark is must !!'), _('Enter the remarks in rejection remark field !!')) return True def unlink(self,cr,uid,ids,context=None): unlink_ids = [] for rec in self.browse(cr,uid,ids): if rec.state not in ('draft','cancel'): raise osv.except_osv(_('Warning!'), _('You can not delete this entry !!')) else: unlink_ids.append(rec.id) return osv.osv.unlink(self, cr, uid, unlink_ids, context=context) def write(self, cr, uid, ids, vals, context=None): vals.update({'update_date': time.strftime('%Y-%m-%d %H:%M:%S'),'update_user_id':uid}) return super(kg_mail_queue, self).write(cr, uid, ids, vals, context) def send_mail(self,cr,uid,ids=0,context = None): today = date.today() que_search = self.search(cr,uid,[('created_date','=',today),('state','=','pending')]) if que_search: for que_rec in self.browse(cr, uid, que_search, context=context): if que_rec.state == 'pending': email_from = [que_rec.mail_from] if que_rec.mail_to: email_to = [que_rec.mail_to] else: email_to = [''] if que_rec.mail_cc: email_cc = [que_rec.mail_cc] else: email_cc = [''] if que_rec.mail_bcc: email_bcc = [que_rec.mail_bcc] else: email_bcc = [''] ir_mail_server = self.pool.get('ir.mail_server') msg = ir_mail_server.build_email( email_from = email_from[0], email_to = email_to, subject = que_rec.subject, body = que_rec.body_1, email_cc = email_cc, email_bcc = email_bcc, object_id = ids and ('%s-%s' % (ids, 'kg.mail.settings')), subtype = 'html', subtype_alternative = 'plain') res = ir_mail_server.send_email(cr, uid, msg,mail_server_id=1, context=context) self.write(cr,uid,que_rec.id,{'state':'sent','sent_time':time.strftime('%Y-%m-%d %H:%M:%S')}) else: pass else: pass return True _constraints = [ ]
class view(osv.osv): _inherit = "ir.ui.view" _columns = { 'inherit_option_id': fields.many2one('ir.ui.view', 'Optional Inheritancy'), 'inherited_option_ids': fields.one2many('ir.ui.view', 'inherit_option_id', 'Optional Inheritancies'), 'page': fields.boolean("Whether this view is a web page template (complete)"), 'website_meta_title': fields.char("Website meta title", size=70, translate=True), 'website_meta_description': fields.text("Website meta description", size=160, translate=True), 'website_meta_keywords': fields.char("Website meta keywords", translate=True), } _defaults = { 'page': False, } # Returns all views (called and inherited) related to a view # Used by translation mechanism, SEO and optional templates def _views_get(self, cr, uid, view, options=True, context=None, root=True, stack_result=None): if not context: context = {} if not stack_result: stack_result = [] def view_obj(view): if isinstance(view, basestring): mod_obj = self.pool.get("ir.model.data") m, n = view.split('.') view = mod_obj.get_object(cr, uid, m, n, context=context) elif isinstance(view, (int, long)): view = self.pool.get("ir.ui.view").browse(cr, uid, view, context=context) return view try: view = view_obj(view) except ValueError: # Shall we log that ? return [] while root and view.inherit_id: view = view.inherit_id result = [view] node = etree.fromstring(view.arch) for child in node.xpath("//t[@t-call]"): try: call_view = view_obj(child.get('t-call')) except ValueError: continue if call_view not in result: result += self._views_get(cr, uid, call_view, options=options, context=context, stack_result=result) todo = view.inherit_children_ids if options: todo += filter(lambda x: not x.inherit_id, view.inherited_option_ids) # Keep options in a determinitic order whatever their enabled disabled status todo.sort(lambda x, y: cmp(x.id, y.id)) for child_view in todo: for r in self._views_get(cr, uid, child_view, options=bool(child_view.inherit_id), context=context, root=False, stack_result=result): if r not in result: result.append(r) return result def extract_embedded_fields(self, cr, uid, arch, context=None): return arch.xpath('//*[@data-oe-model != "ir.ui.view"]') def save_embedded_field(self, cr, uid, el, context=None): Model = self.pool[el.get('data-oe-model')] field = el.get('data-oe-field') column = Model._all_columns[field].column converter = self.pool['website.qweb'].get_converter_for( el.get('data-oe-type')) value = converter.from_html(cr, uid, Model, column, el) if value is not None: # TODO: batch writes? Model.write(cr, uid, [int(el.get('data-oe-id'))], {field: value}, context=context) def to_field_ref(self, cr, uid, el, context=None): # filter out meta-information inserted in the document attributes = dict( (k, v) for k, v in el.items() if not k.startswith('data-oe-')) attributes['t-field'] = el.get('data-oe-expression') out = html.html_parser.makeelement(el.tag, attrib=attributes) out.tail = el.tail return out def replace_arch_section(self, cr, uid, view_id, section_xpath, replacement, context=None): # the root of the arch section shouldn't actually be replaced as it's # not really editable itself, only the content truly is editable. [view] = self.browse(cr, uid, [view_id], context=context) arch = etree.fromstring(view.arch.encode('utf-8')) # => get the replacement root if not section_xpath: root = arch else: # ensure there's only one match [root] = arch.xpath(section_xpath) root.text = replacement.text root.tail = replacement.tail # replace all children del root[:] for child in replacement: root.append(copy.deepcopy(child)) return arch def save(self, cr, uid, res_id, value, xpath=None, context=None): """ Update a view section. The view section may embed fields to write :param str model: :param int res_id: :param str xpath: valid xpath to the tag to replace """ res_id = int(res_id) arch_section = html.fromstring( value, parser=html.HTMLParser(encoding='utf-8')) if xpath is None: # value is an embedded field on its own, not a view section self.save_embedded_field(cr, uid, arch_section, context=context) return for el in self.extract_embedded_fields(cr, uid, arch_section, context=context): self.save_embedded_field(cr, uid, el, context=context) # transform embedded field back to t-field el.getparent().replace( el, self.to_field_ref(cr, uid, el, context=context)) arch = self.replace_arch_section(cr, uid, res_id, xpath, arch_section, context=context) self.write( cr, uid, res_id, {'arch': etree.tostring(arch, encoding='utf-8').decode('utf-8')}, context=context)
class ids_hr_employee_separation(osv.osv): def separation_submit(self, cr, uid, ids, context=None): """Initiate workflow- submit the form. """ self._check_resignations(cr, uid, ids, context=context) users= self.pool.get('res.users') #code to update employee working status self._update_employee_working_status(cr, uid, ids, 'on_resign', context=None) resign=self.browse(cr, uid, ids, context=None) if users.has_group(cr, uid, 'ids_emp.group_location_hr'): print "------------------------------location--outer------------------------" if resign.employee_id.parent_id.parent_id.id==7626: print "------------------------------res--------------------------" url="http://ids-erp.idsil.loc:8069/web" values = { 'subject': 'Resignation' + '-' + resign.employee_id.name, 'body_html': 'Resignation of' +' '+resign.employee_id.name+' '+'is Intiated.Please take necessary action.<br/><br/><br/>Kindly do not reply.<br/>---This is auto generated email---<br/>Regards:<br/>ERP HR Team<br/>IDS Infotech LTD.<br/>Url:'+url, 'email_to': resign.employee_id.parent_id.work_email, 'email_from': '*****@*****.**', } else: print "------------------------------location--------------------------" url="http://ids-erp.idsil.loc:8069/web" values = { 'subject': 'Resignation' + '-' + resign.employee_id.name, 'body_html': 'Resignation of' +' '+resign.employee_id.name+' '+'is Intiated.Please take necessary action.<br/><br/><br/>Kindly do not reply.<br/>---This is auto generated email---<br/>Regards:<br/>ERP HR Team<br/>IDS Infotech LTD.<br/>Url:'+url, 'email_to': resign.employee_id.division.hod_email, 'email_cc': resign.employee_id.parent_id.work_email, 'email_from': '*****@*****.**', } #--------------------------------------------------------------- mail_obj = self.pool.get('mail.mail') msg_id = mail_obj.create(cr, uid, values, context=context) if msg_id: mail_obj.send(cr, uid, [msg_id], context=context) elif users.has_group(cr, uid, 'ids_emp.group_business_head'): print "------------------------------business--------------------------" url="http://ids-erp.idsil.loc:8069/web" values = { 'subject': 'Resignation' + '-' + resign.employee_id.name, 'body_html': 'Resignation of' +' '+resign.employee_id.name+' '+'is Intiated.Please take necessary action.<br/><br/><br/>Kindly do not reply.<br/>---This is auto generated email---<br/>Regards:<br/>ERP HR Team<br/>IDS Infotech LTD.<br/>Url:'+url, 'email_to': resign.employee_id.division.hr_email, 'email_from': '*****@*****.**', } #--------------------------------------------------------------- mail_obj = self.pool.get('mail.mail') msg_id = mail_obj.create(cr, uid, values, context=context) if msg_id: mail_obj.send(cr, uid, [msg_id], context=context) else: print "------------------------------manager--------------------------" url="http://ids-erp.idsil.loc:8069/web" values = { 'subject': 'Resignation' + '-' + resign.employee_id.name, 'body_html': 'Resignation of' +' '+resign.employee_id.name+' '+'is Intiated.Please take necessary action.<br/><br/><br/>Kindly do not reply.<br/>---This is auto generated email---<br/>Regards:<br/>ERP HR Team<br/>IDS Infotech LTD.<br/>Url:'+url, 'email_to': resign.employee_id.division.hod_email, 'email_cc': resign.employee_id.division.hr_email, 'email_from': '*****@*****.**', } #--------------------------------------------------------------- mail_obj = self.pool.get('mail.mail') msg_id = mail_obj.create(cr, uid, values, context=context) if msg_id: mail_obj.send(cr, uid, [msg_id], context=context) return self.write(cr, uid, ids, {'state': 'confirm'}) def separation_first_validate(self, cr, uid, ids, context=None): """Validating the form by Manager and update the working status. """ self._check_validate(cr, uid, ids, context=context) #code to update employee working status self._update_employee_working_status(cr, uid, ids, 'resigned', context=None) resign=self.browse(cr, uid, ids, context=None) url="http://ids-erp.idsil.loc:8069/web" values = { 'subject': 'Resignation' + '-' + resign.employee_id.name, 'body_html': 'Resignation of' +' '+resign.employee_id.name+' '+'is Approved.<br/><br/><br/>Kindly do not reply.<br/>---This is auto generated email---<br/>Regards:<br/>ERP HR Team<br/>IDS Infotech LTD.<br/><br/>Url:'+url, 'email_to': resign.employee_id.parent_id.work_email, 'email_cc': resign.employee_id.division.hr_email, 'email_from': '*****@*****.**', } #--------------------------------------------------------------- mail_obj = self.pool.get('mail.mail') msg_id = mail_obj.create(cr, uid, values, context=context) if msg_id: mail_obj.send(cr, uid, [msg_id], context=context) return self.write(cr, uid, ids, {'state':'approve'}) def separation_second_validate(self, cr, uid, ids, context=None): """Final validation by HOD. """ now=datetime.now() current = now.strftime('%Y-%m-%d') self._check_validate(cr, uid, ids, context=context) #code to update employee working status self._update_employee_working_status(cr, uid, ids, 'resigned', context=None) resign=self.browse(cr, uid, ids, context=None) if resign.last_date>current: raise osv.except_osv(_('Warning!'), _('You can not validate before last date in company!')) if resign.employee_id.parent_id.parent_id.id==7626: print "------------------------------res--------------------------" url="http://ids-erp.idsil.loc:8069/web" values = { 'subject': 'Resignation' + '-' + resign.employee_id.name, 'body_html': 'Resignation of' +' '+resign.employee_id.name+' '+'is Intiated.Please take necessary action.<br/><br/><br/>Kindly do not reply.<br/>---This is auto generated email---<br/>Regards:<br/>ERP HR Team<br/>IDS Infotech LTD.<br/>Url:'+url, 'email_to': resign.employee_id.parent_id.work_email, 'email_from': '*****@*****.**', } else: url="http://ids-erp.idsil.loc:8069/web" values = { 'subject': 'Resignation' + '-' + resign.employee_id.name, 'body_html': 'Resignation of' +' '+resign.employee_id.name+' '+'is Validated.<br/><br/><br/>Kindly do not reply.<br/>---This is auto generated email---<br/>Regards:<br/>ERP HR Team<br/>IDS Infotech LTD.\<br/><br/>Url:'+url, 'email_to': resign.employee_id.parent_id.work_email, 'email_cc': resign.employee_id.division.hod_email, 'email_from': '*****@*****.**', } #--------------------------------------------------------------- mail_obj = self.pool.get('mail.mail') msg_id = mail_obj.create(cr, uid, values, context=context) if msg_id: mail_obj.send(cr, uid, [msg_id], context=context) ee = self.pool.get('hr.employee').browse(cr, uid, resign.employee_id, context=context) emp_busi_id = self.pool.get('ids.business.information').search(cr, uid , [('employee_id','=',resign.employee_id.id)], context=context) eb = self.pool.get('ids.business.information').browse(cr, uid, emp_busi_id[0], context=context) emp_tech_id = self.pool.get('ids.technical.information').search(cr, uid , [('employee_id','=',resign.employee_id.id)], context=context) et = self.pool.get('ids.technical.information').browse(cr, uid, emp_tech_id[0], context=context) res = { 'employee_id':resign.employee_id.id, 'job_id':resign.employee_id.job_id.id, 'department_id':resign.employee_id.department_id.id, 'dob':resign.employee_id.birthday, 'division_id':resign.employee_id.division.id, 'location_id':resign.employee_id.office_location.id, 'gender':resign.employee_id.gender, 'mobile_no':resign.employee_id.mobile_phone, 'joining_date':resign.employee_id.joining_date, 'confirmation_status':resign.employee_id.confirmation_status.title(), 'resign_id':resign.id, 'capture_date':resign.capture_date, 'last_date':resign.last_date, 'email_control':eb.email_control, 'internet_control':eb.internet_control, 'remote_control':eb.remote_control, 'software_requirement':eb.software_requirements, 'application_share':eb.application_share_access, 'data_backup':eb.backup_remarks, 'allocation_it_asset':et.allocation_of_itassets or False, 'email_id_tech':et.email_created or False, 'internet_control_tech':et.internet_access_control or False, 'backup_setup_tech':et.backup_setup or False, 'software_requirement_tech': et.software_provisioning_and_access_control or False, 'application_share_tech':et.application_share_access or False, 'state':'phase1', } self.pool.get('emp.no.dues').create(cr, uid, res) return self.write(cr, uid, ids, {'state':'done'}) def separation_refuse(self, cr, uid, ids, context=None): """In case, resignation of employee is refused. """ #code to update employee working status self._update_employee_working_status(cr, uid, ids, 'working', context=None) resign=self.browse(cr, uid, ids, context=None) url="http://ids-erp.idsil.loc:8069/web" values = { 'subject': 'Resignation' + '-' + resign.employee_id.name, 'body_html': 'Resignation of' +' '+resign.employee_id.name+' '+'is Refused.<br/><br/><br/>Kindly do not reply.<br/>---This is auto generated email---<br/>Regards:<br/>ERP HR Team<br/>IDS Infotech LTD.<br/>Url:'+url, 'email_to': resign.employee_id.parent_id.work_email, 'email_cc': resign.employee_id.division.hr_email, 'email_from': '*****@*****.**', } #--------------------------------------------------------------- mail_obj = self.pool.get('mail.mail') msg_id = mail_obj.create(cr, uid, values, context=context) if msg_id: mail_obj.send(cr, uid, [msg_id], context=context) return self.write(cr, uid, ids, {'state': 'cancel'}) def refuse_location(self, cr, uid, ids, context=None): """In case, resignation of employee is refused. """ #code to update employee working status self._update_employee_working_status(cr, uid, ids, 'working', context=None) resign=self.browse(cr, uid, ids, context=None) url="http://ids-erp.idsil.loc:8069/web" values = { 'subject': 'Resignation' + '-' + resign.employee_id.name, 'body_html': 'Resignation of' +' '+resign.employee_id.name+' '+'is Refused.<br/><br/><br/>Kindly do not reply.<br/>---This is auto generated email---<br/>Regards:<br/>ERP HR Team<br/>IDS Infotech LTD.<br/>Url:'+url, 'email_to': resign.employee_id.parent_id.work_email, 'email_from': '*****@*****.**', } #--------------------------------------------------------------- mail_obj = self.pool.get('mail.mail') msg_id = mail_obj.create(cr, uid, values, context=context) if msg_id: mail_obj.send(cr, uid, [msg_id], context=context) return self.write(cr, uid, ids, {'state': 'cancel'}) def _update_employee_working_status(self, cr, uid, ids, working_status, context=None): """Updating final working status. """ obj_separation = self.browse(cr, uid, ids) sep_emp_id = 0 obj_emp = self.pool.get('hr.employee') for record in obj_separation: sep_emp_id = record.employee_id.id obj_emp.write(cr, SUPERUSER_ID , [sep_emp_id], {'working_status':working_status}) def _initiated_get(self, cr, uid, context=None): emp_id = context.get('default_employee_id', False) if emp_id: return emp_id ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context) if ids: return ids[0] return False _name = 'ids.hr.employee.separation' _inherit = ['mail.thread', 'ir.needaction_mixin'] _description = "Employee Separation" _columns = { 'rgn_number':fields.char('RGN Number', size=15, readonly=True), 'initiated_by': fields.many2one('hr.employee', 'Resign Initiated By', required=True), 'employee_id': fields.many2one('hr.employee', 'Employee', required=True), 'emp_code': fields.char('Employee Code', size=20), 'department_id':fields.many2one('hr.department', 'Department'), 'job_id': fields.many2one('hr.job', 'Designation'), 'confirmation_status': fields.related('employee_id', 'confirmation_status', type='char', relation='hr.employee', string='Confirmation Status', store=True, readonly=True), 'separation_type': fields.many2one('ids.hr.employee.separation.type', 'Resignation Type', required=True), 'rgn_accepted':fields.selection([('yes', 'Yes'), ('no', 'No'), ('na', 'N/A')], 'Resignation Accepted', required=True), 'reason': fields.many2one('ids.hr.employee.separation.reason', 'Reason', required=True), 'eligible_rehire': fields.boolean('Eligible for rehire?'), 'capture_date': fields.date('Capture date', required=True), 'last_date': fields.date('Last date in company', required=True), # 'interview_by': fields.many2one('hr.employee', 'Interview By', required=True), 'user_id':fields.related('employee_id', 'user_id', type='many2one', relation='res.users', string='User', store=True), 'manager_id1': fields.many2one('hr.employee', 'First Approval', invisible=False, readonly=True, help='This area is automatically filled by the user who approve/validate the resignation at first level'), 'manager_id2': fields.many2one('hr.employee', 'Second Approval', invisible=False, readonly=True, help='This area is automatically filled by the user who approve/validate the resignation at second level'), 'notes': fields.text('Notes'), 'state': fields.selection([('draft', 'Draft'), ('confirm', 'Waiting for Approval'), ('approve', 'Approved'), ('done', 'Validated'), ('cancel', 'Refused')], 'Status', readonly=True), 'full_final_status': fields.selection([('pending', 'Pending'), ('done', 'Done')],'Full & Final Status'), } _rec_name = 'rgn_number' _defaults = { 'state': 'draft', 'capture_date':fields.date.context_today, 'full_final_status':'pending', #'initiated_by': _initiated_get } def create(self, cr, uid, vals, context=None): """Create the unique id used for F&F """ vals['rgn_number']=self.pool.get('ir.sequence').get(cr, uid,'ids.hr.employee.separation') res=super(ids_hr_employee_separation, self).create(cr, uid, vals) return res def write(self, cr, uid, ids, vals, context=None): """Updating final working status. """ if vals.get('full_final_status', False): if (vals['full_final_status'] == 'done'): self._update_employee_working_status(cr, uid, ids, 'exit', context=None) if (vals['full_final_status'] == 'pending'): self._update_employee_working_status(cr, uid, ids, 'resigned', context=None) res=super(ids_hr_employee_separation, self).write(cr, uid, ids, vals) return res def unlink(self, cr, uid, ids, context=None): for rec in self.browse(cr, uid, ids, context=context): if rec.state not in ['draft']: raise osv.except_osv(_('Warning!'),_('You cannot delete a resignation which is not in draft state.')) return super(ids_hr_employee_separation, self).unlink(cr, uid, ids, context) def onchange_employee_id(self, cr, uid, ids, employee_id, capture_date, context=None): """get associated values of employee onchange of employee id. """ value = {'department_id': False} notice_period_days=0 if employee_id: employee = self.pool.get('hr.employee').browse(cr, uid, employee_id) notice_period_days = int(employee.notice) last_date = datetime.strptime(capture_date, "%Y-%m-%d")+timedelta(days=notice_period_days) value['department_id'] = employee.department_id.id value['emp_code'] = employee.emp_code value['job_id'] = employee.job_id.id value['initiated_by'] = employee.parent_id.id value['initiated_by'] = employee.parent_id.id value['last_date'] = last_date if employee.employment_type_id=='regular': value['confirmation_status'] = employee.confirmation_status else: value['confirmation_status'] = 'NA' return {'value': value} def _check_resignations(self, cr, uid, ids, context=None): """Constraints on submitting resignation. """ for obj in self.browse(cr, uid, ids, context=context): res_user_id = obj.user_id.id resignation_ids = self.search(cr, uid, [('id','not in',ids),('user_id', '=', res_user_id),('state', '!=', 'refuse')], context=context) if resignation_ids: raise osv.except_osv(_('Warning!'), _('Resignation is already in progress for this employee')) return True def _check_validate(self, cr, uid, ids, context=None): """Constraints on validating resignation. """ users_obj = self.pool.get('res.users') if not users_obj.has_group(cr, uid, 'base.group_hr_manager'): for separation in self.browse(cr, uid, ids, context=context): if separation.employee_id.user_id.id == uid: raise osv.except_osv(_('Warning!'), _('You cannot approve your own Resignation.')) if (separation.manager_id1 and separation.manager_id1.user_id.id == uid) or (separation.manager_id2 and separation.manager_id2.user_id.id == uid): raise osv.except_osv(_('Warning!'), _('You have already approved the Resignation.')) return True def calculate_last_day(self, cr, uid, ids, employee_id, capture_date,context=None): """Calculate last day from date of resignation. """ notice_period_days=0 if employee_id: employee = self.pool.get('hr.employee').browse(cr, uid,employee_id) notice_period_days = int(employee.notice) last_date = datetime.strptime(capture_date, "%Y-%m-%d")+timedelta(days=notice_period_days) return {'value':{'last_date':datetime.strftime(last_date,DEFAULT_SERVER_DATE_FORMAT)}}
class account_followup_print(osv.osv_memory): _name = 'account_followup.print' _description = 'Print Follow-up & Send Mail to Customers' _columns = { 'date': fields.date( 'Follow-up Sending Date', required=True, help= "This field allow you to select a forecast date to plan your follow-ups" ), 'followup_id': fields.many2one('account_followup.followup', 'Follow-Up', required=True, readonly=True), 'partner_ids': fields.many2many('account_followup.stat.by.partner', 'partner_stat_rel', 'osv_memory_id', 'partner_id', 'Partners', required=True), 'company_id': fields.related('followup_id', 'company_id', type='many2one', relation='res.company', store=True, readonly=True), 'email_conf': fields.boolean('Send Email Confirmation'), 'email_subject': fields.char('Email Subject', size=64), 'partner_lang': fields.boolean( 'Send Email in Partner Language', help= 'Do not change message text, if you want to send email in partner language, or configure from company' ), 'email_body': fields.text('Email Body'), 'summary': fields.text('Summary', readonly=True), 'test_print': fields.boolean( 'Test Print', help= 'Check if you want to print follow-ups without changing follow-up level.' ), } def _get_followup(self, cr, uid, context=None): if context is None: context = {} if context.get('active_model', 'ir.ui.menu') == 'account_followup.followup': return context.get('active_id', False) company_id = self.pool.get('res.users').browse( cr, uid, uid, context=context).company_id.id followp_id = self.pool.get('account_followup.followup').search( cr, uid, [('company_id', '=', company_id)], context=context) return followp_id and followp_id[0] or False def process_partners(self, cr, uid, partner_ids, data, context=None): partner_obj = self.pool.get('res.partner') partner_ids_to_print = [] nbmanuals = 0 manuals = {} nbmails = 0 nbunknownmails = 0 nbprints = 0 resulttext = " " for partner in self.pool.get( 'account_followup.stat.by.partner').browse(cr, uid, partner_ids, context=context): if partner.max_followup_id.manual_action: partner_obj.do_partner_manual_action(cr, uid, [partner.partner_id.id], context=context) nbmanuals = nbmanuals + 1 key = partner.partner_id.payment_responsible_id.name or _( "Anybody") if not key in manuals.keys(): manuals[key] = 1 else: manuals[key] = manuals[key] + 1 if partner.max_followup_id.send_email: nbunknownmails += partner_obj.do_partner_mail( cr, uid, [partner.partner_id.id], context=context) nbmails += 1 if partner.max_followup_id.send_letter: partner_ids_to_print.append(partner.id) nbprints += 1 message = _( "Follow-up letter of " ) + "<I> " + partner.partner_id.latest_followup_level_id_without_lit.name + "</I>" + _( " will be sent") partner_obj.message_post(cr, uid, [partner.partner_id.id], body=message, context=context) if nbunknownmails == 0: resulttext += str(nbmails) + _(" email(s) sent") else: resulttext += str(nbmails) + _( " email(s) should have been sent, but ") + str( nbunknownmails) + _( " had unknown email address(es)") + "\n <BR/> " resulttext += "<BR/>" + str(nbprints) + _( " letter(s) in report") + " \n <BR/>" + str(nbmanuals) + _( " manual action(s) assigned:") needprinting = False if nbprints > 0: needprinting = True resulttext += "<p align=\"center\">" for item in manuals: resulttext = resulttext + "<li>" + item + ":" + str( manuals[item]) + "\n </li>" resulttext += "</p>" result = {} action = partner_obj.do_partner_print(cr, uid, partner_ids_to_print, data, context=context) result['needprinting'] = needprinting result['resulttext'] = resulttext result['action'] = action or {} return result def do_update_followup_level(self, cr, uid, to_update, partner_list, date, context=None): #update the follow-up level on account.move.line for id in to_update.keys(): if to_update[id]['partner_id'] in partner_list: self.pool.get('account.move.line').write( cr, uid, [int(id)], { 'followup_line_id': to_update[id]['level'], 'followup_date': date }) def clear_manual_actions(self, cr, uid, partner_list, context=None): # Partnerlist is list to exclude # Will clear the actions of partners that have no due payments anymore partner_list_ids = [ partner.partner_id.id for partner in self.pool.get('account_followup.stat.by.partner'). browse(cr, uid, partner_list, context=context) ] ids = self.pool.get('res.partner').search( cr, uid, [ '&', ('id', 'not in', partner_list_ids), '|', ('payment_responsible_id', '!=', False), ('payment_next_action_date', '!=', False) ], context=context) partners_to_clear = [] for part in self.pool.get('res.partner').browse(cr, uid, ids, context=context): if not part.unreconciled_aml_ids: partners_to_clear.append(part.id) self.pool.get('res.partner').action_done(cr, uid, partners_to_clear, context=context) return len(partners_to_clear) def do_process(self, cr, uid, ids, context=None): if context is None: context = {} #Get partners tmp = self._get_partners_followp(cr, uid, ids, context=context) partner_list = tmp['partner_ids'] to_update = tmp['to_update'] date = self.browse(cr, uid, ids, context=context)[0].date data = self.read(cr, uid, ids, [], context=context)[0] data['followup_id'] = data['followup_id'][0] #Update partners self.do_update_followup_level(cr, uid, to_update, partner_list, date, context=context) #process the partners (send mails...) restot = self.process_partners(cr, uid, partner_list, data, context=context) #clear the manual actions if nothing is due anymore nbactionscleared = self.clear_manual_actions(cr, uid, partner_list, context=context) if nbactionscleared > 0: restot['resulttext'] = restot['resulttext'] + "<li>" + _( "%s partners have no credits and as such the action is cleared" ) % (str(nbactionscleared)) + "</li>" res = restot['action'] #return the next action mod_obj = self.pool.get('ir.model.data') model_data_ids = mod_obj.search( cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_account_followup_sending_results')], context=context) resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] context.update({ 'description': restot['resulttext'], 'needprinting': restot['needprinting'], 'report_data': res }) return { 'name': _('Send Letters and Emails: Actions Summary'), 'view_type': 'form', 'context': context, 'view_mode': 'tree,form', 'res_model': 'account_followup.sending.results', 'views': [(resource_id, 'form')], 'type': 'ir.actions.act_window', 'target': 'new', } def _get_msg(self, cr, uid, context=None): return self.pool.get('res.users').browse( cr, uid, uid, context=context).company_id.follow_up_msg _defaults = { 'date': lambda *a: time.strftime('%Y-%m-%d'), 'followup_id': _get_followup, 'email_body': "", 'email_subject': _('Invoices Reminder'), 'partner_lang': True, } def _get_partners_followp(self, cr, uid, ids, context=None): data = {} data = self.browse(cr, uid, ids, context=context)[0] company_id = data.company_id.id cr.execute( "SELECT l.partner_id, l.followup_line_id,l.date_maturity, l.date, l.id "\ "FROM account_move_line AS l "\ "LEFT JOIN account_account AS a "\ "ON (l.account_id=a.id) "\ "WHERE (l.reconcile_id IS NULL) "\ "AND (a.type='receivable') "\ "AND (l.state<>'draft') "\ "AND (l.partner_id is NOT NULL) "\ "AND (a.active) "\ "AND (l.debit > 0) "\ "AND (l.company_id = %s) " \ "AND (l.blocked = False)" \ "ORDER BY l.date", (company_id,)) #l.blocked added to take litigation into account and it is not necessary to change follow-up level of account move lines without debit move_lines = cr.fetchall() old = None fups = {} fup_id = 'followup_id' in context and context[ 'followup_id'] or data.followup_id.id date = 'date' in context and context['date'] or data.date current_date = datetime.date(*time.strptime(date, '%Y-%m-%d')[:3]) cr.execute( "SELECT * "\ "FROM account_followup_followup_line "\ "WHERE followup_id=%s "\ "ORDER BY delay", (fup_id,)) #Create dictionary of tuples where first element is the date to compare with the due date and second element is the id of the next level for result in cr.dictfetchall(): delay = datetime.timedelta(days=result['delay']) fups[old] = (current_date - delay, result['id']) old = result['id'] partner_list = [] to_update = {} #Fill dictionary of accountmovelines to_update with the partners that need to be updated for partner_id, followup_line_id, date_maturity, date, id in move_lines: if not partner_id: continue if followup_line_id not in fups: continue stat_line_id = partner_id * 10000 + company_id if date_maturity: if date_maturity <= fups[followup_line_id][0].strftime( '%Y-%m-%d'): if stat_line_id not in partner_list: partner_list.append(stat_line_id) to_update[str(id)] = { 'level': fups[followup_line_id][1], 'partner_id': stat_line_id } elif date and date <= fups[followup_line_id][0].strftime( '%Y-%m-%d'): if stat_line_id not in partner_list: partner_list.append(stat_line_id) to_update[str(id)] = { 'level': fups[followup_line_id][1], 'partner_id': stat_line_id } return {'partner_ids': partner_list, 'to_update': to_update}
raise osv.except_osv(_('Error'), _("Error While Fetching Magento Stores!!!, %s")%e) for store in stores: if store['website']['is_default'] == '1': store_info['website_id'] = int(store['website']['website_id']) store_info['store_id'] = view_pool._get_store_view(cr, uid, store, context) break; return store_info _columns = { 'name': fields.char('Base URL', required=True, size=255 ,select=True), 'instance_name': fields.char("Instance Name",size=64,select=True), 'user':fields.char('API User Name', required=True, size=100), 'pwd':fields.char('API Password',required=True, size=100), 'status':fields.char('Connection Status',readonly=True, size=255), 'active':fields.boolean('Active'), 'store_id':fields.many2one('magento.store.view', 'Default Magento Store'), 'group_id':fields.related('store_id', 'group_id', type="many2one", relation="magento.store", string="Default Store", readonly=True, store=True), 'website_id':fields.related('group_id', 'website_id', type="many2one", relation="magento.website", string="Default Magento Website", readonly=True), 'credential':fields.boolean('Show/Hide Credentials Tab', help="If Enable, Credentials tab will be displayed, " "And after filling the details you can hide the Tab."), 'auto_invoice':fields.boolean('Auto Invoice', help="If Enabled, Order will automatically Invoiced on Magento " " when Odoo order Get invoiced."), 'auto_ship':fields.boolean('Auto Shipment', help="If Enabled, Order will automatically shipped on Magento" " when Odoo order Get Delivered."), 'notify':fields.boolean('Notify Customer By Email', help="If True, customer will be notify" "during order shipment and invoice, else it won't."),
class sale_order(osv.osv): _inherit = "sale.order" def copy(self, cr, uid, id, default=None, context=None): if not default: default = {} default.update({ 'shipped': False, 'picking_ids': [], }) return super(sale_order, self).copy(cr, uid, id, default, context=context) def shipping_policy_change(self, cr, uid, ids, policy, context=None): if not policy: return {} inv_qty = 'order' if policy == 'prepaid': inv_qty = 'order' elif policy == 'picking': inv_qty = 'procurement' return {'value': {'invoice_quantity': inv_qty}} def write(self, cr, uid, ids, vals, context=None): if vals.get('order_policy', False): if vals['order_policy'] == 'prepaid': vals.update({'invoice_quantity': 'order'}) elif vals['order_policy'] == 'picking': vals.update({'invoice_quantity': 'procurement'}) return super(sale_order, self).write(cr, uid, ids, vals, context=context) def create(self, cr, uid, vals, context=None): if vals.get('order_policy', False): if vals['order_policy'] == 'prepaid': vals.update({'invoice_quantity': 'order'}) if vals['order_policy'] == 'picking': vals.update({'invoice_quantity': 'procurement'}) order = super(sale_order, self).create(cr, uid, vals, context=context) return order # This is False def _picked_rate(self, cr, uid, ids, name, arg, context=None): if not ids: return {} res = {} tmp = {} for id in ids: tmp[id] = {'picked': 0.0, 'total': 0.0} cr.execute('''SELECT p.sale_id as sale_order_id, sum(m.product_qty) as nbr, mp.state as procurement_state, m.state as move_state, p.type as picking_type FROM stock_move m LEFT JOIN stock_picking p on (p.id=m.picking_id) LEFT JOIN procurement_order mp on (mp.move_id=m.id) WHERE p.sale_id IN %s GROUP BY m.state, mp.state, p.sale_id, p.type''', (tuple(ids),)) for item in cr.dictfetchall(): if item['move_state'] == 'cancel': continue if item['picking_type'] == 'in':#this is a returned picking tmp[item['sale_order_id']]['total'] -= item['nbr'] or 0.0 # Deducting the return picking qty if item['procurement_state'] == 'done' or item['move_state'] == 'done': tmp[item['sale_order_id']]['picked'] -= item['nbr'] or 0.0 else: tmp[item['sale_order_id']]['total'] += item['nbr'] or 0.0 if item['procurement_state'] == 'done' or item['move_state'] == 'done': tmp[item['sale_order_id']]['picked'] += item['nbr'] or 0.0 for order in self.browse(cr, uid, ids, context=context): if order.shipped: res[order.id] = 100.0 else: res[order.id] = tmp[order.id]['total'] and (100.0 * tmp[order.id]['picked'] / tmp[order.id]['total']) or 0.0 return res _columns = { 'state': fields.selection([ ('draft', 'Draft Quotation'), ('sent', 'Quotation Sent'), ('cancel', 'Cancelled'), ('waiting_date', 'Waiting Schedule'), ('progress', 'Sales Order'), ('manual', 'Sale to Invoice'), ('shipping_except', 'Shipping Exception'), ('invoice_except', 'Invoice Exception'), ('done', 'Done'), ], 'Status', readonly=True,help="Gives the status of the quotation or sales order.\ \nThe exception status is automatically set when a cancel operation occurs \ in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception).\nThe 'Waiting Schedule' status is set when the invoice is confirmed\ but waiting for the scheduler to run on the order date.", select=True), 'incoterm': fields.many2one('stock.incoterms', 'Incoterm', help="International Commercial Terms are a series of predefined commercial terms used in international transactions."), 'picking_policy': fields.selection([('direct', 'Deliver each product when available'), ('one', 'Deliver all products at once')], 'Shipping Policy', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="""Pick 'Deliver each product when available' if you allow partial delivery."""), 'order_policy': fields.selection([ ('manual', 'On Demand'), ('picking', 'On Delivery Order'), ('prepaid', 'Before Delivery'), ], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="""On demand: A draft invoice can be created from the sales order when needed. \nOn delivery order: A draft invoice can be created from the delivery order when the products have been delivered. \nBefore delivery: A draft invoice is created from the sales order and must be paid before the products can be delivered."""), 'picking_ids': fields.one2many('stock.picking.out', 'sale_id', 'Related Picking', readonly=True, help="This is a list of delivery orders that has been generated for this sales order."), 'shipped': fields.boolean('Delivered', readonly=True, help="It indicates that the sales order has been delivered. This field is updated only after the scheduler(s) have been launched."), 'picked_rate': fields.function(_picked_rate, string='Picked', type='float'), 'invoice_quantity': fields.selection([('order', 'Ordered Quantities'), ('procurement', 'Shipped Quantities')], 'Invoice on', help="The sales order will automatically create the invoice proposition (draft invoice).\ You have to choose if you want your invoice based on ordered ", required=True, readonly=True, states={'draft': [('readonly', False)]}), } _defaults = { 'picking_policy': 'direct', 'order_policy': 'manual', 'invoice_quantity': 'order', } # Form filling def unlink(self, cr, uid, ids, context=None): sale_orders = self.read(cr, uid, ids, ['state'], context=context) unlink_ids = [] for s in sale_orders: if s['state'] in ['draft', 'cancel']: unlink_ids.append(s['id']) else: raise osv.except_osv(_('Invalid Action!'), _('In order to delete a confirmed sales order, you must cancel it.\nTo do so, you must first cancel related picking for delivery orders.')) return osv.osv.unlink(self, cr, uid, unlink_ids, context=context) def action_view_delivery(self, cr, uid, ids, context=None): ''' This function returns an action that display existing delivery orders of given sales order ids. It can either be a in a list or in a form view, if there is only one delivery order to show. ''' mod_obj = self.pool.get('ir.model.data') act_obj = self.pool.get('ir.actions.act_window') result = mod_obj.get_object_reference(cr, uid, 'stock', 'action_picking_tree') id = result and result[1] or False result = act_obj.read(cr, uid, [id], context=context)[0] #compute the number of delivery orders to display pick_ids = [] for so in self.browse(cr, uid, ids, context=context): pick_ids += [picking.id for picking in so.picking_ids] #choose the view_mode accordingly if len(pick_ids) > 1: result['domain'] = "[('id','in',["+','.join(map(str, pick_ids))+"])]" else: res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_form') result['views'] = [(res and res[1] or False, 'form')] result['res_id'] = pick_ids and pick_ids[0] or False return result def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed', 'done', 'exception'], date_invoice = False, context=None): picking_obj = self.pool.get('stock.picking') res = super(sale_order,self).action_invoice_create( cr, uid, ids, grouped=grouped, states=states, date_invoice = date_invoice, context=context) for order in self.browse(cr, uid, ids, context=context): if order.order_policy == 'picking': picking_obj.write(cr, uid, map(lambda x: x.id, order.picking_ids), {'invoice_state': 'invoiced'}) return res def action_cancel(self, cr, uid, ids, context=None): wf_service = netsvc.LocalService("workflow") if context is None: context = {} sale_order_line_obj = self.pool.get('sale.order.line') proc_obj = self.pool.get('procurement.order') for sale in self.browse(cr, uid, ids, context=context): for pick in sale.picking_ids: if pick.state not in ('draft', 'cancel'): raise osv.except_osv( _('Cannot cancel sales order!'), _('You must first cancel all delivery order(s) attached to this sales order.')) if pick.state == 'cancel': for mov in pick.move_lines: proc_ids = proc_obj.search(cr, uid, [('move_id', '=', mov.id)]) if proc_ids: for proc in proc_ids: wf_service.trg_validate(uid, 'procurement.order', proc, 'button_check', cr) for r in self.read(cr, uid, ids, ['picking_ids']): for pick in r['picking_ids']: wf_service.trg_validate(uid, 'stock.picking', pick, 'button_cancel', cr) return super(sale_order, self).action_cancel(cr, uid, ids, context=context) def action_wait(self, cr, uid, ids, context=None): res = super(sale_order, self).action_wait(cr, uid, ids, context=context) for o in self.browse(cr, uid, ids): noprod = self.test_no_product(cr, uid, o, context) if noprod and o.order_policy=='picking': self.write(cr, uid, [o.id], {'order_policy': 'manual'}, context=context) return res def procurement_lines_get(self, cr, uid, ids, *args): res = [] for order in self.browse(cr, uid, ids, context={}): for line in order.order_line: if line.procurement_id: res.append(line.procurement_id.id) return res def date_to_datetime(self, cr, uid, userdate, context=None): """ Convert date values expressed in user's timezone to server-side UTC timestamp, assuming a default arbitrary time of 12:00 AM - because a time is needed. :param str userdate: date string in in user time zone :return: UTC datetime string for server-side use """ # TODO: move to fields.datetime in server after 7.0 user_date = datetime.strptime(userdate, DEFAULT_SERVER_DATE_FORMAT) if context and context.get('tz'): tz_name = context['tz'] else: tz_name = self.pool.get('res.users').read(cr, SUPERUSER_ID, uid, ['tz'])['tz'] if tz_name: utc = pytz.timezone('UTC') context_tz = pytz.timezone(tz_name) user_datetime = user_date + relativedelta(hours=12.0) local_timestamp = context_tz.localize(user_datetime, is_dst=False) user_datetime = local_timestamp.astimezone(utc) return user_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) return user_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT) # if mode == 'finished': # returns True if all lines are done, False otherwise # if mode == 'canceled': # returns True if there is at least one canceled line, False otherwise def test_state(self, cr, uid, ids, mode, *args): assert mode in ('finished', 'canceled'), _("invalid mode for test_state") finished = True canceled = False write_done_ids = [] write_cancel_ids = [] for order in self.browse(cr, uid, ids, context={}): for line in order.order_line: if (not line.procurement_id) or (line.procurement_id.state=='done'): if line.state != 'done': write_done_ids.append(line.id) else: finished = False if line.procurement_id: if (line.procurement_id.state == 'cancel'): canceled = True if line.state != 'exception': write_cancel_ids.append(line.id) if write_done_ids: self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'}) if write_cancel_ids: self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'exception'}) if mode == 'finished': return finished elif mode == 'canceled': return canceled def _prepare_order_line_procurement(self, cr, uid, order, line, move_id, date_planned, context=None): return { 'name': line.name, 'origin': order.name, 'date_planned': date_planned, 'product_id': line.product_id.id, 'product_qty': line.product_uom_qty, 'product_uom': line.product_uom.id, 'product_uos_qty': (line.product_uos and line.product_uos_qty)\ or line.product_uom_qty, 'product_uos': (line.product_uos and line.product_uos.id)\ or line.product_uom.id, 'location_id': order.shop_id.warehouse_id.lot_stock_id.id, 'procure_method': line.type, 'move_id': move_id, 'company_id': order.company_id.id, 'note': line.name, 'property_ids': [(6, 0, [x.id for x in line.property_ids])], } def _prepare_order_line_move(self, cr, uid, order, line, picking_id, date_planned, context=None): location_id = order.shop_id.warehouse_id.lot_stock_id.id output_id = order.shop_id.warehouse_id.lot_output_id.id return { 'name': line.name, 'picking_id': picking_id, 'product_id': line.product_id.id, 'date': date_planned, 'date_expected': date_planned, 'product_qty': line.product_uom_qty, 'product_uom': line.product_uom.id, 'product_uos_qty': (line.product_uos and line.product_uos_qty) or line.product_uom_qty, 'product_uos': (line.product_uos and line.product_uos.id)\ or line.product_uom.id, 'product_packaging': line.product_packaging.id, 'partner_id': line.address_allotment_id.id or order.partner_shipping_id.id, 'location_id': location_id, 'location_dest_id': output_id, 'sale_line_id': line.id, 'tracking_id': False, 'state': 'draft', #'state': 'waiting', 'company_id': order.company_id.id, 'price_unit': line.product_id.standard_price or 0.0 } def _prepare_order_picking(self, cr, uid, order, context=None): pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out') return { 'name': pick_name, 'origin': order.name, 'date': self.date_to_datetime(cr, uid, order.date_order, context), 'type': 'out', 'state': 'auto', 'move_type': order.picking_policy, 'sale_id': order.id, 'partner_id': order.partner_shipping_id.id, 'note': order.note, 'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none', 'company_id': order.company_id.id, } def ship_recreate(self, cr, uid, order, line, move_id, proc_id): # FIXME: deals with potentially cancelled shipments, seems broken (specially if shipment has production lot) """ Define ship_recreate for process after shipping exception param order: sales order to which the order lines belong param line: sales order line records to procure param move_id: the ID of stock move param proc_id: the ID of procurement """ move_obj = self.pool.get('stock.move') if order.state == 'shipping_except': for pick in order.picking_ids: for move in pick.move_lines: if move.state == 'cancel': mov_ids = move_obj.search(cr, uid, [('state', '=', 'cancel'),('sale_line_id', '=', line.id),('picking_id', '=', pick.id)]) if mov_ids: for mov in move_obj.browse(cr, uid, mov_ids): # FIXME: the following seems broken: what if move_id doesn't exist? What if there are several mov_ids? Shouldn't that be a sum? move_obj.write(cr, uid, [move_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty}) self.pool.get('procurement.order').write(cr, uid, [proc_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty}) return True def _get_date_planned(self, cr, uid, order, line, start_date, context=None): start_date = self.date_to_datetime(cr, uid, start_date, context) date_planned = datetime.strptime(start_date, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=line.delay or 0.0) date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT) return date_planned def _create_pickings_and_procurements(self, cr, uid, order, order_lines, picking_id=False, context=None): """Create the required procurements to supply sales order lines, also connecting the procurements to appropriate stock moves in order to bring the goods to the sales order's requested location. If ``picking_id`` is provided, the stock moves will be added to it, otherwise a standard outgoing picking will be created to wrap the stock moves, as returned by :meth:`~._prepare_order_picking`. Modules that wish to customize the procurements or partition the stock moves over multiple stock pickings may override this method and call ``super()`` with different subsets of ``order_lines`` and/or preset ``picking_id`` values. :param browse_record order: sales order to which the order lines belong :param list(browse_record) order_lines: sales order line records to procure :param int picking_id: optional ID of a stock picking to which the created stock moves will be added. A new picking will be created if ommitted. :return: True """ move_obj = self.pool.get('stock.move') picking_obj = self.pool.get('stock.picking') procurement_obj = self.pool.get('procurement.order') proc_ids = [] for line in order_lines: if line.state == 'done': continue date_planned = self._get_date_planned(cr, uid, order, line, order.date_order, context=context) if line.product_id: if line.product_id.type in ('product', 'consu'): if not picking_id: picking_id = picking_obj.create(cr, uid, self._prepare_order_picking(cr, uid, order, context=context)) move_id = move_obj.create(cr, uid, self._prepare_order_line_move(cr, uid, order, line, picking_id, date_planned, context=context)) else: # a service has no stock move move_id = False proc_id = procurement_obj.create(cr, uid, self._prepare_order_line_procurement(cr, uid, order, line, move_id, date_planned, context=context)) proc_ids.append(proc_id) line.write({'procurement_id': proc_id}) self.ship_recreate(cr, uid, order, line, move_id, proc_id) wf_service = netsvc.LocalService("workflow") if picking_id: wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) for proc_id in proc_ids: wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr) val = {} if order.state == 'shipping_except': val['state'] = 'progress' val['shipped'] = False if (order.order_policy == 'manual'): for line in order.order_line: if (not line.invoiced) and (line.state not in ('cancel', 'draft')): val['state'] = 'manual' break order.write(val) return True def action_ship_create(self, cr, uid, ids, context=None): for order in self.browse(cr, uid, ids, context=context): self._create_pickings_and_procurements(cr, uid, order, order.order_line, None, context=context) return True def action_ship_end(self, cr, uid, ids, context=None): for order in self.browse(cr, uid, ids, context=context): val = {'shipped': True} if order.state == 'shipping_except': val['state'] = 'progress' if (order.order_policy == 'manual'): for line in order.order_line: if (not line.invoiced) and (line.state not in ('cancel', 'draft')): val['state'] = 'manual' break for line in order.order_line: towrite = [] if line.state == 'exception': towrite.append(line.id) if towrite: self.pool.get('sale.order.line').write(cr, uid, towrite, {'state': 'done'}, context=context) res = self.write(cr, uid, [order.id], val) return True def has_stockable_products(self, cr, uid, ids, *args): for order in self.browse(cr, uid, ids): for order_line in order.order_line: if order_line.product_id and order_line.product_id.type in ('product', 'consu'): return True return False
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): result = super(product_search_ept, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar,submenu) if context is None: context={} self._columns = {} self._columns['product_ids'] = fields.text('Product IDS') if view_type != 'form': return result _moves_arch_lst = """ <form string="Stone Search" version="7.0"> <div> """ #_moves_arch_lst += """<group colspan="4" col="10">""" _line_fields = result['fields'] info = [ {'model':None,'_column_name':'product_name','label':'Stone ID','type':'char','name':'name','product_search_type':'char'}, {'model':None,'_column_name':'product_certificate_no','label':'Certificate No.','type':'char','name':'certificate_no','product_search_type':'char'}, {'model':None,'_column_name':'product_weight','label':'Weight','no':3,'type':'float','name':'weight','product_search_type':'char','range':True}, {'model':None,'_column_name':'product_price_caret','label':'PPC','no':1,'type':'float','name':'price_caret','product_search_type':'char','range':True}, {'model':None,'_column_name':'product_discount','label':'Back%','no':1,'type':'float','name':'discount','product_search_type':'char','range':True}, {'model':'product.shape','width':'15%','_column_name':'product_shape','label':'SHP','help':'Shape','type':'many2one','name':'shape_id','product_search_type':'boolean','on_change':'product_shape_change'}, {'model':'product.color','width':'8%','_column_name':'product_color','label':'CLR','help':'Color','type':'many2one','name':'color_id','product_search_type':'boolean','on_change':'product_color_change'}, {'model':'product.clarity','width':'10%','_column_name':'product_clarity','label':'CLRTY','help':'Clarity','type':'many2one','name':'clarity_id','product_search_type':'boolean','on_change':'product_clarity_change'}, {'model':'product.cut','width':'12%','_column_name':'product_cut','label':'CUT','help':'Cut','type':'many2one','name':'cut_id','product_search_type':'boolean','on_change':'product_cut_change'}, {'model':'product.polish','width':'8%','_column_name':'product_polish','label':'POL','help':'Polish','type':'many2one','name':'polish_id','product_search_type':'boolean','on_change':'product_polish_change'}, {'model':'product.symmetry','width':'10%','_column_name':'product_symmetry','label':'SYM','help':'Symmetry','type':'many2one','name':'symmetry_id','product_search_type':'boolean','on_change':'product_symmetry_change'}, {'model':'product.fluorescence.intensity','width':'13%','_column_name':'product_fluorescence_intensity','label':'FLUR','help':'Fluorescence Intensity','type':'many2one','name':'fluorescence_intensity_id','product_search_type':'boolean','on_change':'product_fluorescence_intensity_change'}, {'model':'product.lab','width':'8%','_column_name':'product_lab','label':'LAB','help':'Lab','type':'many2one','name':'lab_id','product_search_type':'boolean','on_change':'product_lab_change'}, {'model':'product.fancy.color','width':'15%','_column_name':'product_fancy_color','label':'FNC CLR','help':'Fancy Color','type':'many2one','name':'fancy_color_id','product_search_type':'boolean','on_change':'product_fancy_color_change'}, {'model':'product.fancy.color.intensity','width':'15%','_column_name':'product1_fancy_color_intensity','label':'FNC CLR INT','help':'Fancy Color Intensity','type':'many2one','name':'fancy_color_intensity','product_search_type':'boolean','on_change':'product1_fancy_color_intensity_change'}, {'model':'product.fancy.color.overtone','width':'15%','_column_name':'product2_fancy_color_overtone','label':'FNC CLR OVR','help':'Fancy Color Overtone','type':'many2one','name':'fancy_color_overtone','product_search_type':'boolean','on_change':'product2_fancy_color_overtone_change'}, {'model':None,'_column_name':'product_status','width':'20%','label':'Status','type':'selection','name':'product_status','product_search_type':'boolean' ,'selection_val':[('available','Available'), ('hold','Hold'), ('sold','Sold'), ('on_approval','On Approval'), ('on_consignment','On Consignment'), ('offline','Offline'), ('repair','Repair'), ('web_sale','Web Sale')]}, {'model':'stock.location','_column_name':'stock_location','width':'15%','label':'Location','type':'many2one','name':'location_id','product_search_type':'boolean' ,'domain':[('usage','=','internal')],}, ] for model_info in info : if model_info['type'] == 'many2one' and model_info['product_search_type'] == 'boolean' : if model_info['model']: ids = self.pool.get(model_info['model']).search(cr,uid,model_info.get('domain',[])) if ids : _moves_arch_lst += """<div style="float:left;width:%s;">"""%(model_info.get('width', '100%')) ''' Header ''' if model_info.get('label', False)=='Location': _moves_arch_lst += """<u><label style="color:rgb(124,123,173);font-weight:bold;" string="%s"/></u>"""%(model_info['label']) if model_info.get('on_change', False): ''' Check box for Select All ''' _moves_arch_lst += """<div><field name="%s" class="oe_inline" nolabel="1" on_change="%s(%s)"/> """%(model_info['on_change'],model_info['on_change'],model_info['on_change']) ''' Label for Select All ''' _moves_arch_lst += """<u><label help='%s' style="color:rgb(124, 123, 173);" string="%s" for="%s" /></u></div>"""%(model_info['help'],model_info['label'],model_info['label']) _line_fields.update({ '%s'%(model_info['on_change']) : { 'string': 'All ?', 'type' : 'boolean', },}) self._columns['%s'%(model_info['on_change'])] = fields.boolean(model_info['on_change']) for obj in self.pool.get(model_info['model']).browse(cr,uid,ids,context=context): name=len(obj.name) > 7 and (obj.name[:7]+'...') or obj.name[:7] _line_fields.update({ '%s%s'%(model_info['_column_name'],obj.id) : { 'string': obj.name, 'type' : 'boolean', 'help' : '%s'%(obj.name) },}) self._columns['%s%s'%(model_info['_column_name'],obj.id)] = fields.boolean(obj.name) ''' Check box and related label ''' _moves_arch_lst += """ <div><field name="%s%s" class="oe_inline" nolabel="1"/> <label string="%s" for="%s%s" /></div> """%(model_info['_column_name'],obj.id,name,model_info['_column_name'],obj.id) _moves_arch_lst += """</div>""" ####################### if model_info['type'] == 'char' and model_info['product_search_type'] == 'char': _moves_arch_lst += """<div style="width:%s;float:left;">""" %('50%') _line_fields.update({ '%s'%(model_info['_column_name']) : { 'string': 'Name', 'type' : 'char', 'help' : '%s'%(model_info['_column_name']), },}) self._columns['%s'%(model_info['_column_name'])] = fields.char(model_info['label'],size=1024) _moves_arch_lst += """ <div> <label style="color:rgb(124, 123, 173);" string="%s" for="%s" /> <field name="%s" style="width: 70%%" nolabel="1"/> </div> </div> """%(model_info['label'],model_info['_column_name'],model_info['_column_name']) ################################ if model_info['type'] == 'selection' and model_info['product_search_type'] == 'boolean' : if model_info['selection_val']: _moves_arch_lst += """<div style="float:left;width:%s">"""%(model_info['width']) _moves_arch_lst += """<u><label style="color:rgb(124, 123, 173);font-weight:bold;" string="%s" /></u><newline/>"""%(model_info['label']) for value in model_info['selection_val']: _line_fields.update({ '%s_%s'%(model_info['_column_name'],value[0]) : { 'string': value[1], 'type' : 'boolean', },}) self._columns['%s_%s'%(model_info['_column_name'],value[0])] = fields.boolean(value[1]) _moves_arch_lst += """ <div><field name="%s_%s" nolabel="1"/> <label string="%s" for="%s_%s" /></div> """%(model_info['_column_name'],value[0],value[1],model_info['_column_name'],value[0]) _moves_arch_lst +="""</div>""" ########################### if model_info.get('range') and model_info['range']: width = '50%' if model_info.get('no') > 1:width = '100%' _moves_arch_lst += """<div style="float:left;width:%s;">"""%(width) _moves_arch_lst += """<div style="float:left;width:%s;"><label style="color:rgb(124, 123, 173);font-weight:bold;" string="%s" /></div>"""%('15%',model_info['label']) if model_info.get('no'): no = model_info.get('no') wid = str(85/int(no)) + '%' while no != 0 : no = no - 1 _line_fields.update({'%s_from_%s'%(model_info['_column_name'],no) : {'string': model_info['label'],'type':'float'}}) _line_fields.update({'%s_to_%s'%(model_info['_column_name'],no) : {'string': model_info['label'],'type':'float'}}) self._columns['%s_from_%s'%(model_info['_column_name'],no)] = fields.float(model_info['label'],digits=(16,2)) self._columns['%s_to_%s'%(model_info['_column_name'],no)] = fields.float(model_info['label'],digits=(16,2)) _moves_arch_lst += """ <div style="float:left;width:%s;"> <div style="float:left;"><field name="%s_from_%s" placeholder="From" class="oe_inline" nolabel="1"/></div> <div style="float:left;"><b><label style="color:rgb(124, 123, 173);" string="--" /></b></div> <div style="float:left;"><field name="%s_to_%s" placeholder="To" class="oe_inline" nolabel="1"/></div> </div> """%(wid,model_info['_column_name'],no,model_info['_column_name'],no) _moves_arch_lst += """</div>""" _moves_arch_lst += """ </div> <footer> <button name="get_product" string="Search" type="object" colspan="2" class="oe_highlight"/> or <button string="Cancel" class="oe_link" special="cancel"/> </footer> </form> """ result['arch'] = _moves_arch_lst result['arch'] = result['arch'].replace('&','&') result['fields'] = _line_fields return result
class ir_cron(osv.osv): """ Model describing cron jobs (also called actions or tasks). """ # TODO: perhaps in the future we could consider a flag on ir.cron jobs # that would cause database wake-up even if the database has not been # loaded yet or was already unloaded (e.g. 'force_db_wakeup' or something) # See also openerp.cron _name = "ir.cron" _order = 'name' _columns = { 'name': fields.char('Name', required=True), 'user_id': fields.many2one('res.users', 'User', required=True), 'active': fields.boolean('Active'), 'interval_number': fields.integer('Interval Number',help="Repeat every x."), 'interval_type': fields.selection( [('minutes', 'Minutes'), ('hours', 'Hours'), ('work_days','Work Days'), ('days', 'Days'),('weeks', 'Weeks'), ('months', 'Months')], 'Interval Unit'), 'numbercall': fields.integer('Number of Calls', help='How many times the method is called,\na negative number indicates no limit.'), 'doall' : fields.boolean('Repeat Missed', help="Specify if missed occurrences should be executed when the server restarts."), 'nextcall' : fields.datetime('Next Execution Date', required=True, help="Next planned execution date for this job."), 'model': fields.char('Object', help="Model name on which the method to be called is located, e.g. 'res.partner'."), 'function': fields.char('Method', help="Name of the method to be called when this job is processed."), 'args': fields.text('Arguments', help="Arguments to be passed to the method, e.g. (uid,)."), 'priority': fields.integer('Priority', help='The priority of the job, as an integer: 0 means higher priority, 10 means lower priority.') } _defaults = { 'nextcall' : lambda *a: time.strftime(DEFAULT_SERVER_DATETIME_FORMAT), 'priority' : 5, 'user_id' : lambda obj,cr,uid,context: uid, 'interval_number' : 1, 'interval_type' : 'months', 'numbercall' : 1, 'active' : 1, } def _check_args(self, cr, uid, ids, context=None): try: for this in self.browse(cr, uid, ids, context): str2tuple(this.args) except Exception: return False return True _constraints = [ (_check_args, 'Invalid arguments', ['args']), ] def _handle_callback_exception(self, cr, uid, model_name, method_name, args, job_id, job_exception): """ Method called when an exception is raised by a job. Simply logs the exception and rollback the transaction. :param model_name: model name on which the job method is located. :param method_name: name of the method to call when this job is processed. :param args: arguments of the method (without the usual self, cr, uid). :param job_id: job id. :param job_exception: exception raised by the job. """ cr.rollback() def _callback(self, cr, uid, model_name, method_name, args, job_id): """ Run the method associated to a given job It takes care of logging and exception handling. :param model_name: model name on which the job method is located. :param method_name: name of the method to call when this job is processed. :param args: arguments of the method (without the usual self, cr, uid). :param job_id: job id. """ try: args = str2tuple(args) openerp.modules.registry.RegistryManager.check_registry_signaling(cr.dbname) registry = openerp.registry(cr.dbname) if model_name in registry: model = registry[model_name] if hasattr(model, method_name): log_depth = (None if _logger.isEnabledFor(logging.DEBUG) else 1) netsvc.log(_logger, logging.DEBUG, 'cron.object.execute', (cr.dbname,uid,'*',model_name,method_name)+tuple(args), depth=log_depth) if _logger.isEnabledFor(logging.DEBUG): start_time = time.time() getattr(model, method_name)(cr, uid, *args) if _logger.isEnabledFor(logging.DEBUG): end_time = time.time() _logger.debug('%.3fs (%s, %s)' % (end_time - start_time, model_name, method_name)) openerp.modules.registry.RegistryManager.signal_caches_change(cr.dbname) else: msg = "Method `%s.%s` does not exist." % (model_name, method_name) _logger.warning(msg) else: msg = "Model `%s` does not exist." % model_name _logger.warning(msg) except Exception, e: _logger.exception("Call of self.pool.get('%s').%s(cr, uid, *%r) failed in Job %s" % (model_name, method_name, args, job_id)) self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)
from openerp.osv import orm, fields FISCAL_POSITION_COLUMNS = { 'name': fields.char('Fiscal Position', size=128, required=True), 'fiscal_category_id': fields.many2one( 'l10n_br_account.fiscal.category', 'Categoria Fiscal'), 'fiscal_category_fiscal_type': fields.related( 'fiscal_category_id', 'fiscal_type', type='char', readonly=True, relation='l10n_br_account.fiscal.category', store=True, string='Fiscal Type'), 'type': fields.selection([('input', 'Entrada'), ('output', 'Saida')], 'Tipo'), 'type_tax_use': fields.selection( [('sale', 'Sale'), ('purchase', 'Purchase'), ('all', 'All')], 'Tax Application'), 'inv_copy_note': fields.boolean(u'Copiar Observação na Nota Fiscal'), 'asset_operation': fields.boolean(u'Operação de Aquisição de Ativo', help=u"""Caso seja marcada essa opção, será incluido o IPI na base de calculo do ICMS."""), 'state': fields.selection([('draft', u'Rascunho'), ('review', u'Revisão'), ('approved', u'Aprovada'), ('unapproved', u'Não Aprovada')], 'Status', readonly=True, track_visibility='onchange', select=True), } FISCAL_POSITION_DEFAULTS = { 'state': 'draft', } class AccountFiscalPositionTemplate(orm.Model):
class view(osv.osv): _name = "ir.ui.view" _inherit = ["ir.ui.view", "website.seo.metadata"] _columns = { 'page': fields.boolean("Whether this view is a web page template (complete)"), 'customize_show': fields.boolean("Show As Optional Inherit"), 'website_id': fields.many2one('website', ondelete='cascade', string="Website"), } _defaults = { 'page': False, 'customize_show': False, } def unlink(self, cr, uid, ids, context=None): res = super(view, self).unlink(cr, uid, ids, context=context) self.clear_caches() return res def _view_obj(self, cr, uid, view_id, context=None): if isinstance(view_id, basestring): if 'website_id' in (context or {}): domain = [('key', '=', view_id), '|', ('website_id', '=', False), ('website_id', '=', context.get('website_id'))] rec_id = self.search(cr, uid, domain, order='website_id', context=context) else: rec_id = self.search(cr, uid, [('key', '=', view_id)], context=context) if rec_id: return self.browse(cr, uid, rec_id, context=context)[0] else: return self.pool['ir.model.data'].xmlid_to_object( cr, uid, view_id, raise_if_not_found=True, context=context) elif isinstance(view_id, (int, long)): return self.browse(cr, uid, view_id, context=context) # assume it's already a view object (WTF?) return view_id # Returns all views (called and inherited) related to a view # Used by translation mechanism, SEO and optional templates def _views_get(self, cr, uid, view_id, options=True, bundles=False, context=None, root=True): """ For a given view ``view_id``, should return: * the view itself * all views inheriting from it, enabled or not - but not the optional children of a non-enabled child * all views called from it (via t-call) """ try: view = self._view_obj(cr, uid, view_id, context=context) except ValueError: _logger.warning("Could not find view object with view_id '%s'" % (view_id)) # Shall we log that ? Yes, you should ! return [] while root and view.inherit_id: view = view.inherit_id result = [view] node = etree.fromstring(view.arch) xpath = "//t[@t-call]" if bundles: xpath += "| //t[@t-call-assets]" for child in node.xpath(xpath): try: called_view = self._view_obj(cr, uid, child.get( 't-call', child.get('t-call-assets')), context=context) except ValueError: continue if called_view not in result: result += self._views_get(cr, uid, called_view, options=options, bundles=bundles, context=context) extensions = view.inherit_children_ids if not options: # only active children extensions = (v for v in view.inherit_children_ids if v.active) # Keep options in a deterministic order regardless of their applicability for extension in sorted(extensions, key=lambda v: v.id): for r in self._views_get( cr, uid, extension, # only return optional grandchildren if this child is enabled options=extension.active, context=context, root=False): if r not in result: result.append(r) return result @tools.ormcache_context('uid', 'xml_id', keys=('website_id', )) def get_view_id(self, cr, uid, xml_id, context=None): if context and 'website_id' in context and not isinstance( xml_id, (int, long)): domain = [('key', '=', xml_id), '|', ('website_id', '=', context['website_id']), ('website_id', '=', False)] [view_id] = self.search( cr, uid, domain, order='website_id', limit=1, context=context) or [None] if not view_id: raise ValueError('View %r in website %r not found' % (xml_id, context['website_id'])) else: view_id = super(view, self).get_view_id(cr, uid, xml_id, context=context) return view_id @api.cr_uid_ids_context def render(self, cr, uid, id_or_xml_id, values=None, engine='ir.qweb', context=None): if request and getattr(request, 'website_enabled', False): engine = 'ir.qweb' if isinstance(id_or_xml_id, list): id_or_xml_id = id_or_xml_id[0] qcontext = self._prepare_qcontext(cr, uid, context=context) # add some values if values: qcontext.update(values) # in edit mode ir.ui.view will tag nodes if not qcontext.get('translatable') and not qcontext.get( 'rendering_bundle'): if qcontext.get('editable'): context = dict(context, inherit_branding=True) elif request.registry['res.users'].has_group( cr, uid, 'base.group_website_publisher'): context = dict(context, inherit_branding_auto=True) view_obj = request.website.get_template(id_or_xml_id) if 'main_object' not in qcontext: qcontext['main_object'] = view_obj values = qcontext return super(view, self).render(cr, uid, id_or_xml_id, values=values, engine=engine, context=context) def _prepare_qcontext(self, cr, uid, context=None): if not context: context = {} company = self.pool['res.company'].browse( cr, SUPERUSER_ID, request.website.company_id.id, context=context) editable = request.website.is_publisher() translatable = editable and context.get( 'lang') != request.website.default_lang_code editable = not translatable and editable qcontext = dict( context.copy(), website=request.website, url_for=website.url_for, slug=website.slug, res_company=company, user_id=self.pool.get("res.users").browse(cr, uid, uid), default_lang_code=request.website.default_lang_code, languages=request.website.get_languages(), translatable=translatable, editable=editable, menu_data=self.pool['ir.ui.menu'].load_menus_root( cr, uid, context=context) if request.website.is_user() else None, ) return qcontext def customize_template_get(self, cr, uid, key, full=False, bundles=False, context=None): """ Get inherit view's informations of the template ``key``. By default, only returns ``customize_show`` templates (which can be active or not), if ``full=True`` returns inherit view's informations of the template ``key``. ``bundles=True`` returns also the asset bundles """ imd = self.pool['ir.model.data'] theme_view_id = imd.xmlid_to_res_id(cr, uid, 'website.theme') user = self.pool['res.users'].browse(cr, uid, context=context) user_groups = set(user.groups_id) views = self._views_get(cr, uid, key, bundles=bundles, context=dict(context or {}, active_test=False)) done = set() result = [] for v in views: if not user_groups.issuperset(v.groups_id): continue if full or (v.customize_show and v.inherit_id.id != theme_view_id): if v.inherit_id not in done: result.append({ 'name': v.inherit_id.name, 'id': v.id, 'key': v.key, 'inherit_id': v.inherit_id.id, 'header': True, 'active': False }) done.add(v.inherit_id) result.append({ 'name': v.name, 'id': v.id, 'key': v.key, 'inherit_id': v.inherit_id.id, 'header': False, 'active': v.active, }) return result
class stock_import_inventory(osv.osv_memory): _name = "stock.import.inventory" _description = "Import Inventory" def _default_location(self, cr, uid, ids, context=None): try: loc_model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock') except ValueError, e: return False return location_id or False _columns = { 'location_id': fields.many2one('stock.location', 'Location', required=True), 'import_file': fields.binary('File', filters="*.xls"), #to consider the product current inventory or not, if yes then add the current inventory to the upload excel quantity as the quantity to do physical inventory 'consider_inventory': fields.boolean('Consider Current Inventory', select=True), 'all_done': fields.boolean('All Data Imported', readonly=True, select=True), 'result_line': fields.one2many('stock.import.inventory.result', 'import_id', 'Importing Result', readonly=True), 'file_template': fields.binary('Template File', readonly=True), 'file_template_name': fields.char('Template File Name'), } def _get_template(self, cr, uid, context): cur_path = os.path.split(os.path.realpath(__file__))[0] path = os.path.join(cur_path,'stock_import_template.xls') data = open(path,'rb').read().encode('base64') # data = os.path.getsize(path) return data _defaults = { 'location_id': _default_location,
class Message(osv.Model): #TODO: turn off for data import only _log_access = True _name = "message.message" _order = "write_date desc,sequence" _description = 'UPDIS Message' _inherit = ['mail.thread', 'ir.needaction_mixin'] def _default_fbbm(self, cr, uid, context=None): employee_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)]) if employee_ids: return self.pool.get('hr.employee').browse(cr, uid, employee_ids[0]).department_id.name def _default_department(self, cr, uid, context=None): employee_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)]) if employee_ids: return self.pool.get('hr.employee').browse(cr, uid, employee_ids[0]).department_id.id def _get_image(self, cr, uid, ids, name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): result[obj.id] = tools.image_get_resized_images(obj.image) return result def _set_image(self, cr, uid, id, field_name, value, args, context=None): return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context) def _get_name_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): result[obj.id] = obj.is_display_name and obj.create_uid.name or u'匿名用户' return result def _get_message_meta_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): message_meta = '' if obj.category_id.message_meta: env = Env(cr, uid, 'message.message', [obj.id]) message_meta = eval(obj.category_id.message_meta, env, nocopy=True) result[obj.id] = message_meta return result def _get_category_message_title_meta_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): category_message_title_meta = '' if obj.category_id.category_message_title_meta: env = Env(cr, uid, 'message.message', [obj.id]) category_message_title_meta = eval(obj.category_id.category_message_title_meta, env, nocopy=True) result[obj.id] = category_message_title_meta return result def _get_create_date_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): if obj.create_date: create_date_display = datetime.datetime.strptime(obj.create_date, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(hours=8) result[obj.id] = create_date_display.strftime('%Y-%m-%d %H:%M:%S') return result def _get_write_date_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): if obj.write_date: write_date_display = datetime.datetime.strptime(obj.write_date, '%Y-%m-%d %H:%M:%S') + datetime.timedelta(hours=8) result[obj.id] = write_date_display.strftime('%Y-%m-%d %H:%M:%S') return result def _get_shorten_name(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): size = obj.category_id.category_message_title_size title = len(obj.name) > size and obj.name[:size] + '...' or obj.name result[obj.id] = title return result def _get_message_list_meta_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): message_meta = '' if obj.category_id.message_meta: env = Env(cr, uid, 'message.message', [obj.id]) message_meta = eval(obj.category_id.phone_message_list_meta, env, nocopy=True) result[obj.id] = message_meta return result def _get_message_detail_meta_display(self, cr, uid, ids, field_name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): category_message_title_meta = '' if obj.category_id.category_message_title_meta: env = Env(cr, uid, 'message.message', [obj.id]) category_message_title_meta = eval(obj.category_id.phone_message_detail_meta, env, nocopy=True) result[obj.id] = category_message_title_meta return result def _get_vote_count(self, cr, uid, ids, field_name, args, context=None): """ Either way, it must return a dictionary of values of the form {'id_1_': 'value_1_', 'id_2_': 'value_2_',...}. If multi is set, then field_name is replaced by field_names: a list of the field names that should be calculated. Each value in the returned dictionary is also a dictionary from field name to value. For example, if the fields 'name', and 'age' are both based on the vital_statistics function, then the return value of vital_statistics might look like this when ids is [1, 2, 5]: { 1: {'name': 'Bob', 'age': 23}, 2: {'name': 'Sally', 'age', 19}, 5: {'name': 'Ed', 'age': 62} } """ result = dict.fromkeys(ids, False) message_vote_obj = self.pool.get('message.vote') for message in self.browse(cr, uid, ids, context=context): vote_like_count = len(message_vote_obj.search(cr, SUPERUSER_ID, [('message_id', '=', message.id), ('up', '=', True)], context)) vote_unlike_count = len(message_vote_obj.search(cr, SUPERUSER_ID, [('message_id', '=', message.id), ('up', '=', False)], context)) result[message.id] = { 'vote_like': vote_like_count, 'vote_unlike': vote_unlike_count, } return result _columns = { 'name': fields.char("Title", size=128, required=True), 'shorten_name': fields.function(_get_shorten_name, type="char", size=256, string="Shorten title"), 'message_meta_display': fields.function(_get_message_meta_display, type="char", size=256, string="Meta"), 'category_message_title_meta_display': fields.function(_get_category_message_title_meta_display, type="char", size=256, string="Category meta"), 'message_list_meta_display': fields.function(_get_message_list_meta_display, type="char", size=256, string="Phone Message List Meta"), 'message_detail_meta_display': fields.function(_get_message_detail_meta_display, type="char", size=256, string="Phone Message Detail meta"), 'category_id': fields.many2one('message.category', 'Category', required=True, change_default=True), 'content': fields.text("Content"), 'sequence': fields.integer("Display Sequence"), 'is_display_name': fields.boolean('Display name?'), 'fbbm': fields.char('Publisher', size=128), 'read_times': fields.integer("Read Times"), 'expire_date': fields.date('Expire Date'), 'create_date': fields.datetime('Created on', select=True ,readonly=True), 'department_id': fields.many2one("hr.department", "Department", domain=[('deleted', '=', False)]), 'create_uid': fields.many2one('res.users', 'Author', select=True, readonly=True), 'write_date': fields.datetime('Modification date', select=True , readonly=True), 'write_uid': fields.many2one('res.users', 'Last Contributor', select=True , readonly=True), 'source': fields.char("Message Source", size=128), 'name_for_display': fields.function(_get_name_display, type="char", size=64, string="Name"), 'sms_receiver_ids': fields.many2many("hr.employee", "message_hr_employee_rel", "message_id", "hr_employee_id", "SMS Receiver"), 'sms': fields.text('SMS', size=140), 'is_allow_send_sms': fields.related('category_id', 'is_allow_send_sms', type="boolean", string="Allow send SMS?"), 'is_allow_sms_receiver': fields.related('category_id', 'is_allow_send_sms', type="boolean", string="Allow specify sms receiver?"), 'category_id_name': fields.related('category_id', 'name', type="char", string="category name"), 'category_id_is_anonymous_allowed': fields.related('category_id', 'is_anonymous_allowed', type="boolean", string="category is anonymous allowed"), 'category_id_is_allowed_edit_sms_text': fields.related('category_id', 'is_allowed_edit_sms_text', type="boolean", string="category is allowed edit sms text"), 'create_date_display': fields.function(_get_create_date_display, type="datetime", string="Create Date Display", readonly=True), 'write_date_display': fields.function(_get_write_date_display, type="datetime", string="Write Date Display", readonly=True), 'vote_user_ids': fields.many2many('res.users', 'message_vote', 'message_id', 'user_id', string='Votes', help='Users that voted for this message'), 'vote_like': fields.function(_get_vote_count, type="integer", string='Like', multi='vote'), 'vote_unlike': fields.function(_get_vote_count, type='integer', string='Unlike', multi='vote'), } _defaults = { 'fbbm': _default_fbbm, 'department_id': _default_department, 'is_display_name': True, } def onchange_category(self, cr, uid, ids, category_id, context=None): ret = {'value': {}} if category_id: message_category = self.pool.get('message.category').browse(cr, uid, category_id) sms_vals = { 'is_allow_send_sms': message_category.is_allow_send_sms, 'is_allow_sms_receiver': message_category.is_allow_sms_receiver, 'sms_receiver_ids': [x.id for x in message_category.default_sms_receiver_ids], 'category_id_name': message_category.name, 'category_id_is_anonymous_allowed': message_category.is_anonymous_allowed, 'category_id_is_allowed_edit_sms_text': message_category.is_allowed_edit_sms_text, } ret['value'].update(sms_vals) return ret #abandon def onchange_name(self, cr, uid, ids, name, sms, context=None): ret = {'value': {}} if not sms: name_vals = { 'sms': name, } if not len(ids): ret['value'].update(name_vals) return ret def create(self, cr, uid, vals, context=None): if context is None: context = {'mail_create_nolog': True} else: context.update({'mail_create_nolog': True}) if self._log_access is True: if not vals['category_id_is_allowed_edit_sms_text']: vals['sms'] = vals['name'] mid = super(Message, self).create(cr, uid, vals, context) sms = self.pool.get('sms.sms') message = self.pool.get('message.message').browse(cr, uid, mid, context=context) if message.is_allow_send_sms: to = ','.join( [rid.mobile_phone.strip() for rid in message.sms_receiver_ids if rid.mobile_phone and rid.mobile_phone.strip()]) if to: content = message.sms and message.category_id.name + ':' + message.sms or message.category_id.name + ':' + message.name sid = sms.create(cr, uid, {'to': to, 'content': content, 'model': 'message.message', 'res_id': mid}, context=context) else: mid = super(Message, self).create(cr, uid, vals, context) return mid def write(self, cr, uid, ids, vals, context=None): #get message old position if self._log_access is True: TYPE = [] if not (len(vals) == 1 and 'read_times' in vals.keys()): messages_old = self.pool.get('message.message').browse(cr, 1, ids, context=context) for message_old in messages_old: if message_old.category_id.display_position == 'shortcut': TYPE.append('0') if message_old.category_id.display_position == 'content_left': TYPE.append('1') if message_old.category_id.display_position == 'content_right': TYPE.append('2') TYPE = set(TYPE) super(Message, self).write(cr, uid, ids, vals, context=context) NEW_TYPE = [] #refresh cms page if not (len(vals) == 1 and 'read_times' in vals.keys()): #get message new position messages = self.pool.get('message.message').browse(cr, 1, ids, context=context) for message in messages: if message.category_id.display_position == 'shortcut': NEW_TYPE.append('0') if message.category_id.display_position == 'content_left': NEW_TYPE.append('1') if message.category_id.display_position == 'content_right': NEW_TYPE.append('2') NEW_TYPE = set(NEW_TYPE) #if old and new position is different refresh both. for v in (TYPE | NEW_TYPE): fresh_old = CMSFresh(v) fresh_old.start() else: super(Message, self).write(cr, uid, ids, vals, context=context) return True def unlink(self, cr, uid, ids, context=None): #get old position TYPE = [] messages_old = self.pool.get('message.message').browse(cr, 1, ids, context=context) for message_old in messages_old: if message_old.category_id.display_position == 'shortcut': TYPE.append('0') if message_old.category_id.display_position == 'content_left': TYPE.append('1') if message_old.category_id.display_position == 'content_right': TYPE.append('2') super(Message, self).unlink(cr, uid, ids, context=None) TYPE = set(TYPE) #refresh for v in TYPE: fresh_old = CMSFresh(v) fresh_old.start() return True def vote_like(self, cr, uid, user_id, message_id, context=None): message_vote_obj = self.pool.get('message.vote') message = self.read(cr, SUPERUSER_ID, int(message_id), ['vote_user_ids'], context=context) new_has_voted = not (user_id in message.get('vote_user_ids')) if new_has_voted: message_vote_obj.create(cr, SUPERUSER_ID, {'message_id': message.get('id'), 'user_id': user_id, 'up': True}) else: self.write(cr, SUPERUSER_ID, [message.get('id')], {'vote_user_ids': [(3, user_id)]}, context=context) message_vote_obj.create(cr, SUPERUSER_ID, {'message_id': message.get('id'), 'user_id': user_id, 'up': True}) return new_has_voted or False def vote_unlike(self, cr, uid, user_id, message_id, context=None): message_vote_obj = self.pool.get('message.vote') message = self.read(cr, SUPERUSER_ID, int(message_id), ['vote_user_ids'], context=context) new_has_voted = not (user_id in message.get('vote_user_ids')) if new_has_voted: message_vote_obj.create(cr, SUPERUSER_ID, {'message_id': message.get('id'), 'user_id': user_id, 'up': False}) else: self.write(cr, SUPERUSER_ID, [message.get('id')], {'vote_user_ids': [(3, user_id)]}, context=context) message_vote_obj.create(cr, SUPERUSER_ID, {'message_id': message.get('id'), 'user_id': user_id, 'up': False}) return new_has_voted or False
('file','File'), ('parser','Parser'), ],'Template source', select=True), 'parser_def': fields.text('Parser Definition'), 'parser_loc':fields.char('Parser location', size=128, help="Path to the parser location. Beginning of the path must be start with the module name!\nLike this: {module name}/{path to the parser.py file}"), 'parser_state':fields.selection([ ('default',_('Default')), ('def',_('Definition')), ('loc',_('Location')), ],'State of Parser', select=True), 'in_format': fields.selection(_get_in_mimetypes, 'Template Mime-type'), 'out_format':fields.many2one('report.mimetypes', 'Output Mime-type'), 'report_sxw_content': fields.function(_report_content, fnct_inv=_report_content_inv, method=True, type='binary', string='SXW content',), 'active':fields.boolean('Active', help='Disables the report if unchecked.'), 'report_wizard':fields.boolean('Report Wizard'), 'copies': fields.integer('Number of Copies'), 'fallback_false':fields.boolean('Disable Format Fallback'), 'xml_id': fields.function(_get_xml_id, type='char', size=128, string="XML ID", method=True, help="ID of the report defined in xml file"), 'extras': fields.function(_get_extras, method=True, type='char', size='256', string='Extra options'), 'deferred':fields.selection([ ('off',_('Off')), ('adaptive',_('Adaptive')), ],'Deferred', help='Deferred (aka Batch) reporting, for reporting on large amount of data.'), 'deferred_limit': fields.integer('Deferred Records Limit', help='Records limit at which you are invited to start the deferred process.'), 'replace_report_id':fields.many2one('ir.actions.report.xml', 'Replace Report'), 'wizard_id':fields.many2one('ir.actions.act_window', 'Wizard Action'), }
class session (osv.Model): _name = 'openacademy.session' _inherit = ['mail.thread', 'ir.needaction_mixin'] def compute_available_seats(self, seats, attendee_ids): if seats == 0 or len(attendee_ids) > seats: return 0.0 else: return 100.0 - (float(len(attendee_ids)) / seats * 100) def get_available_seats(self, cr, uid, ids, field, arg, context={}): res = {} sessions = self.browse(cr, uid, ids, context=context) for session in sessions: res[session.id] = self.compute_available_seats(session.seats, session.attendee_ids) return res def onchange_seats(self, cr, uid, ids, seats, attendee_ids, context={}): res = { 'value': { 'available_seats': self.compute_available_seats(seats, attendee_ids) } } if seats < 0: res['warning'] = { 'title': _('Warning: wrong value'), 'message': _('The seats number cannot be negative.') } elif seats < len(attendee_ids): res['warning'] = { 'title': _('Warning: wrong value'), 'message': _('There is not enough seats for everyone.') } return res def _compute_end_date(self, cr, uid, ids, fields, arg, context={}): res = {} for session in self.browse(cr, uid, ids, context=context): if session.start_date and session.duration: start_date = datetime.strptime(session.start_date, "%Y-%m-%d") duration = timedelta(days=(session.duration-1)) end_date = start_date + duration res[session.id] = end_date.strftime('%Y-%m-%d') else: res[session.id] = session.start_date return res def _set_end_date(self, cr, uid, id, field, value, arg, context={}): session = self.browse(cr, uid, id, context=context) if session.start_date and value: start_date = datetime.strptime(session.start_date, "%Y-%m-%d") end_date = datetime.strptime(value[:10], "%Y-%m-%d") duration = end_date - start_date self.write(cr, uid, id, {'duration': (duration.days + 1)}, context=context) def _compute_hours(self, cr, uid, ids, fields, arg, context={}): res = {} for session in self.browse(cr, uid, ids, context=context): res[session.id] = (session.duration * 24 if session.duration else 0) """ if session.duration: res[session.id] = session.duration * 24 else: res[session.id] = 0 """ return res def _compute_attendee_count(self, cr, uid, ids, fields, arg, context={}): res = {} for session in self.browse(cr, uid, ids, context=context): res[session.id] = len(session.attendee_ids) return res def _set_hours(self, cr, uid, id, field, value, arg, context={}): if value: self.write(cr, uid, id, {'duration':(value/24)}, context=context) def action_draft(self, cr, uid, ids, context={}): return self.write(cr, uid, ids, {'state': 'draft'}, context=context) def action_confirmed(self, cr, uid, ids, context={}): return self.write(cr, uid, ids, {'state': 'confirmed'}, context=context) def action_done(self, cr, uid, ids, context={}): return self.write(cr, uid, ids, {'state': 'done'}, context=context) _columns = { 'name': fields.char(string="Name", size=128, required=True, translate=True), 'start_date': fields.date(string="Start date"), 'duration': fields.float(string="Duration", digits=(6,2), help="Session durantion in days"), 'seats': fields.integer(string="Number of seats"), 'instructor_id':fields.many2one('res.partner', string="Instructor", ondelete="set null", domain="['|',('instructor','=',True),('category_id.name','in',['Teacher level 1', 'Teacher level 2'])]"), 'course_id': fields.many2one('openacademy.course', string="Course", ondelete="cascade"), 'attendee_ids': fields.one2many('openacademy.attendee', 'session_id', string="Attendees"), 'available_seats': fields.function(get_available_seats, type="float", string="Available Seats (%)", readonly=True), 'active': fields.boolean(string="Active", help="Uncheck this to deactivate this session. Beware, it will not appear anymore in the session list."), 'end_date': fields.function(_compute_end_date, fnct_inv=_set_end_date, type="date", string="End date"), 'hours': fields.function(_compute_hours, fnct_inv=_set_hours, type="float", string="Hours"), 'attendee_count': fields.function(_compute_attendee_count, type="integer", string="Attendee Count", store=True), 'color': fields.integer('Color'), 'state': fields.selection([('draft','Draft'),('confirmed','Confirmed'),('done','Done')], string="State"), } _defaults = { 'start_date': fields.date.today, 'active': True, 'state': 'draft', } def _check_instructor_not_in_attendees(self, cr, uid, ids, context={}): for session in self.browse(cr, uid, ids, context=context): #partners = [] #for attendee in session.attendee_ids: # partners.append(attendee.partner_id) partners = [attendee.partner_id for attendee in session.attendee_ids] if session.instructor_id and session.instructor_id in partners: return False return True _constraints = [ (_check_instructor_not_in_attendees, "The instructor cannot be also an attendee.", ['instructor_id', 'attendee_ids']) ]
# Note: This are template style fields. # For nontemplate fields you might add aditional constraints to specific nontemplate fields (eg. company_id). # The chain method from itertools updates fields. Consider using this from within the nontemplate class when adding # new fields. # --- GENERAL SECTION --- 'name': fields.char('Name', required=True), 'description': fields.char('Description'), # This fiscal domain will narrow down the available fiscal attributes / fiscal allocations. # It should help clustering and reducing complexity. Rules are applied *additionally* # preserving the result (reed: taxes applied) of previous iterations. # However it is optional. You can construct more complex "cross-domain" rules leaving it empty. 'fiscal_domain_id': fields.many2one('account.fiscal.domain', 'Fiscal Domain', required=True, select=True), 'is_account_allocation': fields.boolean(u'Account Allocation?'), # --- CRITERION SECTION --- '{0}_country'.format(ATTR_USE_DOM_COMPANY[0]): fields.many2one('res.country', 'Country'), '{0}_state'.format(ATTR_USE_DOM_COMPANY[0]): fields.many2one( 'res.country.state', 'State', domain="[('country_id','=',{0}_country)]".format(ATTR_USE_DOM_COMPANY[0])), 'to_{0}_country'.format(ATTR_USE_DOM_PINVOICE[0]): fields.many2one( 'res.country', 'Country'), 'to_{0}_state'.format(ATTR_USE_DOM_PINVOICE[0]): fields.many2one( 'res.country.state', 'State', domain="[('country_id','=',to_{0}_country)]".format(ATTR_USE_DOM_PINVOICE[0])), 'to_{0}_country'.format(ATTR_USE_DOM_PSHIPPER[0]): fields.many2one( 'res.country', 'Country'), 'to_{0}_state'.format(ATTR_USE_DOM_PSHIPPER[0]): fields.many2one(
class res_partner(osv.osv): _inherit = 'res.partner' _description = 'Client' def check_client(self,cr,uid,ids,fields, arg,context={}): res = {} return res for o in self.browse(cr,uid,ids): res[o.id] = False def check_client2(self,cr,uid,ids,fields, arg,context={}): res={} for o in self.browse(cr,uid,ids): res[o.id] = { 'blocked_test':False, 'motif_blocage':False, } # ignorer les le client comptoir et les fournisseur if o.id == 1052 or o.customer == False : continue # # Manque information # cin = o.cin or False # ice = o.ice or False # rc = o.rc or False # idfiscal = o.idfiscal or False # if not ((ice and rc and idfiscal) or cin) : # res[o.id] = True # continue # IMPAYE if o.test_impaye: impayee = sum([x.amount for x in o.paiement if x.x_courant !='non' and x.date_impaye != False]) or False if impayee : res[o.id] = { 'blocked_test':True, 'motif_blocage':u"Existance d'impayé", } continue # LIMITE DE CREDIT. if o.test_limite_credit and o.credit_limit >= 0: u" Total des commandes confirmées " orders = self.pool['sale.order'].search_read(cr,1,[('partner_id','=',o.id),('shipped','=',False),('invoiced','=',False),('state','not in',('draft','cancel','done'))],['amount_total','picking_ids']) picking_ids= [x['picking_ids'] for x in orders] picking_ids = reduce(lambda x,y:x+y, picking_ids,[]) livraison_partiel = sum(self.pool['stock.picking'].browse(cr,1,picking_ids).filtered(lambda x:x.state !='cancel').mapped('total_cde')) or 0 order_total = sum([x['amount_total'] for x in orders] ) or 0 - livraison_partiel # Pas de prsie en charge des commandes le 13/12/2018 # order_total = 0 u" Total de BL non facturés " bls = self.pool['stock.picking'].search_read(cr,1,[('state','=','done'),('invoice_state','!=','invoiced'),'|',('partner_id','=',o.id),('partner_dropship_id','=',o.id)],['total_cde']) bls_total = sum([x['total_cde'] for x in bls]) or 0 u" Total des factures Brouillon " invoice_draft = self.pool['account.invoice'].search_read(cr,1,[('partner_id','=',o.id),('state','=','draft')],['amount_total'],context=context) invoice_draft_total = sum([x['amount_total'] for x in invoice_draft]) or 0 u" Total des réglements non encaissés " non_encaissee = sum([x.amount for x in o.paiement if x.x_courant != 'non' and x.date_encaissement == False and x.journal_id.type=='bank']) or 0.0 u" Avances et réglements non lettrés " avance = sum([x.writeoff_amount for x in o.paiement if x.x_courant != 'non' and (x.journal_id.type == 'cash' or x.date_encaissement != False) ]) or 0.0 u" limite de crédit + avances > crédit + Cds + Bls + factures brouillon + Reglements non encaissés" if (o.credit_limit + avance - (o.credit + non_encaissee + order_total + bls_total + invoice_draft_total )) <= 1.0 : res[o.id] = { 'blocked_test':True, 'motif_blocage':u"Dépassement de limite de crédit", } continue # DATE D'ECHEANCES if o.test_echeance: date_echeance = dt.now() + relativedelta(days=-60) fact_ech = self.pool['account.invoice'].search(cr,1,[('partner_id','=',o.id),'|',('state','=','draft'),('residual','!=',0) ,'|',('date_invoice','<',date_echeance),'&',('date_invoice','=',False),('create_date','<',Fields.Datetime.to_string(date_echeance))]) bl_ech = self.pool['stock.picking'].search(cr,1,[('invoice_state','!=','invoiced'),('state','=','done'),'|',('partner_id','=',o.id),('partner_dropship_id','=',o.id) ,'|',('date_done','<',Fields.Datetime.to_string(date_echeance)),'&',('date_done','=',False),('create_date','<',Fields.Datetime.to_string(date_echeance))]) if fact_ech or bl_ech : res[o.id] = { 'blocked_test':True, 'motif_blocage':u"Au moins une facture ou un BL dépasse 60 jours de délai de réglement", } continue # REGELEMENT NON REMIS AU BANQUE A LA DATE D'ECHEANCE if o.test_reglement: non_remis = sum([x.amount for x in o.paiement if x.x_courant !='non' and x.journal_id.type == 'bank' and x.date_remise == False and x.date < Fields.Datetime.to_string(dt.now())]) or False if non_remis : res[o.id] = { 'blocked_test':True, 'motif_blocage':u" Au moins un réglement échu non remis en banque", } continue # les client Non vérifié seront bloqué pour vente crédit if o.verifie==False: res[o.id] = { 'blocked_test':True, 'motif_blocage': u"Ce client n'est pas vérifié!" } continue return res or False _store = { 'account.voucher': ( lambda s,c,u,i,x:s.browse(c,u,i,x).partner_id.ids,None,10), 'account.invoice': ( lambda s,c,u,i,x: [inv.partner_id.id for inv in s.browse(c,u,i,x).filtered(lambda z:z.type in ('out_invoice','out_refund'))],None,10), 'sale.order': ( lambda s,c,u,i,x:s.browse(c,u,i,x).partner_id.ids,None,10), 'stock.picking': ( lambda s,c,u,i,x:s.browse(c,u,i,x).partner_id.ids,None,10), 'res.partner': (lambda s,c,u,i,x:i,None,10), } _columns = { 'rc': fields.char(u'Registre de Commerce',oldname="RC"), 'idfiscal': fields.char(u'ID. Fiscal',oldname="IF"), 'tp': fields.char(u'Taxe professionnelle',oldname="TP"), 'ice': fields.char(u"Identifient Commun d'enreprise",oldname="ICE"), 'cin': fields.char(u"Carte d'Identite Nationale",oldname="CIN"), 'zone': fields.many2many('res.partner.zone','client_zone_rel','id_client','id_zone', string="Zones"), 'blocked': fields.function(check_client, string=u"Bloqué ?", type="boolean"), 'paiement': fields.one2many('account.voucher','partner_id','Paiement', domain=[('journal_id.type', 'in', ['bank', 'cash']), ('type','=','receipt')]), 'order_policy': fields.selection([('picking', u'Crédit'),('prepaid', u'Comptant')], u'Type de facturation', required=True,default="picking"), 'agence' : fields.many2many('res.agence','res_partner_agence_rel','partner_id','agence_id',string="Agence/client"), 'picking_ids': fields.one2many('stock.picking','partner_id','Livraisons'), 'blocked_test': fields.function(check_client2, string=u"Bloqué2 ?", type="boolean",multi="all",store=_store), 'motif_blocage': fields.function(check_client2, string="Motif de blocage",type="text",multi="all",store=_store), 'test_limite_credit': fields.boolean(u"Blocage sur limite de crédit ",default=True), 'test_impaye': fields.boolean(u"Blocage sur impayé ",default=True), 'test_echeance': fields.boolean(u"Blocage sur dépassement d''echéance ",default=True), 'test_reglement': fields.boolean(u"Blocage sur état de réglements ",default=True), 'region_stat': fields.many2one("res.partner.zone",u"Région du client"), 'verifie': fields.boolean(u"Ce client est vérifié pour gestion de risque automatique"), } _sql_constraints = [ ('unique_rc', 'unique(rc)', u"RC doit être unique!"), ('unique_if', 'unique(idfiscal)', u"IF doit être unique!"), ('unique_tp', 'unique(tp)', u"TP doit être unique!"), ('unique_ice', 'unique(ice)', u"ICE doit être unique!"), ('unique_cin', 'unique(cin)', u"CIN doit être unique!"), ('unique_name', 'unique(name)', u"Name doit être unique!"), ('unique_ref', 'unique(ref)', u"Référence du client doit être unique!"), ] def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100): if not args: args = [] ids = [] if len(name) > 1: ids = self.search(cr, user, [('ref', 'ilike', name)] + args, limit=limit, context=context) search_domain = [('name', operator, name)] if ids: search_domain.append(('id', 'not in', ids)) ids.extend(self.search(cr, user, search_domain + args, limit=limit, context=context)) locations = self.name_get(cr, user, ids, context) return sorted(locations, key=lambda (id, name): ids.index(id))
class Partner(osv.osv): '''Partner''' _inherit = 'res.partner' def _get_partner_id(self, cr, uid, ids, context=None): member_line_obj = self.pool.get('membership.membership_line') res_obj = self.pool.get('res.partner') data_inv = member_line_obj.browse(cr, uid, ids, context=context) list_partner = [] for data in data_inv: list_partner.append(data.partner.id) ids2 = list_partner while ids2: ids2 = res_obj.search(cr, uid, [('associate_member', 'in', ids2)], context=context) list_partner += ids2 return list_partner def _get_invoice_partner(self, cr, uid, ids, context=None): inv_obj = self.pool.get('account.invoice') res_obj = self.pool.get('res.partner') data_inv = inv_obj.browse(cr, uid, ids, context=context) list_partner = [] for data in data_inv: list_partner.append(data.partner_id.id) ids2 = list_partner while ids2: ids2 = res_obj.search(cr, uid, [('associate_member', 'in', ids2)], context=context) list_partner += ids2 return list_partner def _cron_update_membership(self, cr, uid, context=None): partner_ids = self.search(cr, uid, [('membership_state', '=', 'paid')], context=context) if partner_ids: self._store_set_values(cr, uid, partner_ids, ['membership_state'], context=context) def _membership_state(self, cr, uid, ids, name, args, context=None): """This Function return Membership State For Given Partner. @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @param ids: List of Partner IDs @param name: Field Name @param context: A standard dictionary for contextual values @param return: Dictionary of Membership state Value """ res = {} for id in ids: res[id] = 'none' today = time.strftime('%Y-%m-%d') for id in ids: partner_data = self.browse(cr, uid, id, context=context) if partner_data.membership_cancel and today > partner_data.membership_cancel: res[id] = 'free' if partner_data.free_member else 'canceled' continue if partner_data.membership_stop and today > partner_data.membership_stop: res[id] = 'free' if partner_data.free_member else 'old' continue s = 4 if partner_data.member_lines: for mline in partner_data.member_lines: if mline.date_to >= today and mline.date_from <= today: if mline.account_invoice_line and mline.account_invoice_line.invoice_id: mstate = mline.account_invoice_line.invoice_id.state if mstate == 'paid': s = 0 inv = mline.account_invoice_line.invoice_id for payment in inv.payment_ids: if payment.invoice.type == 'out_refund': s = 2 break elif mstate == 'open' and s != 0: s = 1 elif mstate == 'cancel' and s != 0 and s != 1: s = 2 elif (mstate == 'draft' or mstate == 'proforma') and s != 0 and s != 1: s = 3 if s == 4: for mline in partner_data.member_lines: if mline.date_from < today and mline.date_to < today and mline.date_from <= mline.date_to and mline.account_invoice_line and mline.account_invoice_line.invoice_id.state == 'paid': s = 5 else: s = 6 if s == 0: res[id] = 'paid' elif s == 1: res[id] = 'invoiced' elif s == 2: res[id] = 'canceled' elif s == 3: res[id] = 'waiting' elif s == 5: res[id] = 'old' elif s == 6: res[id] = 'none' if partner_data.free_member and s != 0: res[id] = 'free' if partner_data.associate_member: res_state = self._membership_state( cr, uid, [partner_data.associate_member.id], name, args, context=context) res[id] = res_state[partner_data.associate_member.id] return res def _membership_date(self, cr, uid, ids, name, args, context=None): """Return date of membership""" name = name[0] res = {} member_line_obj = self.pool.get('membership.membership_line') for partner in self.browse(cr, uid, ids, context=context): if partner.associate_member: partner_id = partner.associate_member.id else: partner_id = partner.id res[partner.id] = { 'membership_start': False, 'membership_stop': False, 'membership_cancel': False } if name == 'membership_start': line_id = member_line_obj.search(cr, uid, [('partner', '=', partner_id), ('date_cancel', '=', False)], limit=1, order='date_from', context=context) if line_id: res[partner.id]['membership_start'] = member_line_obj.read( cr, uid, [line_id[0]], ['date_from'], context=context)[0]['date_from'] if name == 'membership_stop': line_id1 = member_line_obj.search( cr, uid, [('partner', '=', partner_id), ('date_cancel', '=', False)], limit=1, order='date_to desc', context=context) if line_id1: res[partner.id]['membership_stop'] = member_line_obj.read( cr, uid, [line_id1[0]], ['date_to'], context=context)[0]['date_to'] if name == 'membership_cancel': if partner.membership_state == 'canceled': line_id2 = member_line_obj.search( cr, uid, [('partner', '=', partner.id)], limit=1, order='date_cancel', context=context) if line_id2: res[partner. id]['membership_cancel'] = member_line_obj.read( cr, uid, [line_id2[0]], ['date_cancel'], context=context)[0]['date_cancel'] return res def _get_partners(self, cr, uid, ids, context=None): ids2 = ids while ids2: ids2 = self.search(cr, uid, [('associate_member', 'in', ids2)], context=context) ids += ids2 return ids def __get_membership_state(self, *args, **kwargs): return self._membership_state(*args, **kwargs) _columns = { 'associate_member': fields.many2one( 'res.partner', 'Associate Member', help= "A member with whom you want to associate your membership.It will consider the membership state of the associated member." ), 'member_lines': fields.one2many('membership.membership_line', 'partner', 'Membership'), 'free_member': fields.boolean('Free Member', help="Select if you want to give free membership."), 'membership_amount': fields.float('Membership Amount', digits=(16, 2), help='The price negotiated by the partner'), 'membership_state': fields.function( __get_membership_state, string='Current Membership Status', type='selection', selection=STATE, store={ 'account.invoice': (_get_invoice_partner, ['state'], 10), 'membership.membership_line': (_get_partner_id, ['state'], 10), 'res.partner': (_get_partners, ['free_member', 'membership_state', 'associate_member'], 10) }, help='It indicates the membership state.\n' '-Non Member: A partner who has not applied for any membership.\n' '-Cancelled Member: A member who has cancelled his membership.\n' '-Old Member: A member whose membership date has expired.\n' '-Waiting Member: A member who has applied for the membership and whose invoice is going to be created.\n' '-Invoiced Member: A member whose invoice has been created.\n' '-Paying member: A member who has paid the membership fee.'), 'membership_start': fields.function(_membership_date, multi='membeship_start', string='Membership Start Date', type='date', store={ 'account.invoice': (_get_invoice_partner, ['state'], 10), 'membership.membership_line': ( _get_partner_id, ['state'], 10, ), 'res.partner': (_get_partners, [ 'free_member', 'membership_state', 'associate_member' ], 10) }, help="Date from which membership becomes active."), 'membership_stop': fields.function( _membership_date, string='Membership End Date', type='date', multi='membership_stop', store={ 'account.invoice': (_get_invoice_partner, ['state'], 10), 'membership.membership_line': (_get_partner_id, ['state'], 10), 'res.partner': (_get_partners, ['free_member', 'membership_state', 'associate_member'], 10) }, help="Date until which membership remains active."), 'membership_cancel': fields.function( _membership_date, string='Cancel Membership Date', type='date', multi='membership_cancel', store={ 'account.invoice': (_get_invoice_partner, ['state'], 11), 'membership.membership_line': (_get_partner_id, ['state'], 10), 'res.partner': (_get_partners, ['free_member', 'membership_state', 'associate_member'], 10) }, help="Date on which membership has been cancelled"), } _defaults = { 'free_member': False, 'membership_cancel': False, } def _check_recursion(self, cr, uid, ids, context=None): """Check Recursive for Associated Members. """ level = 100 while len(ids): cr.execute( 'SELECT DISTINCT associate_member FROM res_partner 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 _constraints = [(_check_recursion, 'Error ! You cannot create recursive associated members.', ['associate_member'])] def create_membership_invoice(self, cr, uid, ids, product_id=None, datas=None, context=None): """ Create Customer Invoice of Membership for partners. @param datas: datas has dictionary value which consist Id of Membership product and Cost Amount of Membership. datas = {'membership_product_id': None, 'amount': None} """ invoice_obj = self.pool.get('account.invoice') invoice_line_obj = self.pool.get('account.invoice.line') invoice_tax_obj = self.pool.get('account.invoice.tax') product_id = product_id or datas.get('membership_product_id', False) amount = datas.get('amount', 0.0) invoice_list = [] if type(ids) in ( int, long, ): ids = [ids] for partner in self.browse(cr, uid, ids, context=context): account_id = partner.property_account_receivable and partner.property_account_receivable.id or False fpos_id = partner.property_account_position and partner.property_account_position.id or False addr = self.address_get(cr, uid, [partner.id], ['invoice']) if partner.free_member: raise osv.except_osv(_('Error!'), _("Partner is a free Member.")) if not addr.get('invoice', False): raise osv.except_osv( _('Error!'), _("Partner doesn't have an address to make the invoice.")) quantity = 1 line_value = { 'product_id': product_id, } line_dict = invoice_line_obj.product_id_change(cr, uid, {}, product_id, False, quantity, '', 'out_invoice', partner.id, fpos_id, price_unit=amount, context=context) line_value.update(line_dict['value']) line_value['price_unit'] = amount if line_value.get('invoice_line_tax_id', False): tax_tab = [(6, 0, line_value['invoice_line_tax_id'])] line_value['invoice_line_tax_id'] = tax_tab invoice_id = invoice_obj.create( cr, uid, { 'partner_id': partner.id, 'account_id': account_id, 'fiscal_position': fpos_id or False }, context=context) line_value['invoice_id'] = invoice_id invoice_line_obj.create(cr, uid, line_value, context=context) invoice_list.append(invoice_id) if line_value['invoice_line_tax_id']: tax_value = invoice_tax_obj.compute(cr, uid, invoice_id).values() for tax in tax_value: invoice_tax_obj.create(cr, uid, tax, context=context) #recompute the membership_state of those partners self.pool.get('res.partner').write(cr, uid, ids, {}) return invoice_list
class sale_order(orm.Model): _inherit = "sale.order" def default_get(self, cr, uid, fields, context=None): context = context or self.pool['res.users'].context_get(cr, uid) # sale_order_obj = self.pool['sale.order'] # sale_order_line_obj = self.pool['sale.order.line'] res = super(sale_order, self).default_get(cr, uid, fields, context=context) if not res.get('shop_id', False): shop_ids = self.pool['sale.order'].search(cr, uid, [], limit=1, context=context) if shop_ids: res['shop_id'] = shop_ids[0] if not res.get('section_id', False): section_ids = self.pool['crm.case.section'].search(cr, uid, [('user_id', '=', uid)], context=context) if section_ids: res['section_id'] = section_ids[0] return res def service_only(self, cr, uid, ids, context): context = context or self.pool['res.users'].context_get(cr, uid) service = True if not isinstance(ids, (list, tuple)): ids = [ids] for order in self.browse(cr, uid, ids, context): if order.order_line: for order_line in order.order_line: if order_line.product_id and order_line.product_id.type != 'service': return False elif not service: return False return True def hook_sale_state(self, cr, uid, orders, vals, context): context = context or self.pool['res.users'].context_get(cr, uid) # function call if change state the sale order return True def adaptative_function(self, cr, uid, ids, vals, context): context = context or self.pool['res.users'].context_get(cr, uid) if not isinstance(ids, (list, tuple)): ids = [ids] if vals.get('section_id', False) or vals.get('carrier_id', False) or vals.get('payment_term'): for order in self.browse(cr, uid, ids, context): partner_vals = {} if not order.partner_id.section_id: partner_vals['section_id'] = vals.get('section_id') if not order.partner_id.property_delivery_carrier: partner_vals['property_delivery_carrier'] = vals.get('carrier_id') if not order.partner_id.property_payment_term: partner_vals['property_payment_term'] = vals.get('payment_term') if partner_vals: self.pool['res.partner'].write(cr, uid, [order.partner_id.id], partner_vals, context) return True def create(self, cr, uid, vals, context=None): context = context or self.pool['res.users'].context_get(cr, uid) ids = super(sale_order, self).create(cr, uid, vals, context=context) self.adaptative_function(cr, uid, ids, vals, context) return ids def write(self, cr, uid, ids, vals, context=None): if context is None: context = self.pool['res.users'].context_get(cr, uid) if not isinstance(ids, (list, tuple)): ids = [ids] orders = self.browse(cr, uid, ids, context) self.adaptative_function(cr, uid, ids, vals, context) if vals.get('state', False): self.hook_sale_state(cr, uid, orders, vals, context) return super(sale_order, self).write(cr, uid, ids, vals, context=context) def action_wait(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): company = self.pool['res.users'].browse(cr, uid, uid).company_id if self.service_only(cr, uid, [order.id], context) and order.order_policy and order.order_policy == 'picking': if company.auto_order_policy: order.write({'order_policy': 'manual'}) else: raise orm.except_orm(_('Warning'), _( "You can't create an order with Invoicing being based on Picking if there are only service products")) else: if company.auto_order_policy: default = self.default_get(cr, uid, ['order_policy'], context) order.write({'order_policy': default.get('order_policy')}) return super(sale_order, self).action_wait(cr, uid, ids, context) def copy(self, cr, uid, ids, default, context=None): context = context or self.pool['res.users'].context_get(cr, uid) default.update( { 'tech_validation': False, 'manager_validation': False, 'customer_validation': False, 'email_sent_validation': False, 'supervisor_validation': False, 'date_order': fields.date.context_today, } ) default.update(self.default_get(cr, uid, ['order_policy', 'picking_policy', 'invoice_quantity'], context)) return super(sale_order, self).copy(cr, uid, ids, default, context) def action_cancel_draft(self, cr, uid, ids, *args): self.write(cr, uid, ids, { 'tech_validation': False, 'manager_validation': False, 'customer_validation': False, 'email_sent_validation': False, 'supervisor_validation': False, }) super(sale_order, self).action_cancel_draft(cr, uid, ids, *args) return True def onchange_invoice_type_id(self, cr, uid, ids, invoice_type_id, context=None): if context is None: context = self.pool['res.users'].context_get(cr, uid) res = {} if invoice_type_id: invoice_type_obj = self.pool['sale_journal.invoice.type'] invoice_type = invoice_type_obj.browse(cr, uid, invoice_type_id, context) if invoice_type.invoicing_method == 'grouped': res['order_policy'] = 'picking' return {'value': res} def onchange_partner_id(self, cr, uid, ids, part, context=None): context = context or self.pool['res.users'].context_get(cr, uid) res = super(sale_order, self).onchange_partner_id(cr, uid, ids, part) if res.get('value', False) and part: if not res['value'].get('fiscal_position', False): company_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.id company = self.pool['res.company'].browse(cr, uid, company_id, context) if company.default_property_account_position: res['value']['fiscal_position'] = company.default_property_account_position and company.default_property_account_position.id return res def _credit_limit(self, cr, uid, ids, field_name, arg, context): context = context or self.pool['res.users'].context_get(cr, uid) res = dict.fromkeys(ids, 0.0) for order in self.browse(cr, uid, ids, context=context): if order.order_policy == 'prepaid': res[order.id] = 0 continue partner = order.partner_id credit = partner.credit # We sum from all the sale orders that are aproved, the sale order lines that are not yet invoiced order_obj = self.pool['sale.order'] approved_invoices_ids = order_obj.search(cr, uid, [('partner_id', '=', partner.id), ('state', 'not in', ['draft', 'cancel', 'done'])], context=context) approved_invoices_amount = 0.0 for orders in order_obj.browse(cr, uid, approved_invoices_ids, context=context): for order_line in orders.order_line: if not order_line.invoiced: approved_invoices_amount += order_line.price_subtotal # We sum from all the invoices that are in draft the total amount invoice_obj = self.pool['account.invoice'] draft_invoices_ids = invoice_obj.search(cr, uid, [('partner_id', '=', partner.id), ('state', '=', 'draft')], context=context) draft_invoices_amount = 0.0 for invoice in invoice_obj.browse(cr, uid, draft_invoices_ids, context=context): draft_invoices_amount += invoice.amount_total available_credit = partner.credit_limit - credit - approved_invoices_amount - draft_invoices_amount res[order.id] = available_credit - order.amount_total return res def partner_overdue_check(self, cr, uid, company, partner, context): # return True if there are same overdue payment account_move_line_obj = self.pool['account.move.line'] overdue_date = (datetime.today() - relativedelta(days=company.date_max_overdue or 0.0)).strftime(DEFAULT_SERVER_DATE_FORMAT) account_move_ids = account_move_line_obj.search(cr, uid, [ ('partner_id', '=', partner.id), ('account_id.type', 'in', ['receivable', 'payable']), ('stored_invoice_id', '!=', False), ('reconcile_id', '=', False), ('date_maturity', '<', overdue_date)], context=context) if account_move_ids: return True return False def check_limit(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context=context): if order.credit_limit < 0 and order.company_id and order.company_id.check_credit_limit: title = _(u'Credit Over Limit') msg = _(u'Is not possible to confirm because customer exceed the credit limit. \n Is Possible change the Order Policy \"Pay Before Delivery\" \n on tab \"Other Information\"') raise orm.except_orm(_(title), _(msg)) return False if order.visible_minimum and order.sale_order_minimun > order.amount_untaxed: if order.shop_id.user_allow_minimun_id and order.shop_id.user_allow_minimun_id.id == uid: # if user can validate return True # test if on line there are the product if order.shop_id.product_allow_minimun_id: for line in order.order_line: if line.product_id and line.product_id == order.shop_id.product_allow_minimun_id: return True title = _(u'Minimum Amount Billable') if order.shop_id.user_allow_minimun_id: msg = _(u'Is not possible to confirm because is not reached the minimum billable {amount} {currency} \n Only {user} can do it').format(amount=order.sale_order_minimun, currency=order.pricelist_id.currency_id.symbol, user=order.shop_id.user_allow_minimun_id.name) else: msg = _(u'Is not possible to confirm because is not reached the minimum billable {amount} {currency}').format(amount=order.sale_order_minimun, currency=order.pricelist_id.currency_id.symbol) if order.shop_id.product_allow_minimun_id: msg += _(u'\n\n or add the product \'{product}\'').format(product=order.shop_id.product_allow_minimun_id.name_get()[0][1]) raise orm.except_orm(_(title), _(msg)) return False if order.company_id and order.company_id.check_overdue: if self.partner_overdue_check(cr, uid, order.company_id, order.partner_id, context): title = _(u'Overdue Limit') msg = _(u'Is not possible to confirm because customer have a overdue payment') raise orm.except_orm(_(title), _(msg)) return False return True def name_get(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) if not len(ids): return [] res = [] for sale in self.browse(cr, uid, ids, context=context): name = u'[{sale_name}] {partner_name}'.format(sale_name=sale.name, partner_name=sale.partner_id.name) res.append((sale.id, name)) return res def name_search(self, cr, uid, name='', args=None, operator='ilike', context=None, limit=10): context = context or self.pool['res.users'].context_get(cr, uid) if not args: args = [] if name: ids = self.search(cr, uid, [('name', operator, name)] + args, limit=limit, context=context) if not len(ids): ids = self.search(cr, uid, [('partner_id', 'ilike', name)] + args, limit=limit, context=context) ids = list(set(ids)) if not len(ids): ptrn = re.compile('(\[(.*?)\])') res = ptrn.search(name) if res: ids = self.search( cr, uid, [('name', '=', res.group(2))] + args, limit=limit, context=context) else: ids = self.search(cr, uid, args, limit=limit, context=context) result = self.name_get(cr, uid, ids, context=context) return result def __init__(self, registry, cr): """ Add state "Suspended" """ super(sale_order, self).__init__(registry, cr) options = [('wait_technical_validation', _('Technical Validation')), ('wait_manager_validation', _('Manager Validation')), ('send_to_customer', _('Send To Customer')), ('wait_customer_validation', _('Customer Validation')), ('wait_supervisor_validation', _('Supervisor Validation'))] type_selection = self._columns['state'].selection for option in options: if option not in type_selection: type_selection.append(option) def _get_shop_id(self, cr, uid, context): shop_ids = self.pool['sale.shop'].search(cr, uid, [], context=context, limit=1) return shop_ids and shop_ids[0] or False _columns = { 'create_uid': fields.many2one('res.users', 'Created by', readonly=True), 'credit_limit': fields.function(_credit_limit, string="Remaining Credit Limit", type='float', readonly=True, method=True), 'sale_order_minimun': fields.related('shop_id', 'sale_order_minimun', type='float', string='Minimun Invoice', store=False, readonly=True), 'visible_minimum': fields.related('shop_id', 'sale_order_have_minimum', type='boolean', string=_('Minimun Amount'), store=False, readonly=True), 'visible_credit_limit': fields.related('company_id', 'check_credit_limit', type='boolean', string=_('Fido Residuo Visibile'), store=False, readonly=True), 'validity': fields.date('Validity'), 'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', readonly=True, states={ 'draft': [('readonly', False)], 'wait_technical_validation': [('readonly', False)], 'wait_manager_validation': [('readonly', False)]} ), 'project_id': fields.many2one('account.analytic.account', 'Contract/Analytic Account', readonly=True, states={ 'draft': [('readonly', False)], 'wait_technical_validation': [('readonly', False)], 'wait_manager_validation': [('readonly', False)], 'send_to_customer': [('readonly', False)], 'wait_customer_validation': [('readonly', False)], }, help="The analytic account related to a sales order."), 'required_tech_validation': fields.related('company_id', 'need_tech_validation', type='boolean', string=_('Required Technical Validation'), store=False, readonly=True), 'need_tech_validation': fields.boolean("Technical Validation", readonly=True), 'tech_validation': fields.boolean("Tech Validated ?", readonly=True), 'required_manager_validation': fields.related('company_id', 'need_manager_validation', type='boolean', string=_('Required Manager Validation'), store=False, readonly=True), 'need_manager_validation': fields.boolean("Manager Validation", readonly=True), 'manager_validation': fields.boolean("Manager Validated ?", readonly=True), 'email_sent_validation': fields.boolean("Email Sent to Customer ?", readonly=True), 'customer_validation': fields.boolean("Customer Validated ?", readonly=True), # A validation after customer confirmation: 'required_supervisor_validation': fields.related('company_id', 'need_supervisor_validation', type='boolean', string=_('Required Supervisor Validation'), store=False, readonly=True), 'skip_supervisor_validation_onstandard_product': fields.related('company_id', 'skip_supervisor_validation_onstandard_product', type='boolean', string=_( 'Skip Supervisor Verification if there are only standard product'), store=False, readonly=True), 'supervisor_validation': fields.boolean(_("Supervisor Validated?"), readonly=True), 'product_id': fields.related('order_line', 'product_id', type='many2one', relation='product.product', string='Product'), 'revision_note': fields.char('Reason', size=256, select=True), 'lost_reason_id': fields.many2one('crm.lost.reason', string='Lost Reason'), 'last_revision_note': fields.related('sale_version_id', 'revision_note', type='char', string="Last Revision Note", store=True), } _defaults = { 'need_tech_validation': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.need_tech_validation, 'need_manager_validation': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.need_manager_validation, 'skip_supervisor_validation_onstandard_product': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.skip_supervisor_validation_onstandard_product, 'required_tech_validation': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.need_tech_validation, 'required_manager_validation': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.need_manager_validation, 'required_supervisor_validation': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context).company_id.need_supervisor_validation, 'validity': lambda self, cr, uid, context: (datetime.today() + relativedelta(days=self.pool['res.users'].browse(cr, uid, uid, context).company_id.default_sale_order_validity or 0.0)).strftime(DEFAULT_SERVER_DATE_FORMAT), 'shop_id': lambda self, cr, uid, context: self._get_shop_id(cr, uid, context), } def action_reopen(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) result = super(sale_order, self).action_reopen(cr, uid, ids, context=context) for order in self.browse(cr, uid, ids, context): if order.state == 'draft': self.write(cr, uid, ids, { 'tech_validation': False, 'manager_validation': False, 'email_sent_validation': False, 'customer_validation': False, }, context) return result def check_direct_order_confirm(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): if order.state == 'draft' and order.pricelist_id and order.pricelist_id.contract: return True else: return False def check_tech_validation(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): if order.shop_id.user_tech_validation_id: if order.shop_id.user_tech_validation_id.id == uid: return True else: title = _('Technical Validation') msg = _(u"It's not possible to confirm, for shop {shop} only user '{user}' can do it".format(shop=order.shop_id.name, user=order.shop_id.user_tech_validation_id.name)) raise orm.except_orm(_(title), _(msg)) return False else: return True def check_manager_validation(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): if order.shop_id.user_manager_validation_id: if order.shop_id.user_manager_validation_id.id == uid: return True else: title = _('Manager Validation') msg = _(u"It's not possible to confirm, for shop {shop} only user '{user}' can do it".format(shop=order.shop_id.name, user=order.shop_id.user_manager_validation_id.name)) raise orm.except_orm(_(title), _(msg)) return False else: return True def check_supervisor_validation(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): if order.shop_id.user_supervisor_validation_id: if order.shop_id.user_supervisor_validation_id.id == uid: return True else: title = _('Supervisor Validation') msg = _(u"It's not possible to confirm, for shop {shop} only user '{user}' can do it".format(shop=order.shop_id.name, user=order.shop_id.user_supervisor_validation_id.name)) raise orm.except_orm(_(title), _(msg)) return False else: return True def required_tech_validation(self, order): if order.company_id.tech_validation_if_no_product: for line in order.order_line: if not line.product_id: order.write({'need_tech_validation': True}) return True return False def check_discount(self, order): if order.company_id.enable_discount_validation: max_discount = order.company_id.max_discount for line in order.order_line: if line.discount > max_discount: order.write({'need_manager_validation': True}) return True return False def action_validate(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): if not order.partner_id.validate and order.company_id.enable_partner_validation: title = _('Partner To Validate') msg = _("It's not possible to confirm because customer must be validated") raise orm.except_orm(_(title), _(msg)) return False if order.need_tech_validation and not order.tech_validation or self.required_tech_validation(order): vals = { 'state': 'wait_technical_validation', } elif self.check_discount(order): vals = { 'state': 'wait_manager_validation', } elif order.company_id.enable_margin_validation and order.amount_untaxed and (order.margin / order.amount_untaxed) * 100 < order.company_id.minimum_margin and not order.manager_validation: vals = { 'state': 'wait_manager_validation', } elif order.need_manager_validation and not order.manager_validation: vals = { 'state': 'wait_manager_validation', } elif not order.email_sent_validation: vals = { 'state': 'send_to_customer', } elif not order.customer_validation: vals = { 'state': 'wait_customer_validation', } elif order.required_supervisor_validation and not order.supervisor_validation: vals = { 'state': 'wait_supervisor_validation', } else: vals = { 'state': 'draft', 'tech_validation': False, 'manager_validation': False, 'customer_validation': False, 'email_sent_validation': False, 'supervisor_validation': False } order.write(vals) return True def check_validate(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) for order in self.browse(cr, uid, ids, context): res = True if order.need_tech_validation and not order.tech_validation: res = False elif order.need_manager_validation and not order.manager_validation: res = False elif order.required_supervisor_validation and not order.supervisor_validation: if order.skip_supervisor_validation_onstandard_product: for line in order.order_line: if line.product_id and line.product_id.is_kit: return False res = True else: res = False return res and order.email_sent_validation and order.customer_validation return True def check_direct_confirm(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) if self.check_limit(cr, uid, ids, context): for order in self.browse(cr, uid, ids, context): values = { 'state': 'wait_customer_validation', 'customer_validation': True } if order.need_tech_validation: values['tech_validation'] = True if (order.company_id.enable_margin_validation and order.amount_untaxed and (order.margin / order.amount_untaxed) < order.company_id.minimum_margin) or order.need_manager_validation: values['manager_validation'] = True if order.required_supervisor_validation: values['supervisor_validation'] = True self.write(cr, uid, [order.id], values, context) return self.action_validate(cr, uid, ids, context) else: return False def copy(self, cr, uid, ids, default={}, context=None): default = default or {} context = context or self.pool['res.users'].context_get(cr, uid) default.update({ 'validity': (datetime.today() + relativedelta(days=self.pool['res.users'].browse(cr, uid, uid, context).company_id.default_sale_order_validity or 0.0)).strftime(DEFAULT_SERVER_DATE_FORMAT), 'tech_validation': False, 'manager_validation': False, 'customer_validation': False, 'email_sent_validation': False, 'supervisor_validation': False, 'lost_reason_id': False }) return super(sale_order, self).copy(cr, uid, ids, default, context=context)
class hr_applicant(osv.Model): _name = "hr.applicant" _description = "Applicant" _order = "id desc" _inherit = ['mail.thread', 'ir.needaction_mixin'] _track = { 'stage_id': { # this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages 'hr_recruitment.mt_applicant_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id. sequence <= 1, 'hr_recruitment.mt_applicant_stage_changed': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id. sequence > 1, }, } _mail_mass_mailing = _('Applicants') def _get_default_department_id(self, cr, uid, context=None): """ Gives default department by checking if present in the context """ return (self._resolve_department_id_from_context( cr, uid, context=context) or False) def _get_default_stage_id(self, cr, uid, context=None): """ Gives default stage_id """ department_id = self._get_default_department_id(cr, uid, context=context) return self.stage_find(cr, uid, [], department_id, [('fold', '=', False)], context=context) def _resolve_department_id_from_context(self, cr, uid, context=None): """ Returns ID of department based on the value of 'default_department_id' context key, or None if it cannot be resolved to a single department. """ if context is None: context = {} if type(context.get('default_department_id')) in (int, long): return context.get('default_department_id') if isinstance(context.get('default_department_id'), basestring): department_name = context['default_department_id'] department_ids = self.pool.get('hr.department').name_search( cr, uid, name=department_name, context=context) if len(department_ids) == 1: return int(department_ids[0][0]) return None def _get_default_company_id(self, cr, uid, department_id=None, context=None): company_id = False if department_id: department = self.pool['hr.department'].browse(cr, uid, department_id, context=context) company_id = department.company_id.id if department and department.company_id else False if not company_id: company_id = self.pool['res.company']._company_default_get( cr, uid, 'hr.applicant', context=context) return company_id def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): access_rights_uid = access_rights_uid or uid stage_obj = self.pool.get('hr.recruitment.stage') order = stage_obj._order # lame hack to allow reverting search, should just work in the trivial case if read_group_order == 'stage_id desc': order = "%s desc" % order # retrieve section_id from the context and write the domain # - ('id', 'in', 'ids'): add columns that should be present # - OR ('department_id', '=', False), ('fold', '=', False): add default columns that are not folded # - OR ('department_id', 'in', department_id), ('fold', '=', False) if department_id: add department columns that are not folded department_id = self._resolve_department_id_from_context( cr, uid, context=context) search_domain = [] if department_id: search_domain += ['|', ('department_id', '=', department_id)] search_domain += [ '|', ('id', 'in', ids), ('department_id', '=', False) ] stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) # restore order of the search result.sort( lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) fold = {} for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): fold[stage.id] = stage.fold or False return result, fold def _compute_day(self, cr, uid, ids, fields, args, context=None): """ @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @param ids: List of Openday’s IDs @return: difference between current date and log date @param context: A standard dictionary for contextual values """ res = {} for issue in self.browse(cr, uid, ids, context=context): for field in fields: res[issue.id] = {} duration = 0 ans = False hours = 0 if field in ['day_open']: if issue.date_open: date_create = datetime.strptime( issue.create_date, "%Y-%m-%d %H:%M:%S") date_open = datetime.strptime(issue.date_open, "%Y-%m-%d %H:%M:%S") ans = date_open - date_create elif field in ['day_close']: if issue.date_closed: date_create = datetime.strptime( issue.create_date, "%Y-%m-%d %H:%M:%S") date_close = datetime.strptime(issue.date_closed, "%Y-%m-%d %H:%M:%S") ans = date_close - date_create if ans: duration = float(ans.days) res[issue.id][field] = abs(float(duration)) return res def _get_attachment_number(self, cr, uid, ids, fields, args, context=None): res = dict.fromkeys(ids, 0) for app_id in ids: res[app_id] = self.pool['ir.attachment'].search_count( cr, uid, [('res_model', '=', 'hr.applicant'), ('res_id', '=', app_id)], context=context) return res _columns = { 'name': fields.char('Subject / Application Name', size=128, required=True), 'active': fields.boolean('Active', help="If the active field is set to false, it will allow you to hide the case without removing it."), 'description': fields.text('Description'), 'email_from': fields.char('Email', size=128, help="These people will receive email."), 'email_cc': fields.text('Watchers Emails', size=252, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), 'probability': fields.float('Probability'), 'partner_id': fields.many2one('res.partner', 'Contact'), 'create_date': fields.datetime('Creation Date', readonly=True, select=True), 'write_date': fields.datetime('Update Date', readonly=True), 'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage', track_visibility='onchange', domain="['|', ('department_id', '=', department_id), ('department_id', '=', False)]"), 'last_stage_id': fields.many2one('hr.recruitment.stage', 'Last Stage', help='Stage of the applicant before being in the current stage. Used for lost cases analysis.'), 'categ_ids': fields.many2many('hr.applicant_category', string='Tags'), 'company_id': fields.many2one('res.company', 'Company'), 'user_id': fields.many2one('res.users', 'Responsible', track_visibility='onchange'), 'date_closed': fields.datetime('Closed', readonly=True, select=True), 'date_open': fields.datetime('Assigned', readonly=True, select=True), 'date_last_stage_update': fields.datetime('Last Stage Update', select=True), 'date_action': fields.date('Next Action Date'), 'title_action': fields.char('Next Action', size=64), 'priority': fields.selection(AVAILABLE_PRIORITIES, 'Appreciation'), 'job_id': fields.many2one('hr.job', 'Applied Job'), 'salary_proposed_extra': fields.char('Proposed Salary Extra', size=100, help="Salary Proposed by the Organisation, extra advantages"), 'salary_expected_extra': fields.char('Expected Salary Extra', size=100, help="Salary Expected by Applicant, extra advantages"), 'salary_proposed': fields.float('Proposed Salary', help="Salary Proposed by the Organisation"), 'salary_expected': fields.float('Expected Salary', help="Salary Expected by Applicant"), 'availability': fields.integer('Availability', help="The number of days in which the applicant will be available to start working"), 'partner_name': fields.char("Applicant's Name", size=64), 'partner_phone': fields.char('Phone', size=32), 'partner_mobile': fields.char('Mobile', size=32), 'type_id': fields.many2one('hr.recruitment.degree', 'Degree'), 'department_id': fields.many2one('hr.department', 'Department'), 'survey': fields.related('job_id', 'survey_id', type='many2one', relation='survey.survey', string='Survey'), 'response_id': fields.many2one('survey.user_input', "Response", ondelete='set null', oldname="response"), 'reference': fields.char('Referred By', size=128), 'source_id': fields.many2one('hr.recruitment.source', 'Source'), 'day_open': fields.function(_compute_day, string='Days to Open', \ multi='day_open', type="float", store=True), 'day_close': fields.function(_compute_day, string='Days to Close', \ multi='day_close', type="float", store=True), 'color': fields.integer('Color Index'), 'emp_id': fields.many2one('hr.employee', string='Employee', help='Employee linked to the applicant.'), 'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True), 'attachment_number': fields.function(_get_attachment_number, string='Number of Attachments', type="integer"), } _defaults = { 'active': lambda *a: 1, 'user_id': lambda s, cr, uid, c: uid, 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c), 'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c), 'company_id': lambda s, cr, uid, c: s._get_default_company_id( cr, uid, s._get_default_department_id(cr, uid, c), c), 'color': 0, 'date_last_stage_update': fields.datetime.now, } _group_by_full = {'stage_id': _read_group_stage_ids} def onchange_job(self, cr, uid, ids, job_id=False, context=None): department_id = False if job_id: job_record = self.pool.get('hr.job').browse(cr, uid, job_id, context=context) department_id = job_record and job_record.department_id and job_record.department_id.id or False user_id = job_record and job_record.user_id and job_record.user_id.id or False return {'value': {'department_id': department_id, 'user_id': user_id}} def onchange_department_id(self, cr, uid, ids, department_id=False, stage_id=False, context=None): if not stage_id: stage_id = self.stage_find(cr, uid, [], department_id, [('fold', '=', False)], context=context) return {'value': {'stage_id': stage_id}} def onchange_partner_id(self, cr, uid, ids, partner_id, context=None): data = { 'partner_phone': False, 'partner_mobile': False, 'email_from': False } if partner_id: addr = self.pool.get('res.partner').browse(cr, uid, partner_id, context) data.update({ 'partner_phone': addr.phone, 'partner_mobile': addr.mobile, 'email_from': addr.email }) return {'value': data} def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None): """ Override of the base.stage method Parameter of the stage search taken from the lead: - department_id: if set, stages must belong to this section or be a default case """ if isinstance(cases, (int, long)): cases = self.browse(cr, uid, cases, context=context) # collect all section_ids department_ids = [] if section_id: department_ids.append(section_id) for case in cases: if case.department_id: department_ids.append(case.department_id.id) # OR all section_ids and OR with case_default search_domain = [] if department_ids: search_domain += ['|', ('department_id', 'in', department_ids)] search_domain.append(('department_id', '=', False)) # AND with the domain in parameter search_domain += list(domain) # perform search, return the first found stage_ids = self.pool.get('hr.recruitment.stage').search( cr, uid, search_domain, order=order, context=context) if stage_ids: return stage_ids[0] return False def action_makeMeeting(self, cr, uid, ids, context=None): """ This opens Meeting's calendar view to schedule meeting on current applicant @return: Dictionary value for created Meeting view """ applicant = self.browse(cr, uid, ids[0], context) applicant_ids = [] if applicant.partner_id: applicant_ids.append(applicant.partner_id.id) if applicant.department_id and applicant.department_id.manager_id and applicant.department_id.manager_id.user_id and applicant.department_id.manager_id.user_id.partner_id: applicant_ids.append( applicant.department_id.manager_id.user_id.partner_id.id) category = self.pool.get('ir.model.data').get_object( cr, uid, 'hr_recruitment', 'categ_meet_interview', context) res = self.pool.get('ir.actions.act_window').for_xml_id( cr, uid, 'calendar', 'action_calendar_event', context) res['context'] = { 'default_partner_ids': applicant_ids, 'default_user_id': uid, 'default_name': applicant.name, 'default_categ_ids': category and [category.id] or False, } return res def action_start_survey(self, cr, uid, ids, context=None): context = context if context else {} applicant = self.browse(cr, uid, ids, context=context)[0] survey_obj = self.pool.get('survey.survey') response_obj = self.pool.get('survey.user_input') # create a response and link it to this applicant if not applicant.response_id: response_id = response_obj.create( cr, uid, { 'survey_id': applicant.survey.id, 'partner_id': applicant.partner_id.id }, context=context) self.write(cr, uid, ids[0], {'response_id': response_id}, context=context) else: response_id = applicant.response_id.id # grab the token of the response and start surveying response = response_obj.browse(cr, uid, response_id, context=context) context.update({'survey_token': response.token}) return survey_obj.action_start_survey(cr, uid, [applicant.survey.id], context=context) def action_print_survey(self, cr, uid, ids, context=None): """ If response is available then print this response otherwise print survey form (print template of the survey) """ context = context if context else {} applicant = self.browse(cr, uid, ids, context=context)[0] survey_obj = self.pool.get('survey.survey') response_obj = self.pool.get('survey.user_input') if not applicant.response_id: return survey_obj.action_print_survey(cr, uid, [applicant.survey.id], context=context) else: response = response_obj.browse(cr, uid, applicant.response_id.id, context=context) context.update({'survey_token': response.token}) return survey_obj.action_print_survey(cr, uid, [applicant.survey.id], context=context) def action_get_attachment_tree_view(self, cr, uid, ids, context=None): model, action_id = self.pool.get('ir.model.data').get_object_reference( cr, uid, 'base', 'action_attachment') action = self.pool.get(model).read(cr, uid, action_id, context=context) action['context'] = { 'default_res_model': self._name, 'default_res_id': ids[0] } action['domain'] = str( ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)]) return action def message_get_suggested_recipients(self, cr, uid, ids, context=None): recipients = super(hr_applicant, self).message_get_suggested_recipients( cr, uid, ids, context=context) for applicant in self.browse(cr, uid, ids, context=context): if applicant.partner_id: self._message_add_suggested_recipient( cr, uid, recipients, applicant, partner=applicant.partner_id, reason=_('Contact')) elif applicant.email_from: self._message_add_suggested_recipient( cr, uid, recipients, applicant, email=applicant.email_from, reason=_('Contact Email')) return recipients def message_new(self, cr, uid, msg, custom_values=None, context=None): """ Overrides mail_thread message_new that is called by the mailgateway through message_process. This override updates the document according to the email. """ if custom_values is None: custom_values = {} val = msg.get('from').split('<')[0] defaults = { 'name': msg.get('subject') or _("No Subject"), 'partner_name': val, 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), 'user_id': False, 'partner_id': msg.get('author_id', False), } if msg.get('priority'): defaults['priority'] = msg.get('priority') defaults.update(custom_values) return super(hr_applicant, self).message_new(cr, uid, msg, custom_values=defaults, context=context) def create(self, cr, uid, vals, context=None): if context is None: context = {} context['mail_create_nolog'] = True if vals.get( 'department_id') and not context.get('default_department_id'): context['default_department_id'] = vals.get('department_id') if vals.get('job_id') or context.get('default_job_id'): job_id = vals.get('job_id') or context.get('default_job_id') vals.update( self.onchange_job(cr, uid, [], job_id, context=context)['value']) obj_id = super(hr_applicant, self).create(cr, uid, vals, context=context) applicant = self.browse(cr, uid, obj_id, context=context) if applicant.job_id: name = applicant.partner_name if applicant.partner_name else applicant.name self.pool['hr.job'].message_post( cr, uid, [applicant.job_id.id], body=_('New application from %s') % name, subtype="hr_recruitment.mt_job_applicant_new", context=context) return obj_id def write(self, cr, uid, ids, vals, context=None): if isinstance(ids, (int, long)): ids = [ids] res = True # user_id change: update date_open if vals.get('user_id'): vals['date_open'] = fields.datetime.now() # stage_id: track last stage before update if 'stage_id' in vals: vals['date_last_stage_update'] = fields.datetime.now() for applicant in self.browse(cr, uid, ids, context=None): vals['last_stage_id'] = applicant.stage_id.id res = super(hr_applicant, self).write(cr, uid, [applicant.id], vals, context=context) else: res = super(hr_applicant, self).write(cr, uid, ids, vals, context=context) # post processing: if job changed, post a message on the job if vals.get('job_id'): for applicant in self.browse(cr, uid, ids, context=None): name = applicant.partner_name if applicant.partner_name else applicant.name self.pool['hr.job'].message_post( cr, uid, [vals['job_id']], body=_('New application from %s') % name, subtype="hr_recruitment.mt_job_applicant_new", context=context) # post processing: if stage changed, post a message in the chatter if vals.get('stage_id'): stage = self.pool['hr.recruitment.stage'].browse(cr, uid, vals['stage_id'], context=context) if stage.template_id: # TDENOTE: probably factorize me in a message_post_with_template generic method FIXME compose_ctx = dict(context, active_ids=ids) compose_id = self.pool['mail.compose.message'].create( cr, uid, { 'model': self._name, 'composition_mode': 'mass_mail', 'template_id': stage.template_id.id, 'same_thread': True, 'post': True, 'notify': True, }, context=compose_ctx) self.pool['mail.compose.message'].write( cr, uid, [compose_id], self.pool['mail.compose.message'].onchange_template_id( cr, uid, [compose_id], stage.template_id.id, 'mass_mail', self._name, False, context=compose_ctx)['value'], context=compose_ctx) self.pool['mail.compose.message'].send_mail( cr, uid, [compose_id], context=compose_ctx) return res def create_employee_from_applicant(self, cr, uid, ids, context=None): """ Create an hr.employee from the hr.applicants """ if context is None: context = {} hr_employee = self.pool.get('hr.employee') model_data = self.pool.get('ir.model.data') act_window = self.pool.get('ir.actions.act_window') emp_id = False for applicant in self.browse(cr, uid, ids, context=context): address_id = contact_name = False if applicant.partner_id: address_id = self.pool.get('res.partner').address_get( cr, uid, [applicant.partner_id.id], ['contact'])['contact'] contact_name = self.pool.get('res.partner').name_get( cr, uid, [applicant.partner_id.id])[0][1] if applicant.job_id and (applicant.partner_name or contact_name): applicant.job_id.write( { 'no_of_hired_employee': applicant.job_id.no_of_hired_employee + 1 }, context=context) create_ctx = dict(context, mail_broadcast=True) emp_id = hr_employee.create( cr, uid, { 'name': applicant.partner_name or contact_name, 'job_id': applicant.job_id.id, 'address_home_id': address_id, 'department_id': applicant.department_id.id or False, 'address_id': applicant.company_id and applicant.company_id.partner_id and applicant.company_id.partner_id.id or False, 'work_email': applicant.department_id and applicant.department_id.company_id and applicant.department_id.company_id.email or False, 'work_phone': applicant.department_id and applicant.department_id.company_id and applicant.department_id.company_id.phone or False, }, context=create_ctx) self.write(cr, uid, [applicant.id], {'emp_id': emp_id}, context=context) self.pool['hr.job'].message_post( cr, uid, [applicant.job_id.id], body=_('New Employee %s Hired') % applicant.partner_name if applicant.partner_name else applicant.name, subtype="hr_recruitment.mt_job_applicant_hired", context=context) else: raise osv.except_osv( _('Warning!'), _('You must define an Applied Job and a Contact Name for this applicant.' )) action_model, action_id = model_data.get_object_reference( cr, uid, 'hr', 'open_view_employee_list') dict_act_window = act_window.read(cr, uid, [action_id], [])[0] if emp_id: dict_act_window['res_id'] = emp_id dict_act_window['view_mode'] = 'form,tree' return dict_act_window def get_empty_list_help(self, cr, uid, help, context=None): context['empty_list_help_model'] = 'hr.job' context['empty_list_help_id'] = context.get('default_job_id', None) context['empty_list_help_document_name'] = _("job applicants") return super(hr_applicant, self).get_empty_list_help(cr, uid, help, context=context)
#This program is distributed in the hope that it will be useful, # #but WITHOUT ANY WARRANTY; without even the implied warranty of # #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # #GNU General Public License for more details. # # # #You should have received a copy of the GNU General Public License # #along with this program. If not, see <http://www.gnu.org/licenses/>. # ################################################################################# from openerp import pooler from openerp.osv import fields, osv TAX_CODE_COLUMNS = { 'domain':fields.char('Domain', size=32, help="This field is only used if you develop your own module allowing developers to create specific taxes in a custom domain."), 'tax_discount': fields.boolean('Discount this Tax in Prince', help="Mark it for (ICMS, PIS, COFINS and others taxes included)."), } TAX_DEFAULTS = { 'base_reduction': 0, 'amount_mva': 0, } class account_tax_code_template(osv.osv): """ Add fields used to define some brazilian taxes """ _inherit = 'account.tax.code.template' _columns = TAX_CODE_COLUMNS def generate_tax_code(self, cr, uid, tax_code_root_id, company_id, context=None): """This function generates the tax codes from the templates of tax
class stock_fill_inventory(osv.TransientModel): _inherit = "stock.fill.inventory" def _default_location(self, cr, uid, ids, context=None): try: location = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock') with mute_logger('openerp.osv.orm'): location.check_access_rule('read', context=context) location_id = location.id except (ValueError, orm.except_orm), e: return False return location_id or False _columns = { 'category_id': fields.many2one('product.category', 'Category', required=False), 'display_with_zero': fields.boolean('Add with zero quantity', required=False) } def fill_inventory(self, cr, uid, ids, context=None): """ To Import stock inventory according to products available in the selected locations. @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param ids: the ID or list of IDs if we want more than one @param context: A standard dictionary @return: """ if context is None: context = {} _digits_id = self.pool.get('decimal.precision').search(cr, uid, [('name','=','Product Unit of Measure')])
break return {} def on_change_intervall(self, cr, uid, id, interval): ###Function that will update the cron ###freqeuence self.pool.get("currency.rate.update").save_cron(cr, uid, {"interval_type": interval}) compagnies = self.search(cr, uid, []) for comp in compagnies: self.write(cr, uid, comp, {"interval_type": interval}) return {} _inherit = "res.company" _columns = { ### activate the currency update "auto_currency_up": fields.boolean("Automatical update of the currency this company"), "services_to_use": fields.one2many("currency.rate.update.service", "company_id", "Currency update services"), ###predifine cron frequence "interval_type": fields.selection( [("days", "Day(s)"), ("weeks", "Week(s)"), ("months", "Month(s)")], "Currency update frequence", help="""changing this value will also affect other compagnies""", ), ###function field that allows to know the ###mutli company currency implementation "multi_company_currency_enable": fields.function( _multi_curr_enable, method=True, type="boolean", string="Multi company currency",
'amount_total': 0.0, } val = val1 = 0.0 cur = order.pricelist_id.currency_id for line in order.order_line: val1 += line.price_subtotal val += self._amount_line_tax(cr, uid, line, context=context) res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val) res[order.id]['amount_untaxed'] = cur_obj.round(cr, uid, cur, val1) res[order.id]['amount_total'] = res[order.id]['amount_untaxed'] + res[order.id]['amount_tax'] return res _columns = { 'tot_volume': fields.function(_tot_volume, string='Total Volume', type='float'), 'avg_volume': fields.function(_avg_volume, string='Average Volume', type='float'), 'calculated' : fields.boolean('Amount Calculated'), 'amount_untaxed': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Untaxed Amount', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line', 'calculated'], 10), 'sale.order.line': (_get_order, ['purchase_price','price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), }, multi='sums', help="The amount without tax.", track_visibility='always'), 'amount_tax': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Taxes', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), 'sale.order.line': (_get_order, ['purchase_price', 'price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), }, multi='sums', help="The tax amount."), 'amount_total': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Total', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line', 'calculated'], 10),
qty = line.qty2_2 uom = line.uom_id.factor_inv qty_all = round(qty*uom,3) result[line.id] = qty_all return result _columns = { 'name' : fields.char('Name', required=True), 'partner_id' : fields.many2one('res.partner','Principle',domain=[('supplier','=',True)],required=True), 'product_id' : fields.many2one('product.product','Bonus Product'), 'qty_2' : fields.float('Bonus Qty2', digits_compute=dp.get_precision('Product Unit of Measure')), 'qty' : fields.function(_qty_all_1,type="float",string='Bonus Qty',digits_compute=dp.get_precision('Product Unit of Measure')), 'uom_id' : fields.many2one('product.uom','UoM',required=True), 'uom_id2' : fields.many2one('product.uom','UoM',required=True), 'value' : fields.float('Price Value',domain=[('is_percent','=',False)]), 'per_product' : fields.boolean('Per Product'), 'persentase' : fields.float('Percent Value', digits_compute= dp.get_precision('Discount'),domain=[('is_percent','=',True)]), 'multi' : fields.boolean('Multiples'), 'is_active' : fields.boolean('Active?'), 'date_from' : fields.date('Start Date', required=True), 'date_to' : fields.date('End Date', required=True), 'condition_ids' : fields.one2many('master.condition','discount_id','Value Condition'), 'condition2_ids' : fields.one2many('master.condition2','discount_id','Product Condition'), 'condition3_ids' : fields.one2many('master.condition3','discount_id','Product Condition 2'), 'condition4_ids' : fields.one2many('master.condition4','discount_id','Product Condition 3'), 'condition5_ids' : fields.one2many('master.condition5','discount_id','Product Condition 4'), 'group_price_ids' : fields.many2many('res.partner.category', id1='discount_id', id2='category_id', string='Group Price Category'), 'is_percent' : fields.boolean('Is Percent'), 'is_flat' : fields.boolean('Flat'), 'type' :fields.selection([('regular','Regular Discount'),('promo','Promo Discount'),('extra','Extra Discount'),('cash','Cash Discount'),('mix','Mix Discount')],string='Type Discount',required=True), 'min_qty_product' : fields.float('Min. Product Item',digits_compute=dp.get_precision('Product Unit of Measure')),
# # #You should have received a copy of the GNU Affero General Public License # #along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################### from openerp.osv import orm, fields FISCAL_POSITION_COLUMNS = { 'fiscal_category_id': fields.many2one('l10n_br_account.fiscal.category', 'Categoria Fiscal'), 'type': fields.selection([('input', 'Entrada'), ('output', 'Saida')], 'Tipo'), 'type_tax_use': fields.selection([('sale', 'Sale'), ('purchase', 'Purchase'), ('all', 'All')], 'Tax Application'), 'inv_copy_note': fields.boolean('Copiar Observação na Nota Fiscal')} class AccountFiscalPositionTemplate(orm.Model): _inherit = 'account.fiscal.position.template' _columns = FISCAL_POSITION_COLUMNS def onchange_type(self, cr, uid, ids, type=False, context=None): type_tax = {'input': 'purhcase', 'output': 'sale'} return {'value': {'type_tax_use': type_tax.get(type, 'all'), 'tax_ids': False}} def onchange_fiscal_category_id(self, cr, uid, ids, fiscal_category_id=False, context=None): if fiscal_category_id: fc_fields = self.pool.get('l10n_br_account.fiscal.category').read(
class procurement_rule(osv.osv): _inherit = 'procurement.rule' def _get_action(self, cr, uid, context=None): result = super(procurement_rule, self)._get_action(cr, uid, context=context) return result + [('move', _('Move From Another Location'))] def _get_rules(self, cr, uid, ids, context=None): res = [] for route in self.browse(cr, uid, ids): res += [x.id for x in route.pull_ids] return res _columns = { 'location_id': fields.many2one('stock.location', 'Procurement Location'), 'location_src_id': fields.many2one('stock.location', 'Source Location', help="Source location is action=move"), 'route_id': fields.many2one('stock.location.route', 'Route', help="If route_id is False, the rule is global"), 'procure_method': fields.selection( [('make_to_stock', 'Take From Stock'), ('make_to_order', 'Create Procurement')], 'Move Supply Method', required=True, help= """Determines the procurement method of the stock move that will be generated: whether it will need to 'take from the available stock' in its source location or needs to ignore its stock and create a procurement over there.""" ), 'route_sequence': fields.related('route_id', 'sequence', string='Route Sequence', store={ 'stock.location.route': (_get_rules, ['sequence'], 10), 'procurement.rule': (lambda self, cr, uid, ids, c={}: ids, ['route_id'], 10), }), 'picking_type_id': fields.many2one( 'stock.picking.type', 'Picking Type', help= "Picking Type determines the way the picking should be shown in the view, reports, ..." ), 'delay': fields.integer('Number of Days'), 'partner_address_id': fields.many2one('res.partner', 'Partner Address'), 'propagate': fields.boolean( 'Propagate cancel and split', help= 'If checked, when the previous move of the move (which was generated by a next procurement) is cancelled or split, the move generated by this move will too' ), 'warehouse_id': fields.many2one('stock.warehouse', 'Served Warehouse', help='The warehouse this rule is for'), 'propagate_warehouse_id': fields.many2one( 'stock.warehouse', 'Warehouse to Propagate', help= "The warehouse to propagate on the created move/procurement, which can be different of the warehouse this rule is for (e.g for resupplying rules from another warehouse)" ), } _defaults = { 'procure_method': 'make_to_stock', 'propagate': True, 'delay': 0, }
class crm_lead(base_stage, format_address, osv.osv): """ CRM Lead Case """ _name = "crm.lead" _description = "Lead/Opportunity" _order = "priority,date_action,id desc" _inherit = ['mail.thread', 'ir.needaction_mixin'] _track = { 'state': { 'crm.mt_lead_create': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'new', 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancel', }, 'stage_id': { 'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['new', 'cancel', 'done'], }, } def create(self, cr, uid, vals, context=None): if context is None: context = {} if not vals.get('stage_id'): ctx = context.copy() if vals.get('section_id'): ctx['default_section_id'] = vals['section_id'] if vals.get('type'): ctx['default_type'] = vals['type'] vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx) return super(crm_lead, self).create(cr, uid, vals, context=context) def _get_default_section_id(self, cr, uid, context=None): """ Gives default section by checking if present in the context """ return self._resolve_section_id_from_context(cr, uid, context=context) or False def _get_default_stage_id(self, cr, uid, context=None): """ Gives default stage_id """ section_id = self._get_default_section_id(cr, uid, context=context) return self.stage_find(cr, uid, [], section_id, [('state', '=', 'draft')], context=context) def _resolve_section_id_from_context(self, cr, uid, context=None): """ Returns ID of section based on the value of 'section_id' context key, or None if it cannot be resolved to a single Sales Team. """ if context is None: context = {} if type(context.get('default_section_id')) in (int, long): return context.get('default_section_id') if isinstance(context.get('default_section_id'), basestring): section_name = context['default_section_id'] section_ids = self.pool.get('crm.case.section').name_search(cr, uid, name=section_name, context=context) if len(section_ids) == 1: return int(section_ids[0][0]) return None def _resolve_type_from_context(self, cr, uid, context=None): """ Returns the type (lead or opportunity) from the type context key. Returns None if it cannot be resolved. """ if context is None: context = {} return context.get('default_type') def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): access_rights_uid = access_rights_uid or uid stage_obj = self.pool.get('crm.case.stage') order = stage_obj._order # lame hack to allow reverting search, should just work in the trivial case if read_group_order == 'stage_id desc': order = "%s desc" % order # retrieve section_id from the context and write the domain # - ('id', 'in', 'ids'): add columns that should be present # - OR ('case_default', '=', True), ('fold', '=', False): add default columns that are not folded # - OR ('section_ids', '=', section_id), ('fold', '=', False) if section_id: add section columns that are not folded search_domain = [] section_id = self._resolve_section_id_from_context(cr, uid, context=context) if section_id: search_domain += ['|', ('section_ids', '=', section_id)] search_domain += [('id', 'in', ids)] else: search_domain += ['|', ('id', 'in', ids), ('case_default', '=', True)] # retrieve type from the context (if set: choose 'type' or 'both') type = self._resolve_type_from_context(cr, uid, context=context) if type: search_domain += ['|', ('type', '=', type), ('type', '=', 'both')] # perform search stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) # restore order of the search result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) fold = {} for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): fold[stage.id] = stage.fold or False return result, fold def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): res = super(crm_lead,self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu) if view_type == 'form': res['arch'] = self.fields_view_get_address(cr, user, res['arch'], context=context) return res _group_by_full = { 'stage_id': _read_group_stage_ids } def _compute_day(self, cr, uid, ids, fields, args, context=None): """ :return dict: difference between current date and log date """ cal_obj = self.pool.get('resource.calendar') res_obj = self.pool.get('resource.resource') res = {} for lead in self.browse(cr, uid, ids, context=context): for field in fields: res[lead.id] = {} duration = 0 ans = False if field == 'day_open': if lead.date_open: date_create = datetime.strptime(lead.create_date, "%Y-%m-%d %H:%M:%S") date_open = datetime.strptime(lead.date_open, "%Y-%m-%d %H:%M:%S") ans = date_open - date_create date_until = lead.date_open elif field == 'day_close': if lead.date_closed: date_create = datetime.strptime(lead.create_date, "%Y-%m-%d %H:%M:%S") date_close = datetime.strptime(lead.date_closed, "%Y-%m-%d %H:%M:%S") date_until = lead.date_closed ans = date_close - date_create if ans: resource_id = False if lead.user_id: resource_ids = res_obj.search(cr, uid, [('user_id','=',lead.user_id.id)]) if len(resource_ids): resource_id = resource_ids[0] duration = float(ans.days) if lead.section_id and lead.section_id.resource_calendar_id: duration = float(ans.days) * 24 new_dates = cal_obj.interval_get(cr, uid, lead.section_id.resource_calendar_id and lead.section_id.resource_calendar_id.id or False, datetime.strptime(lead.create_date, '%Y-%m-%d %H:%M:%S'), duration, resource=resource_id ) no_days = [] date_until = datetime.strptime(date_until, '%Y-%m-%d %H:%M:%S') for in_time, out_time in new_dates: if in_time.date not in no_days: no_days.append(in_time.date) if out_time > date_until: break duration = len(no_days) res[lead.id][field] = abs(int(duration)) return res def _history_search(self, cr, uid, obj, name, args, context=None): res = [] msg_obj = self.pool.get('mail.message') message_ids = msg_obj.search(cr, uid, [('email_from','!=',False), ('subject', args[0][1], args[0][2])], context=context) lead_ids = self.search(cr, uid, [('message_ids', 'in', message_ids)], context=context) if lead_ids: return [('id', 'in', lead_ids)] else: return [('id', '=', '0')] _columns = { 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', track_visibility='onchange', select=True, help="Linked partner (optional). Usually created when converting the lead."), 'id': fields.integer('ID', readonly=True), 'name': fields.char('Subject', size=64, required=True, select=1), 'active': fields.boolean('Active', required=False), 'date_action_last': fields.datetime('Last Action', readonly=1), 'date_action_next': fields.datetime('Next Action', readonly=1), 'email_from': fields.char('Email', size=128, help="Email address of the contact", select=1), 'section_id': fields.many2one('crm.case.section', 'Sales Team', select=True, track_visibility='onchange', help='When sending mails, the default email address is taken from the sales team.'), 'create_date': fields.datetime('Creation Date' , readonly=True), 'email_cc': fields.text('Global CC', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), 'description': fields.text('Notes'), 'write_date': fields.datetime('Update Date' , readonly=True), 'categ_ids': fields.many2many('crm.case.categ', 'crm_lead_category_rel', 'lead_id', 'category_id', 'Categories', \ domain="['|',('section_id','=',section_id),('section_id','=',False), ('object_id.model', '=', 'crm.lead')]"), 'type_id': fields.many2one('crm.case.resource.type', 'Campaign', \ domain="['|',('section_id','=',section_id),('section_id','=',False)]", help="From which campaign (seminar, marketing campaign, mass mailing, ...) did this contact come from?"), 'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel (mail, direct, phone, ...)"), 'contact_name': fields.char('Contact Name', size=64), 'partner_name': fields.char("Customer Name", size=64,help='The name of the future partner company that will be created while converting the lead into opportunity', select=1), 'opt_out': fields.boolean('Opt-Out', oldname='optout', help="If opt-out is checked, this contact has refused to receive emails or unsubscribed to a campaign."), 'type':fields.selection([ ('lead','Lead'), ('opportunity','Opportunity'), ],'Type', help="Type is used to separate Leads and Opportunities"), 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), 'date_closed': fields.datetime('Closed', readonly=True), 'stage_id': fields.many2one('crm.case.stage', 'Stage', track_visibility='onchange', domain="['&', '&', ('fold', '=', False), ('section_ids', '=', section_id), '|', ('type', '=', type), ('type', '=', 'both')]"), 'user_id': fields.many2one('res.users', 'Salesperson', select=True, track_visibility='onchange'), 'referred': fields.char('Referred By', size=64), 'date_open': fields.datetime('Opened', readonly=True), 'day_open': fields.function(_compute_day, string='Days to Open', \ multi='day_open', type="float", store=True), 'day_close': fields.function(_compute_day, string='Days to Close', \ multi='day_close', type="float", store=True), 'state': fields.related('stage_id', 'state', type="selection", store=True, selection=crm.AVAILABLE_STATES, string="Status", readonly=True, help='The Status is set to \'Draft\', when a case is created. If the case is in progress the Status is set to \'Open\'. When the case is over, the Status is set to \'Done\'. If the case needs to be reviewed then the Status is set to \'Pending\'.'), # Only used for type opportunity 'probability': fields.float('Success Rate (%)',group_operator="avg"), 'planned_revenue': fields.float('Expected Revenue', track_visibility='always'), 'ref': fields.reference('Reference', selection=crm._links_get, size=128), 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), 'phone': fields.char("Phone", size=64), 'date_deadline': fields.date('Expected Closing', help="Estimate of the date on which the opportunity will be won."), 'date_action': fields.date('Next Action Date', select=True), 'title_action': fields.char('Next Action', size=64), 'color': fields.integer('Color Index'), 'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True), 'partner_address_email': fields.related('partner_id', 'email', type='char', string='Partner Contact Email', readonly=True), 'company_currency': fields.related('company_id', 'currency_id', type='many2one', string='Currency', readonly=True, relation="res.currency"), 'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True), 'user_login': fields.related('user_id', 'login', type='char', string='User Login', readonly=True), # Fields for address, due to separation from crm and res.partner 'street': fields.char('Street', size=128), 'street2': fields.char('Street2', size=128), 'zip': fields.char('Zip', change_default=True, size=24), 'city': fields.char('City', size=128), 'state_id': fields.many2one("res.country.state", 'State'), 'country_id': fields.many2one('res.country', 'Country'), 'phone': fields.char('Phone', size=64), 'fax': fields.char('Fax', size=64), 'mobile': fields.char('Mobile', size=64), 'function': fields.char('Function', size=128), 'title': fields.many2one('res.partner.title', 'Title'), 'company_id': fields.many2one('res.company', 'Company', select=1), 'payment_mode': fields.many2one('crm.payment.mode', 'Payment Mode', \ domain="[('section_id','=',section_id)]"), 'planned_cost': fields.float('Planned Costs'), } _defaults = { 'active': 1, 'type': 'lead', 'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c), 'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c), 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c), 'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c), 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c), 'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], 'color': 0, } _sql_constraints = [ ('check_probability', 'check(probability >= 0 and probability <= 100)', 'The probability of closing the deal should be between 0% and 100%!') ] def onchange_stage_id(self, cr, uid, ids, stage_id, context=None): if not stage_id: return {'value':{}} stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context) if not stage.on_change: return {'value':{}} return {'value':{'probability': stage.probability}} def on_change_partner(self, cr, uid, ids, partner_id, context=None): result = {} values = {} if partner_id: partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) values = { 'partner_name' : partner.name, 'street' : partner.street, 'street2' : partner.street2, 'city' : partner.city, 'state_id' : partner.state_id and partner.state_id.id or False, 'country_id' : partner.country_id and partner.country_id.id or False, 'email_from' : partner.email, 'phone' : partner.phone, 'mobile' : partner.mobile, 'fax' : partner.fax, } return {'value' : values} def _check(self, cr, uid, ids=False, context=None): """ Override of the base.stage method. Function called by the scheduler to process cases for date actions Only works on not done and cancelled cases """ cr.execute('select * from crm_case \ where (date_action_last<%s or date_action_last is null) \ and (date_action_next<=%s or date_action_next is null) \ and state not in (\'cancel\',\'done\')', (time.strftime("%Y-%m-%d %H:%M:%S"), time.strftime('%Y-%m-%d %H:%M:%S'))) ids2 = map(lambda x: x[0], cr.fetchall() or []) cases = self.browse(cr, uid, ids2, context=context) return self._action(cr, uid, cases, False, context=context) def stage_find(self, cr, uid, cases, section_id, domain=None, order='sequence', context=None): """ Override of the base.stage method Parameter of the stage search taken from the lead: - type: stage type must be the same or 'both' - section_id: if set, stages must belong to this section or be a default stage; if not set, stages must be default stages """ if isinstance(cases, (int, long)): cases = self.browse(cr, uid, cases, context=context) # collect all section_ids section_ids = [] types = ['both'] if not cases : type = context.get('default_type') types += [type] if section_id: section_ids.append(section_id) for lead in cases: if lead.section_id: section_ids.append(lead.section_id.id) if lead.type not in types: types.append(lead.type) # OR all section_ids and OR with case_default search_domain = [] if section_ids: search_domain += [('|')] * len(section_ids) for section_id in section_ids: search_domain.append(('section_ids', '=', section_id)) else: search_domain.append(('case_default', '=', True)) # AND with cases types search_domain.append(('type', 'in', types)) # AND with the domain in parameter search_domain += list(domain) # perform search, return the first found stage_ids = self.pool.get('crm.case.stage').search(cr, uid, search_domain, order=order, context=context) if stage_ids: return stage_ids[0] return False def case_cancel(self, cr, uid, ids, context=None): """ Overrides case_cancel from base_stage to set probability """ res = super(crm_lead, self).case_cancel(cr, uid, ids, context=context) self.write(cr, uid, ids, {'probability' : 0.0}, context=context) return res def case_reset(self, cr, uid, ids, context=None): """ Overrides case_reset from base_stage to set probability """ res = super(crm_lead, self).case_reset(cr, uid, ids, context=context) self.write(cr, uid, ids, {'probability': 0.0}, context=context) return res def case_mark_lost(self, cr, uid, ids, context=None): """ Mark the case as lost: state=cancel and probability=0 """ for lead in self.browse(cr, uid, ids): stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 0.0),('on_change','=',True)], context=context) if stage_id: self.case_set(cr, uid, [lead.id], values_to_update={'probability': 0.0}, new_stage_id=stage_id, context=context) return True def case_mark_won(self, cr, uid, ids, context=None): """ Mark the case as won: state=done and probability=100 """ for lead in self.browse(cr, uid, ids): stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 100.0),('on_change','=',True)], context=context) if stage_id: self.case_set(cr, uid, [lead.id], values_to_update={'probability': 100.0}, new_stage_id=stage_id, context=context) return True def set_priority(self, cr, uid, ids, priority): """ Set lead priority """ return self.write(cr, uid, ids, {'priority' : priority}) def set_high_priority(self, cr, uid, ids, context=None): """ Set lead priority to high """ return self.set_priority(cr, uid, ids, '1') def set_normal_priority(self, cr, uid, ids, context=None): """ Set lead priority to normal """ return self.set_priority(cr, uid, ids, '3') def _merge_get_result_type(self, cr, uid, opps, context=None): """ Define the type of the result of the merge. If at least one of the element to merge is an opp, the resulting new element will be an opp. Otherwise it will be a lead. We'll directly use a list of browse records instead of a list of ids for performances' sake: it will spare a second browse of the leads/opps. :param list opps: list of browse records containing the leads/opps to process :return string type: the type of the final element """ for opp in opps: if (opp.type == 'opportunity'): return 'opportunity' return 'lead' def _merge_data(self, cr, uid, ids, oldest, fields, context=None): """ Prepare lead/opp data into a dictionary for merging. Different types of fields are processed in different ways: - text: all the values are concatenated - m2m and o2m: those fields aren't processed - m2o: the first not null value prevails (the other are dropped) - any other type of field: same as m2o :param list ids: list of ids of the leads to process :param list fields: list of leads' fields to process :return dict data: contains the merged values """ opportunities = self.browse(cr, uid, ids, context=context) def _get_first_not_null(attr): for opp in opportunities: if hasattr(opp, attr) and bool(getattr(opp, attr)): return getattr(opp, attr) return False def _get_first_not_null_id(attr): res = _get_first_not_null(attr) return res and res.id or False def _concat_all(attr): return '\n\n'.join(filter(lambda x: x, [getattr(opp, attr) or '' for opp in opportunities if hasattr(opp, attr)])) # Process the fields' values data = {} for field_name in fields: field_info = self._all_columns.get(field_name) if field_info is None: continue field = field_info.column if field._type in ('many2many', 'one2many'): continue elif field._type == 'many2one': data[field_name] = _get_first_not_null_id(field_name) # !! elif field._type == 'text': data[field_name] = _concat_all(field_name) #not lost else: data[field_name] = _get_first_not_null(field_name) #not lost # Define the resulting type ('lead' or 'opportunity') data['type'] = self._merge_get_result_type(cr, uid, opportunities, context) return data def _mail_body(self, cr, uid, lead, fields, title=False, context=None): body = [] if title: body.append("%s\n" % (title)) for field_name in fields: field_info = self._all_columns.get(field_name) if field_info is None: continue field = field_info.column value = '' if field._type == 'selection': if hasattr(field.selection, '__call__'): key = field.selection(self, cr, uid, context=context) else: key = field.selection value = dict(key).get(lead[field_name], lead[field_name]) elif field._type == 'many2one': if lead[field_name]: value = lead[field_name].name_get()[0][1] elif field._type == 'many2many': if lead[field_name]: for val in lead[field_name]: field_value = val.name_get()[0][1] value += field_value + "," else: value = lead[field_name] body.append("%s: %s" % (field.string, value or '')) return "<br/>".join(body + ['<br/>']) def _merge_notify(self, cr, uid, opportunity_id, opportunities, context=None): """ Create a message gathering merged leads/opps information. """ #TOFIX: mail template should be used instead of fix body, subject text details = [] result_type = self._merge_get_result_type(cr, uid, opportunities, context) if result_type == 'lead': merge_message = _('Merged leads') else: merge_message = _('Merged opportunities') subject = [merge_message] for opportunity in opportunities: subject.append(opportunity.name) title = "%s : %s" % (opportunity.type == 'opportunity' and _('Merged opportunity') or _('Merged lead'), opportunity.name) fields = list(CRM_LEAD_FIELDS_TO_MERGE) details.append(self._mail_body(cr, uid, opportunity, fields, title=title, context=context)) # Chatter message's subject subject = subject[0] + ": " + ", ".join(subject[1:]) details = "\n\n".join(details) return self.message_post(cr, uid, [opportunity_id], body=details, subject=subject, context=context) def _merge_opportunity_history(self, cr, uid, opportunity_id, opportunities, context=None): message = self.pool.get('mail.message') for opportunity in opportunities: for history in opportunity.message_ids: message.write(cr, uid, history.id, { 'res_id': opportunity_id, 'subject' : _("From %s : %s") % (opportunity.name, history.subject) }, context=context) return True def _merge_opportunity_attachments(self, cr, uid, opportunity_id, opportunities, context=None): attachment = self.pool.get('ir.attachment') # return attachments of opportunity def _get_attachments(opportunity_id): attachment_ids = attachment.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', opportunity_id)], context=context) return attachment.browse(cr, uid, attachment_ids, context=context) count = 1 first_attachments = _get_attachments(opportunity_id) for opportunity in opportunities: attachments = _get_attachments(opportunity.id) for first in first_attachments: for attachment in attachments: if attachment.name == first.name: values = dict( name = "%s (%s)" % (attachment.name, count,), res_id = opportunity_id, ) attachment.write(values) count+=1 return True def merge_opportunity(self, cr, uid, ids, context=None): """ Different cases of merge: - merge leads together = 1 new lead - merge at least 1 opp with anything else (lead or opp) = 1 new opp :param list ids: leads/opportunities ids to merge :return int id: id of the resulting lead/opp """ if context is None: context = {} if len(ids) <= 1: raise osv.except_osv(_('Warning!'), _('Please select more than one element (lead or opportunity) from the list view.')) opportunities = self.browse(cr, uid, ids, context=context) sequenced_opps = [] for opportunity in opportunities: sequenced_opps.append((opportunity.stage_id and opportunity.stage_id.state != 'cancel' and opportunity.stage_id.sequence or 0, opportunity)) sequenced_opps.sort(key=lambda tup: tup[0], reverse=True) opportunities = [opportunity for sequence, opportunity in sequenced_opps] ids = [opportunity.id for opportunity in opportunities] highest = opportunities[0] opportunities_rest = opportunities[1:] tail_opportunities = opportunities_rest fields = list(CRM_LEAD_FIELDS_TO_MERGE) merged_data = self._merge_data(cr, uid, ids, highest, fields, context=context) # Merge messages and attachements into the first opportunity self._merge_opportunity_history(cr, uid, highest.id, tail_opportunities, context=context) self._merge_opportunity_attachments(cr, uid, highest.id, tail_opportunities, context=context) # Merge notifications about loss of information opportunities = [highest] opportunities.extend(opportunities_rest) self._merge_notify(cr, uid, highest, opportunities, context=context) # Check if the stage is in the stages of the sales team. If not, assign the stage with the lowest sequence if merged_data.get('type') == 'opportunity' and merged_data.get('section_id'): section_stages = self.pool.get('crm.case.section').read(cr, uid, merged_data['section_id'], ['stage_ids'], context=context) if merged_data.get('stage_id') not in section_stages['stage_ids']: stages_sequences = self.pool.get('crm.case.stage').search(cr, uid, [('id','in',section_stages['stage_ids'])], order='sequence', limit=1, context=context) merged_data['stage_id'] = stages_sequences[0] # Write merged data into first opportunity self.write(cr, uid, [highest.id], merged_data, context=context) # Delete tail opportunities self.unlink(cr, uid, [x.id for x in tail_opportunities], context=context) return highest.id def _convert_opportunity_data(self, cr, uid, lead, customer, section_id=False, context=None): crm_stage = self.pool.get('crm.case.stage') contact_id = False if customer: contact_id = self.pool.get('res.partner').address_get(cr, uid, [customer.id])['default'] if not section_id: section_id = lead.section_id and lead.section_id.id or False val = { 'planned_revenue': lead.planned_revenue, 'probability': lead.probability, 'name': lead.name, 'partner_id': customer and customer.id or False, 'user_id': (lead.user_id and lead.user_id.id), 'type': 'opportunity', 'date_action': fields.datetime.now(), 'date_open': fields.datetime.now(), 'email_from': customer and customer.email or lead.email_from, 'phone': customer and customer.phone or lead.phone, } if not lead.stage_id or lead.stage_id.type=='lead': val['stage_id'] = self.stage_find(cr, uid, [lead], section_id, [('state', '=', 'draft'),('type', 'in', ('opportunity','both'))], context=context) return val def convert_opportunity(self, cr, uid, ids, partner_id, user_ids=False, section_id=False, context=None): customer = False if partner_id: partner = self.pool.get('res.partner') customer = partner.browse(cr, uid, partner_id, context=context) for lead in self.browse(cr, uid, ids, context=context): if lead.state in ('done', 'cancel'): continue vals = self._convert_opportunity_data(cr, uid, lead, customer, section_id, context=context) self.write(cr, uid, [lead.id], vals, context=context) self.message_post(cr, uid, ids, body=_("Lead <b>converted into an Opportunity</b>"), subtype="crm.mt_lead_convert_to_opportunity", context=context) if user_ids or section_id: self.allocate_salesman(cr, uid, ids, user_ids, section_id, context=context) return True def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): partner = self.pool.get('res.partner') vals = {'name': name, 'user_id': lead.user_id.id, 'comment': lead.description, 'section_id': lead.section_id.id or False, 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, 'email': lead.email_from and tools.email_split(lead.email_from)[0], 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, 'street': lead.street, 'street2': lead.street2, 'zip': lead.zip, 'city': lead.city, 'country_id': lead.country_id and lead.country_id.id or False, 'state_id': lead.state_id and lead.state_id.id or False, 'is_company': is_company, 'type': 'contact' } partner = partner.create(cr, uid, vals, context=context) return partner def _create_lead_partner(self, cr, uid, lead, context=None): partner_id = False if lead.partner_name and lead.contact_name: partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context) elif lead.partner_name and not lead.contact_name: partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) elif not lead.partner_name and lead.contact_name: partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, context=context) elif lead.email_from and self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)[0]: contact_name = self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)[0] partner_id = self._lead_create_contact(cr, uid, lead, contact_name, False, context=context) else: raise osv.except_osv( _('Warning!'), _('No customer name defined. Please fill one of the following fields: Company Name, Contact Name or Email ("Name <email@address>")') ) return partner_id def _lead_set_partner(self, cr, uid, lead, partner_id, context=None): """ Assign a partner to a lead. :param object lead: browse record of the lead to process :param int partner_id: identifier of the partner to assign :return bool: True if the partner has properly been assigned """ res = False res_partner = self.pool.get('res.partner') if partner_id: res_partner.write(cr, uid, partner_id, {'section_id': lead.section_id and lead.section_id.id or False}) contact_id = res_partner.address_get(cr, uid, [partner_id])['default'] res = lead.write({'partner_id': partner_id}, context=context) message = _("<b>Partner</b> set to <em>%s</em>." % (lead.partner_id.name)) self.message_post(cr, uid, [lead.id], body=message, context=context) return res def handle_partner_assignation(self, cr, uid, ids, action='create', partner_id=False, context=None): """ Handle partner assignation during a lead conversion. if action is 'create', create new partner with contact and assign lead to new partner_id. otherwise assign lead to the specified partner_id :param list ids: leads/opportunities ids to process :param string action: what has to be done regarding partners (create it, assign an existing one, or nothing) :param int partner_id: partner to assign if any :return dict: dictionary organized as followed: {lead_id: partner_assigned_id} """ #TODO this is a duplication of the handle_partner_assignation method of crm_phonecall partner_ids = {} # If a partner_id is given, force this partner for all elements force_partner_id = partner_id for lead in self.browse(cr, uid, ids, context=context): # If the action is set to 'create' and no partner_id is set, create a new one if action == 'create': partner_id = force_partner_id or self._create_lead_partner(cr, uid, lead, context) self._lead_set_partner(cr, uid, lead, partner_id, context=context) partner_ids[lead.id] = partner_id return partner_ids def allocate_salesman(self, cr, uid, ids, user_ids=None, team_id=False, context=None): """ Assign salesmen and salesteam to a batch of leads. If there are more leads than salesmen, these salesmen will be assigned in round-robin. E.g.: 4 salesmen (S1, S2, S3, S4) for 6 leads (L1, L2, ... L6). They will be assigned as followed: L1 - S1, L2 - S2, L3 - S3, L4 - S4, L5 - S1, L6 - S2. :param list ids: leads/opportunities ids to process :param list user_ids: salesmen to assign :param int team_id: salesteam to assign :return bool """ index = 0 for lead_id in ids: value = {} if team_id: value['section_id'] = team_id if user_ids: value['user_id'] = user_ids[index] # Cycle through user_ids index = (index + 1) % len(user_ids) if value: self.write(cr, uid, [lead_id], value, context=context) return True def schedule_phonecall(self, cr, uid, ids, schedule_time, call_summary, desc, phone, contact_name, user_id=False, section_id=False, categ_id=False, action='schedule', context=None): """ :param string action: ('schedule','Schedule a call'), ('log','Log a call') """ phonecall = self.pool.get('crm.phonecall') model_data = self.pool.get('ir.model.data') phonecall_dict = {} if not categ_id: res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2') if res_id: categ_id = model_data.browse(cr, uid, res_id, context=context).res_id for lead in self.browse(cr, uid, ids, context=context): if not section_id: section_id = lead.section_id and lead.section_id.id or False if not user_id: user_id = lead.user_id and lead.user_id.id or False vals = { 'name': call_summary, 'opportunity_id': lead.id, 'user_id': user_id or False, 'categ_id': categ_id or False, 'description': desc or '', 'date': schedule_time, 'section_id': section_id or False, 'partner_id': lead.partner_id and lead.partner_id.id or False, 'partner_phone': phone or lead.phone or (lead.partner_id and lead.partner_id.phone or False), 'partner_mobile': lead.partner_id and lead.partner_id.mobile or False, 'priority': lead.priority, } new_id = phonecall.create(cr, uid, vals, context=context) phonecall.case_open(cr, uid, [new_id], context=context) if action == 'log': phonecall.case_close(cr, uid, [new_id], context=context) phonecall_dict[lead.id] = new_id self.schedule_phonecall_send_note(cr, uid, [lead.id], new_id, action, context=context) return phonecall_dict def redirect_opportunity_view(self, cr, uid, opportunity_id, context=None): models_data = self.pool.get('ir.model.data') # Get opportunity views dummy, form_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_form_view_oppor') dummy, tree_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_tree_view_oppor') return { 'name': _('Opportunity'), 'view_type': 'form', 'view_mode': 'tree, form', 'res_model': 'crm.lead', 'domain': [('type', '=', 'opportunity')], 'res_id': int(opportunity_id), 'view_id': False, 'views': [(form_view or False, 'form'), (tree_view or False, 'tree'), (False, 'calendar'), (False, 'graph')], 'type': 'ir.actions.act_window', } def redirect_lead_view(self, cr, uid, lead_id, context=None): models_data = self.pool.get('ir.model.data') # Get lead views dummy, form_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_form_view_leads') dummy, tree_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_tree_view_leads') return { 'name': _('Lead'), 'view_type': 'form', 'view_mode': 'tree, form', 'res_model': 'crm.lead', 'domain': [('type', '=', 'lead')], 'res_id': int(lead_id), 'view_id': False, 'views': [(form_view or False, 'form'), (tree_view or False, 'tree'), (False, 'calendar'), (False, 'graph')], 'type': 'ir.actions.act_window', } def action_makeMeeting(self, cr, uid, ids, context=None): """ Open meeting's calendar view to schedule meeting on current opportunity. :return dict: dictionary value for created Meeting view """ opportunity = self.browse(cr, uid, ids[0], context) res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'base_calendar', 'action_crm_meeting', context) res['context'] = { 'default_opportunity_id': opportunity.id, 'default_partner_id': opportunity.partner_id and opportunity.partner_id.id or False, 'default_partner_ids' : opportunity.partner_id and [opportunity.partner_id.id] or False, 'default_user_id': uid, 'default_section_id': opportunity.section_id and opportunity.section_id.id or False, 'default_email_from': opportunity.email_from, 'default_name': opportunity.name, } return res def write(self, cr, uid, ids, vals, context=None): if vals.get('stage_id') and not vals.get('probability'): # change probability of lead(s) if required by stage stage = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context) if stage.on_change: vals['probability'] = stage.probability return super(crm_lead, self).write(cr, uid, ids, vals, context=context) def new_mail_send(self, cr, uid, ids, context=None): ''' This function opens a window to compose an email, with the edi sale template message loaded by default ''' assert len(ids) == 1, 'This option should only be used for a single id at a time.' ir_model_data = self.pool.get('ir.model.data') try: template_id = ir_model_data.get_object_reference(cr, uid, 'crm', 'email_template_opportunity_mail')[1] except ValueError: template_id = False try: compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1] except ValueError: compose_form_id = False if context is None: context = {} ctx = context.copy() ctx.update({ 'default_model': 'crm.lead', 'default_res_id': ids[0], 'default_use_template': bool(template_id), 'default_template_id': template_id, 'default_composition_mode': 'comment', }) return { 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'form', 'res_model': 'mail.compose.message', 'views': [(compose_form_id, 'form')], 'view_id': compose_form_id, 'target': 'new', 'context': ctx, } # ---------------------------------------- # Mail Gateway # ---------------------------------------- def message_get_reply_to(self, cr, uid, ids, context=None): """ Override to get the reply_to of the parent project. """ return [lead.section_id.message_get_reply_to()[0] if lead.section_id else False for lead in self.browse(cr, uid, ids, context=context)] def message_new(self, cr, uid, msg, custom_values=None, context=None): """ Overrides mail_thread message_new that is called by the mailgateway through message_process. This override updates the document according to the email. """ if custom_values is None: custom_values = {} desc = html2plaintext(msg.get('body')) if msg.get('body') else '' defaults = { 'name': msg.get('subject') or _("No Subject"), 'description': desc, 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), 'partner_id': msg.get('author_id', False), 'user_id': False, } if msg.get('author_id'): defaults.update(self.on_change_partner(cr, uid, None, msg.get('author_id'), context=context)['value']) if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): defaults['priority'] = msg.get('priority') defaults.update(custom_values) return super(crm_lead, self).message_new(cr, uid, msg, custom_values=defaults, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): """ Overrides mail_thread message_update that is called by the mailgateway through message_process. This method updates the document according to the email. """ if isinstance(ids, (str, int, long)): ids = [ids] if update_vals is None: update_vals = {} if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): update_vals['priority'] = msg.get('priority') maps = { 'cost':'planned_cost', 'revenue': 'planned_revenue', 'probability':'probability', } for line in msg.get('body', '').split('\n'): line = line.strip() res = tools.command_re.match(line) if res and maps.get(res.group(1).lower()): key = maps.get(res.group(1).lower()) update_vals[key] = res.group(2).lower() return super(crm_lead, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) # ---------------------------------------- # OpenChatter methods and notifications # ---------------------------------------- def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None): phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0] if action == 'log': prefix = 'Logged' else: prefix = 'Scheduled' message = _("<b>%s a call</b> for the <em>%s</em>.") % (prefix, phonecall.date) return self.message_post(cr, uid, ids, body=message, context=context) def onchange_state(self, cr, uid, ids, state_id, context=None): if state_id: country_id=self.pool.get('res.country.state').browse(cr, uid, state_id, context).country_id.id return {'value':{'country_id':country_id}} return {}
class stock_partial_picking_line(orm.TransientModel): _inherit = "stock.partial.picking.line" def _get_color_line(self, cr, uid, picking_line, product_ids=[], context=None): if picking_line.product_id.id in product_ids: color = 'blue' elif picking_line.tracking: color = 'red' elif picking_line.line_check or context.get('line_check', False): color = 'grey' else: color = 'black' return color def action_check(self, cr, uid, move_ids, context): line = self.pool['stock.partial.picking.line'].browse(cr, uid, move_ids, context)[0] color = self._get_color_line(cr, uid, line, [], context) line_vals = { 'line_check': not line.line_check, 'row_color': color } line.write(line_vals) return True def action_add(self, cr, uid, move_ids, context): line = self.pool['stock.partial.picking.line'].browse(cr, uid, move_ids, context)[0] dest_qty = line.quantity + 1 if line.move_id.product_qty >= dest_qty: line.write({'quantity': dest_qty}) return True def action_remove(self, cr, uid, move_ids, context): line = self.pool['stock.partial.picking.line'].browse(cr, uid, move_ids, context)[0] if line.quantity >= 1.0: line.write({'quantity': line.quantity - 1}) return True def _get_prodlot_code(self, cr, uid, ids, field_name, arg, context=None): res = {} for move in self.browse(cr, uid, ids): res[move.id] = move.prodlot_id and move.prodlot_id.name or False return res def _set_prodlot_code(self, cr, uid, ids, field_name, value, arg, context=None): if not value: return False if isinstance(ids, (int, long)): ids = [ids] if not hasattr(self, 'serials'): self.serial = {ids[0]: value} else: if not ids[0] in self.serial and value in self.serial.values(): raise orm.except_orm(_('Warning'), (_('Serial number {number} is written more than once').format(number=value))) else: self.serials.append(value) lot_obj = self.pool['stock.production.lot'] for move in self.browse(cr, uid, ids, context=context): product_id = move.product_id.id value = value.upper() existing_prodlot_ids = lot_obj.search(cr, uid, [('name', '=', value), ('product_id', '=', product_id)]) if existing_prodlot_ids and not existing_prodlot_ids[0] == move.prodlot_id.id: raise orm.except_orm(_('Warning'), (_('Serial number "{number}" is already exists').format(number=value))) elif not existing_prodlot_ids: prodlot_id = self.pool['stock.production.lot'].create(cr, uid, { 'name': value, 'product_id': product_id, }) move.write({'prodlot_id': int(prodlot_id)}) return True def get_color(self, cr, uid, ids, field_name, arg, context): res = {} for line in self.browse(cr, uid, ids, context): res[line.id] = 'black' if line.tracking: res[line.id] = 'red' return res def _product_code(self, cr, uid, ids, name, arg, context=None): res = {} if context is None: context = {} product_obj = self.pool['product.product'] for line in self.browse(cr, uid, ids, context=context): partner_id = line.move_id.partner_id and line.move_id.partner_id.id or None code = product_obj._get_partner_code_name(cr, uid, [], line.product_id, partner_id, context=context)['code'] if code != line.product_id.default_code: res[line.id] = code else: res[line.id] = '' return res _columns = { 'code': fields.function(_product_code, type='char', string='Supplier Reference'), 'row_color': fields.char(string='Row color'), 'new_prodlot_code': fields.function(_get_prodlot_code, fnct_inv=_set_prodlot_code, method=True, type='char', size=64, string='Prodlot fast input', select=1 ), 'split_type': fields.related('product_id', 'lot_split_type', type="selection", selection=LOT_SPLIT_TYPE_SELECTION, string="Split", store=False), 'tracking_id': fields.many2one('stock.tracking', 'Pack/Tracking'), 'balance': fields.boolean('Balance'), 'pallet_qty': fields.integer('Number Pallet'), 'pallet_id': fields.many2one('product.ul', 'Pallet', domain=[('type', '=', 'pallet')]), 'line_check': fields.boolean('Check'), } def onchange_new_prodlot_code(self, cr, uid, ids, new_prodlot_code, product_id, prodlot_id, context=None): lot_obj = self.pool['stock.production.lot'] if new_prodlot_code: new_prodlot_code = new_prodlot_code.upper() # all serial number must be on uppercase existing_prodlot_ids = lot_obj.search(cr, uid, [('name', '=', new_prodlot_code), ('product_id', '=', product_id)], limit=1, context=context) existing_lot = lot_obj.browse(cr, uid, existing_prodlot_ids, context) move_type = self.pool['stock.partial.picking.line'].browse(cr, uid, ids, context=context)[0].move_id.picking_id.type if existing_lot: product = self.pool['product.product'].browse(cr, uid, product_id, context=context) if product.lot_split_type == 'single' and move_type != 'out' and existing_lot[0].stock_available > 0: return { 'warning': { 'title': _('Warning!'), 'message': (_('Serial number "{number}" is already exists').format(number=new_prodlot_code)) } } else: existing_lot_id = existing_prodlot_ids[0] return {'value': {'prodlot_id': existing_lot_id}} else: prodlot_id = self.pool['stock.production.lot'].create(cr, uid, { 'name': new_prodlot_code, 'product_id': product_id, }) return {'value': {'prodlot_id': prodlot_id}}
'cc_state':fields.char('State', size=32,), 'cc_zip':fields.char('Postal/Zip', size=32,), 'cc_country':fields.char('Country', size=32,), 'cc_order_date':fields.date('Order Date',), <<<<<<< HEAD 'cc_order_amt':fields.float('Order Amt'), ======= 'cc_order_amt':fields.float('Order Amt',required=True), >>>>>>> c1979f64b3360c86d60e00c92be0271d89f97f2d 'cc_number':fields.char('Credit Card Number', size=256), 'cc_v':fields.char('Card Code Verification', size=3), 'cc_e_d_month':fields.char('Expiration Date MM', size=32), 'cc_e_d_year':fields.char('Expiration Date YY', size=32), 'cc_comment':fields.char('Comment', size=128,), 'cc_auth_code':fields.char('Authorization Code', size=32), 'cc_save_card_details':fields.boolean('Save Credit Card details'), 'cc_ecommerce_sale':fields.boolean('Ecommerce sale'), 'cc_p_authorize':fields.boolean('Pre-authorize'), 'cc_charge':fields.boolean('Charge'), 'cc_info_hide':fields.boolean('Credit Info Hide'), 'cc_status':fields.text('Status Message'), 'cc_details_autofill':fields.boolean('Credit Card Details Auto Fill'), 'cc_reseller':fields.boolean('Reseller'), 'rel_sale_order_id':fields.many2one('sale.order', 'Related Sale Order'), 'cc_trans_id':fields.char('Transaction ID', size=128), 'cc_bank':fields.many2one('res.bank', 'Bank'), 'cc_details':fields.many2one('res.partner.bank', 'Bank'), 'cc_length':fields.integer('CC Length'), 'cc_transaction':fields.boolean('Transaction Done'), 'key':fields.char('Encryption Key', size=1024, help="The Key used to Encrypt the Credit Card Number"),
class openacademy_session(osv.Model): _name = 'openacademy.session' def _check_instructor_not_in_attendees( self, cr, uid, ids, context=None ): # cuando la fucnion empieza con guion bajo es que es privada, no puedeser usada fuera de open academy if not context: contex = {} print ids, "imprimo ids" for session in self.browse(cr, uid, ids, context): lista_attendee = [] for attendee in session.attendee_ids: lista_attendee.append(attendee.partner_id.id) print lista_attendee, " imprimo lista de attendees" if session.instructor_id and session.instructor_id.id in lista_attendee: return False return True def _get_percent(self, seats, attendee_ids): try: return (100 * len(attendee_ids)) / seats except ZeroDivisionError: return 0.0 def _take_seats_percent(self, cr, uid, ids, field, args, context=None): print field, "field" print args, "args" resultado = {} for session in self.browse(cr, uid, ids, context): resultado[session.id] = self._get_percent(session.seats, session.attendee_ids) print resultado, "resultado" return resultado def onchange_taken_seats(self, cr, uid, ids, seats, attendee_ids): print attendee_ids, "recibe" num_attendees = self.resolve_2many_commands(cr, uid, 'attendee_ids', attendee_ids, ['id']) print num_attendees, "despues" resultado = { 'value': { 'taken_seats_percent': self._get_percent(seats, attendee_ids) } } print resultado if seats < len(num_attendees): resultado['warning'] = { 'title': "Atencion asientos", 'message': "No puedes tener menos asientos que asistentes" } print resultado return resultado def _determin_end_date(self, cr, uid, ids, field, arg, context=None): res = {} for session in self.browse(cr, uid, ids, context): if session.start_date and session.duration: start_date = datetime.strptime(session.start_date, "%Y-%m-%d") duration = timedelta(days=session.duration - 1) end_date = start_date + duration res[session.id] = end_date.strftime("%Y-%m-%d") else: res[session.id] = session.start_date print res return res def _set_end_date(self, cr, uid, id, field, value, arg, context=None): session = self.browse(cr, uid, id, context) if session.start_date and value: start_date = datetime.strptime(session.start_date, "%Y-%m-%d") end_date = datetime.strptime(value, "%Y-%m-%d") duration = end_date - start_date self.write(cr, uid, id, {'duration': duration.days + 1}, context) def _determin_hours_from_duration(self, cr, uid, ids, field, arg, context=None): result = {} sessions = self.browse(cr, uid, ids, context=context) for session in sessions: result[session.id] = (session.duration * 8 if session.duration else 0) return result def _set_hours(self, cr, uid, id, field, value, arg, context=None): if value: self.write(cr, uid, id, {'duration': (value / 8)}, context=context) def _get_attendee_count(self, cr, uid, ids, name, args, context=None): res = {} for session in self.browse(cr, uid, ids, context): res[session.id] = len(session.attendee_ids) return res def action_confirm(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state': 'confirm'}, context=context) def action_draft(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state': 'draft'}, context=context) def action_done(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state': 'done'}, context=context) _columns = { 'name': fields.char(string='Nombre', size=256, required=True), 'start_date': fields.date(string='Fecha Inicio'), 'duration': fields.float(string='Duracion', digits=(6, 2), help='Duracion en dias'), 'seats': fields.integer(string='Numero de asientos'), 'active': fields.boolean(string='Activo'), # 'instructor_id': fields.many2one('res.partner', string='Instructor', domain= ['|',('instructor','=',True),('name','ilike','%axel%')] ), 'instructor_id': fields.many2one('res.partner', string='Instructor', domain=[('instructor', '=', True)], required=True), 'course_id': fields.many2one('openacademy.course', string='Curso', ondelete='cascade'), 'attendee_ids': fields.one2many('openacademy.attendee', 'session_id', string='Asistentes'), 'taken_seats_percent': fields.function(_take_seats_percent, type='float', string='Taken Seats'), 'end_date': fields.function(_determin_end_date, type='date', string="End Date", fnct_inv=_set_end_date), 'hours': fields.function(_determin_hours_from_duration, type='float', string='Hours', fnct_inv=_set_hours), 'attendee_count': fields.function(_get_attendee_count, type='integer', string='Attendee Count', store=True), 'color': fields.integer('Color'), 'state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done')], string='State'), } _defaults = { 'active': True, 'start_date': fields.date.today, 'state': 'draft', } _constraints = [(_check_instructor_not_in_attendees, "El instructor no puede ser asistente", ['instructor_id', 'attendee_ids'])]
res[company.id]['smart_budget'] = round(res[company.id]['smart_budget'] - res[company.id]['smart_amount_budget'],0) #res[company.id]['smart_cash'] = 0.0 return res _columns = { 'smart_budget': fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Budget',multi='all',help="Approved invoiced amount.",), # 'smart_cash': fields.function(smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Budget', # help="Approved invoiced amount.", # multi='all',), 'smart_cash': fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Cash', help="Free invoiced amount for salary or expenses.", multi='all',), 'prepayment': fields.boolean('Prepayment',help="SMart User: this virtual company can have prepayment smart_cash, SMart Company: this country applies prepayment"), 'prepayment_days': fields.integer('Prepayment Days',help="Leadtime in days before invoiced amount becomes smart_cash (global)"), 'smart_share': fields.float('SMarts Share',digits_compute=dp.get_precision('Account')), 'sale_order_sum_cash': fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Order sum cash',multi='all',help="Approved invoiced amount.",), 'sale_order_sum_budget':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Order sum budget',multi='all',help="Approved invoiced amount.",), 'smart_amount_cash':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Amount cash',multi='all',help="Approved invoiced amount.",), 'smart_amount_budget':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Amount budget',multi='all',help="Approved invoiced amount.",), 'activity_amount_cash':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart activity amount cash',multi='all',help="Approved invoiced amount.",), 'activity_amount_budget':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart activity amount budget',multi='all',help="Approved invoiced amount.",), 'expense_sum_cash':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart expense sum cash',multi='all',help="Approved invoiced amount.",), 'expense_sum_budget':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart expense sum budget',multi='all',help="Approved invoiced amount.",), } _defaults = { 'prepayment': True, 'smart_cash': 0.0,
class EdiHistoryCheck(osv.osv): ''' Manage path for all company EDI that has active routes ''' _name = 'edi.history.check' _description = 'EDI history check' _order = 'name,sequence,line_out,line_in' # ---------------------------- # Button and utility function: # ---------------------------- def dummy_button(self, cr, uid, ids, context=None): ''' Button does nothing ''' return True # Button OUT block: def button_header_out(self, cr, uid, ids, context=None): return self.get_order_out(cr, uid, ids, 'header', context=context) def button_detail_out(self, cr, uid, ids, context=None): return self.get_order_out(cr, uid, ids, 'detail', context=context) def button_document_out(self, cr, uid, ids, context=None): return self.get_order_out(cr, uid, ids, 'account', context=context) def get_order_out(self, cr, uid, ids, button_mode='header', context=None): ''' Open view for see all order OUT button_mode for choose the type of filter: 'header', 'detail', 'account' ''' order_proxy = self.browse(cr, uid, ids, context=context)[0] if button_mode == 'header': domain = [('name', '=', order_proxy.name)] elif button_mode == 'detail': domain = [('name', '=', order_proxy.name_detail)] elif button_mode == 'account': domain = [('document_out', '=', order_proxy.document_out)] else: raise osv.except_osv( _('Error!'), _('Buttot filter not found!'), ) context['search_default_order_state_urgent_grouped'] = False return { 'res_model': 'edi.history.check', 'type': 'ir.actions.act_window', 'target': 'new', 'view_type': 'form', 'view_mode': 'tree,form', #'res_id': context.get('active_id', False), 'context': None, 'domain': domain, } # Button IN block: def button_header_in(self, cr, uid, ids, context=None): return self.get_order_in(cr, uid, ids, 'header', context=context) def button_detail_in(self, cr, uid, ids, context=None): return self.get_order_in(cr, uid, ids, 'detail', context=context) def get_order_in(self, cr, uid, ids, button_mode='header', context=None): ''' Open view for see all order IN context for pass button_mode: 'header' (def.), 'detail' ''' # Search header or detail order code order_proxy = self.browse(cr, uid, ids, context=context)[0] order_pool = self.pool.get('edi.history.order') if button_mode == 'header': order = _('Order header IN: %s') % order_proxy.name domain = [('name', '=', order_proxy.name)] else: order = _('Order detail IN: %s') % order_proxy.name_detail domain = [('name', '=', order_proxy.name_detail)] order_ids = order_pool.search(cr, uid, domain, context=context) if order_ids: return { 'res_model': 'edi.history.order', 'type': 'ir.actions.act_window', 'target': 'new', 'view_type': 'form', 'view_mode': 'form', 'res_id': order_ids[0], 'context': { 'search_default_order_state_urgent_grouped': False}, 'domain': [('name', '=', order_ids[0])], } raise osv.except_osv( _('Error!'), _('Order not found in archive: %s' % order), ) # ------------------------------------------------------------------------- # Scheduled action # ------------------------------------------------------------------------- def import_invoce_check(self, cr, uid, code, context=None): ''' Import procedure for check if order are correctly imported and match with invoice / delivery document in account self: instance object cr: cursor for database uid: user ID context: parameter arguments passed ''' # TODO code will be list if used in other company # --------------------------------------------------------------------- # Utility function # --------------------------------------------------------------------- def update_dict_with_parent_product(self, cr, uid, parent_product, parent_code, context=None): ''' Search parent code in dict if not present create and update dictionary (used this for external search) (ID, quantity tolerance, price tolerance) ''' parent_pool = self.pool.get('edi.product.parent') if parent_code not in parent_product: parent_product[parent_code] = ( parent_pool.create(cr, uid, { 'code': parent_code, 'name': _('Product parent code %s') % parent_code, }, context=context), 5.0, # quantity_tolerance 5.0, # price_tolerance ) else: parent_ids = parent_pool.search(cr, uid, [ ('code', '=', parent_code)], context=context) # parent_ids must exist (parent_product loaded with it parent_proxy = parent_pool.browse( cr, uid, parent_ids, context=context)[0] parent_product[parent_code] = ( parent_proxy.id, parent_proxy.quantity_tolerance, parent_proxy.price_tolerance, ) return def remove_from_list(order_in_check, order, line): ''' Remove without error ''' try: order_in_check.remove((order, line_out)) # error if not present except: pass # no problem on error return def sort_sequence(filelist): ''' Internal utility for sort list get priority to orders and then to ordchg after to normal order ''' # TODO add some controls (need to be right) # Split in 2 list the element depend on type: if len(filelist) in (0, 1): # 0 raise error? return filelist orders = sorted([ filename for filename in filelist if 'ORDERS' in filename]) if len(orders) > 1: _logger.error('More than one ORDERS file %s' % orders ) # TODO what to do now? ordchg = sorted([ filename for filename in filelist if 'ORDCHG' in filename]) # order and merge as is: orders.extend(ordchg) return orders def load_order_from_history(order, history_filename, order_record, order_in_check): ''' Function that load all files and create a dictionary with row key The function also save in database the order for a readable check in. order: order code origin history_filename: database of filename (list for every order) order_record: dict with order line imported from history files order_in_check: list of all record (for set order_in attribute) ''' if order in order_record: # pass only the first time return modified = False order_record[order] = {} sort_history_filename = sort_sequence( history_filename.get(order, [])) for filename in sort_history_filename: # Read all files and update dict with row record: f = open(filename) m_time = time.ctime(os.path.getmtime(filename)) c_time = time.ctime(os.path.getctime(filename)) for row in f: file_type = row[10:16] # ORDERS or ORDCHG update_type = row[16:19] # 003 ann, 001 mod. q. line_in = row[2336:2341] # 5 article = row[2356:2367].strip() # 11 quantity = float(row[2631:2641].strip() or '0') price = float(row[2964:2994].strip() or '0') if file_type == 'ORDERS': line_type = 'original' else: # ORDCHG modified = True if update_type == '001': line_type = 'update' elif update_type == '003': line_type = 'cancel' else: # operation non in list _logger.error( 'Update code not found: %s' % update_type) line_type = 'error' order_record[order][line_in] = ( # For check with account information: article, line_type, quantity, price, # For history record file in database: filename, c_time, m_time) if (order, line_in) not in order_in_check: # for modify! order_in_check.append( (order, line_in)) # add key for test A - B # Save file for create a HTML order more readable: if order: # jump empty order_html = _(''' <style> .table_bf { border: 1px solid black; padding: 3px; } .table_bf td { border: 1px solid black; padding: 3px; text-align: center; } .table_bf_wo td { border: 1px solid black; padding: 3px; text-align: center; background-color: AntiqueWhite; text-decoration: line-through; } .table_bf_upd td { border: 1px solid black; padding: 3px; text-align: center; background-color: Gainsboro; } .table_bf th { border: 1px solid black; padding: 3px; text-align: center; background-color: grey; color: white; } </style> <table class='table_bf'> <tr class='table_bf'> <th>Line</th><th>Article</th><th>Quant.</th> <th>Price</th><th>File</th><th>Create</th> <th>Mod.</th> </tr>''') for line_in in sorted(order_record[order].keys()): # Chose color line with class: if order_record[order][line_in][1] == 'cancel': row_class = 'class="table_bf_wo"' elif order_record[order][line_in][1] == 'update': row_class = 'class="table_bf_upd"' else: row_class = '' order_html += ''' <tr %s> <td>%s</td><td>%s</td><td>%s</td> <td class='{text-align: right;}'>%s</td> <td class='{text-align: right;}'>%s</td> <td>%s</td><td>%s</td> </tr> ''' % ( row_class, # Cancel state line_in, # Row order_record[order][line_in][0], # Article order_record[order][line_in][2], # Quant. order_record[order][line_in][3], # Price # Filename (base name extract from full path: os.path.basename(order_record[order][line_in][4]), order_record[order][line_in][5], # Create date order_record[order][line_in][6], # Modifty date ) order_html += '</table>' order_pool = self.pool.get('edi.history.order') order_ids = order_pool.search(cr, uid, [ ('name', '=', order)], context=context) # Create file list: file_list = "" for f in sort_history_filename: file_list += "%s\n" % os.path.basename(f) if order_ids: order_id = order_ids[0] order_pool.write(cr, uid, order_id, { 'note': order_html, 'modified': modified, 'file': file_list, 'total': len(sort_history_filename), }, context=context) else: order_id = order_pool.create(cr, uid, { 'name': order, 'modified': modified, 'note': order_html, 'file': file_list, 'total': len(sort_history_filename), }, context=context) return # TODO order_id? # --------------------------------------------------------------------- # Start procedure and get configuration parameters # --------------------------------------------------------------------- _logger.info('Start import history order for check') # -------------------------- # Read configuration record: # -------------------------- # Read record for code passed in scheduling: config_pool = self.pool.get('edi.history.configuration') config_ids = config_pool.search(cr, uid, [ ('code', '=', code)], context=context) if not config_ids: _logger.error('Code %s not found in configuration file!' % code) return False config_proxy = config_pool.browse( cr, uid, config_ids, context=context)[0] # Folder path: input_folder = config_proxy.history_path # history order input_invoice = config_proxy.invoice_file # account invoice # Precision for price / quantity evaluation quant_prec = config_proxy.quantity_precision price_prec = config_proxy.price_precision # --------------- # Clean database: # --------------- remove_ids = self.search(cr, uid, [], context=context) self.unlink(cr, uid, remove_ids, context=context) # ------------------- # Read files history: # ------------------- # Save in dict for future access (that parse whole order and modify) history_filename = {} # list of file (key=order, value=filename) order_record = {} # record in file (k order, v {row: (art, state)} order_in_check = [] for root, directories, files in os.walk(input_folder): for filename in files: filepath = os.path.join(root, filename) f = open(filepath, 'rb') row = f.read() f.close() order = row[19:29].strip() if not order: _logger.error( 'Found order without name (jump): %s' % filename) continue if order not in history_filename: history_filename[order] = [] # Create empty list #os.path.getmtime(filepath) # TODO for evaluation order prior. history_filename[order].append(filepath) # ------------------------- # Load parent product code: # ------------------------- parent_product = {} parent_pool = self.pool.get('edi.product.parent') parent_ids = parent_pool.search(cr, uid, [], context=context) for c in parent_pool.browse(cr, uid, parent_ids, context=context): parent_product[c.code] = ( c.id, c.quantity_tolerance, c.price_tolerance) # --------------------- # Start import invoice: # --------------------- # Read all lines and save only duplicated order_out_check = [] # List of order-row for check duplication i = -config_proxy.header sequence = 0 old_order = False old_line = False # TODO problem with order (not sorted with invoice/ddt) for invoice in csv.reader( open(input_invoice, 'rb'), delimiter=str(config_proxy.delimiter)): i += 1 sequence += 1 if i <= 0: _logger.info('Jump header lines') continue # Mapping fields: doc_type = invoice[0].strip() document_out = invoice[1].strip() order = invoice[2].strip() # header article = invoice[3].strip() order_detail = invoice[4].strip() line_out = invoice[5].strip() quantity = float(invoice[6].strip().replace(',', '.') or '0') price = float(invoice[7].strip().replace(',', '.') or '0') # TODO check alias article for get correct element update_dict_with_parent_product(self, cr, uid, parent_product, article[:3], context=context) data = { 'sequence': sequence, # for sort as account (check seq. err.) 'name': order, # to search in history 'name_detail': order_detail, 'line_in': False, 'line_out': line_out, 'quantity_in': False, 'quantity_out': quantity, 'over_quantity': False, 'price_in': False, 'price_out': price, 'over_price': False, 'document_out': document_out, 'document_type': doc_type, 'product_code_in': False, 'parent_in_id': False, #'product_parent_in': False, 'product_code_out': article, 'parent_out_id': parent_product.get(article[:3], ( False, 0.0, 0.0))[0], #'product_parent_out': article[:3], } # ------------------------------- # STATE MANAGE: Speed check case: # ------------------------------- if not order: if not order_detail: data['name'] = _('NOT FOUND!') data['state'] = 'no_order' self.create(cr, uid, data, context=context) continue data['use_detail_order'] = True order = order_detail data['name'] = order elif order != order_detail: data['state'] = 'order' self.create(cr, uid, data, context=context) continue data['state'] = 'ok' # temporary OK after account check # Load order if not present in database: if order not in order_record: load_order_from_history( order, history_filename, order_record, order_in_check) # ----------------------------- # STATE MANAGE: Duplicated row: # ----------------------------- if (order, line_out) in order_out_check: # if duplicated was yet removed from order_in_check data['state'] = 'duplicated' self.create(cr, uid, data, context=context) continue # Jump line order_out_check.append((order, line_out)) # used for check dupl. # ----------------------------- # STATE MANAGE: Sequence error: # ----------------------------- wrong_sequence = False # Note: sequence is evaluated here for "old" counter but, # if present, the write operation is done after wrong_line & only # out, this because are more important than sequence error if old_order == (order, document_out): # NOTE: Break the sequence with order and invoice!!! if old_line and line_out < old_line: old_line = line_out data['state'] = 'sequence' wrong_sequence = True else: old_line = line_out # No error, save this as old else: # If change order reset line: old_order = (order, document_out) old_line = line_out # first line of order # --------------------------------------------------- # HISTORY ANALYSIS: Wrong line (not present in order) # --------------------------------------------------- # NOTE: More important than sequence!! if line_out not in order_record[order]: # (article, line_type) data['state'] = 'wrong_line' # not presen in order self.create(cr, uid, data, context=context) continue # Jump line # Remove here line for only_in test (so out removed from in) remove_from_list(order_in_check, order, line_out) # --------------------------------------------------------------- # HISTORY ANALYSIS: Line removed (present in order but cancelled) # --------------------------------------------------------------- if order_record[order][line_out][1] == 'cancel': data['state'] = 'only_out' # present but deleted in order self.create(cr, uid, data, context=context) continue # Jump line # Note: Here we write wrong sequence, before valuated, if present: if wrong_sequence: self.create(cr, uid, data, context=context) continue # ------------------------------------------ # HISTORY ANALYSIS: Test article is the same # ------------------------------------------ data['product_code_in'] = order_record[order][line_out][0] update_dict_with_parent_product(self, cr, uid, parent_product, order_record[order][line_out][0][:3], context=context) data['parent_in_id'] = parent_product.get( order_record[order][line_out][0][:3], (False, 0.0, 0.0))[0] if order_record[order][line_out][0] != article[:11]: data['line_in'] = line_out data['state'] = 'article' self.create(cr, uid, data, context=context) continue # Jump line # ------------------------------------------------ # HISTORY ANALYSIS: Warning test for q. and price: # ------------------------------------------------ # Price in % of tolerance data['price_in'] = order_record[order][line_out][3] # 4 element price_tolerance = parent_product.get( order_record[order][line_out][0][:3], (False, 0.0, 0.0))[2] if data['price_in'] and 100.0 / data['price_in'] * abs( data['price_in'] - price) > price_tolerance: data['over_price'] = True # Quantity in % of tolerance data['quantity_in'] = order_record[order][line_out][2] # 3 element quantity_tolerance = parent_product.get( order_record[order][line_out][0][:3], (False, 0.0, 0.0))[1] if data['quantity_in'] and 100.0 / data['quantity_in'] * abs( data['quantity_in'] - quantity) > quantity_tolerance: data['over_quantity'] = True # ---------------------------------------- # HISTORY ANALYSIS: Test price is the same # ---------------------------------------- if abs(data['price_in'] - price) > price_prec: data['line_in'] = line_out #data['quantity_in'] = False # raise price error, q restore 0.0 data['state'] = 'price' self.create(cr, uid, data, context=context) continue # Jump line # ------------------------------------------- # HISTORY ANALYSIS: Test quantity is the same # ------------------------------------------- if abs(data['quantity_in'] - quantity) > quant_prec: data['line_in'] = line_out data['state'] = 'quantity' self.create(cr, uid, data, context=context) continue # Jump line # ---------------------------- # CORRECT RECORD: Save article # ---------------------------- # Note: line_in now are equals to line_out! data['line_in'] = line_out # must to be here (after all) self.create(cr, uid, data, context=context) # Write line present in IN and not in OUT: # Note: must be before mark order evaluation! order_yet_found = [] for (order, line_in) in order_in_check: if order not in order_record: _logger.error( 'Order %s with in line not found not present!' % order) continue order_in = order_record[order] # yet loaded master loop # Create line for order in without order out fields: # TODO put in function: update_dict_with_parent_product(self, cr, uid, parent_product, order_in[line_in][0][:3], context=context) data = { 'sequence': 0, # first line not prest in order out 'name': order, 'name_detail': False, 'line_in': line_in, # Use line_out 'line_out': False, 'quantity_in': order_in[line_in][2], 'quantity_out': False, 'price_in': order_in[line_in][3], 'price_out': False, 'document_out': False, 'document_type': False, 'state': 'only_in', # Product and parent: 'product_code_in': order_in[line_in][0], 'parent_in_id': parent_product.get( order_in[line_in][0][:3], (False, 0.0, 0.0))[0], #'product_parent_in': order_in[line_in][0][:3], # TODO remove 'product_code_out': False, 'parent_out_id': False, #'product_parent_out': False, # TODO remove } self.create(cr, uid, data, context=context) if order not in order_yet_found: # write only once _logger.warning('Order with only_in case: %s' % order) order_yet_found.append(order) # ------------------------------------------- # Update field for show all order with errors # ------------------------------------------- # Mark all order as error where there's almost one line (after all!) # NOTE: Splitted in two, maybe for precedence problems _logger.info('Mark all order error if has line problem:') line_ko_ids = self.search(cr, uid, [ # Line with problems ('state', '!=', 'ok'), # OK ('state', '!=', 'quantity'), # No order error if quantity ], context=context) order_ko_list = [ # Order code list: item.name for item in self.browse( cr, uid, line_ko_ids, context=context)] order_ko_ids = self.search(cr, uid, [ # All order lines: ('name', 'in', order_ko_list), ], context=context) self.write(cr, uid, order_ko_ids, { # Upd. field for search whole order 'order_error': True }, context=context) return True _columns = { 'sequence': fields.integer('Sequence'), 'name': fields.char('Order name', size=25, help='Order ID of company'), 'name_detail': fields.char('Order detail', size=25, help='Order ID in accounting detail'), 'price_in': fields.float('Price in', digits=(16, 2)), 'price_out': fields.float('Price out', digits=(16, 2)), 'over_price': fields.boolean('Over price'), 'quantity_in': fields.float('Quantity in', digits=(16, 2)), 'quantity_out': fields.float('Quantity out', digits=(16, 2)), 'over_quantity': fields.boolean('Over quantity'), 'line_in': fields.char('Line in', size=5, help='Order line'), 'line_out': fields.char('Line out', size=5, help='Delivery of invoice line'), 'product_code_in': fields.char('Product in', size=18, help='Order product in'), 'product_code_out': fields.char('Product out', size=18, help='Invoice or delivery product out'), 'parent_in_id': fields.many2one('edi.product.parent', 'Parent IN', help='Parent ID code (order in product)'), 'parent_out_id': fields.many2one('edi.product.parent', 'Parent OUT', help='Parent ID code (document out accounting product)'), 'document_out': fields.char('Document out', size=20, help='Number of document, delivery or invoice, out'), 'document_type': fields.selection([ ('OC', 'Order'), ('BC', 'Document of transport'), ('FT', 'Invoice'), ], 'Document type', help='Document out type'), 'order_error': fields.boolean('Order error', help='As if the line is correct in order there\'s almost one line' 'with error'), 'use_detail_order': fields.boolean('Use detail order', help='This line don''t have order so used detail'), 'state': fields.selection([ # First control, readind account file: ('no_order', 'Order not present'), ('order', 'Order doesn\'t match'), ('sequence', 'Sequence error'), ('duplicated', 'Row duplicated'), # Control reading history: ('only_in', 'Not imported'), # not present in accounting ('only_out', 'Extra line'), # only out (order was cancel) ('wrong_line', 'Wrong line'), # not present in order ('article', 'Article error'), ('quantity', 'Quantity error'), # TODO Manage ('price', 'Price error'), # TODO ('unmanaged', 'Error unmanaged'), # If all correct: ('ok', 'Correct'), ], 'State', help='Error state'), }
class stock_fill_inventory(osv.osv_memory): _inherit = "stock.fill.inventory" def _default_location(self, cr, uid, ids, context=None): try: location = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock') with mute_logger('openerp.osv.orm'): location.check_access_rule('read', context=context) location_id = location.id except (ValueError, orm.except_orm), e: return False return location_id or False _columns = { 'set_account_zero': fields.boolean("Set account valuation to zero",help="If checked, the balance of the inventory account will be reseted to 0 after validating the inventory"), } def fill_inventory(self, cr, uid, ids, context=None): """ To Import stock inventory according to products available in the selected locations. @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param ids: the ID or list of IDs if we want more than one @param context: A standard dictionary @return: """ if context is None: context = {} res = super(stock_fill_inventory, self).fill_inventory(
class account_voucher(osv.osv): _inherit = 'account.voucher' #set the account for the selected partner def onchange_od_partner_id(self, cr, uid, ids, partner_id, context=None): res = {} if partner_id: partner = self.pool.get('res.partner').browse(cr, uid, partner_id) # print "###############",partner # writeoff_acc_id = partner. # return {'value': {'writeoff_acc_id': writeoff_acc_id}} return res def onchange_od_group_pay(self, cr, uid, ids, od_group_pay, context=None): return {'value': {'partner_id': ''}} _columns = { 'od_partner_id': fields.many2one('res.partner', 'Partner'), 'od_populate': fields.selection([('dr', 'Debit'), ('cr', 'Credit')], 'Allocation'), 'od_group_pay': fields.boolean('Pay as Group', readonly=True, states={'draft': [('readonly', False)]}), 'od_payee': fields.char('Payee', readonly=True, states={'draft': [('readonly', False)]}), 'od_acc_payee': fields.boolean('A/c Payee', readonly=True, states={'draft': [('readonly', False)]}) } #partner selection in diff amount writeoff def writeoff_move_line_get(self, cr, uid, voucher_id, line_total, move_id, name, company_currency, current_currency, context=None): move_line = super(account_voucher, self).writeoff_move_line_get(cr, uid, voucher_id, line_total, move_id, name, company_currency, current_currency, context=None) voucher = self.pool.get('account.voucher').browse( cr, uid, voucher_id, context) if voucher.state == 'draft' and voucher.pdc_check and voucher.od_with_out_allocaion: # partner_rec_acc_id = voucher.partner_id.property_account_receivable.id # partner_payable_acc_id = voucher.partner_id.property_account_payable.id mvl_acc_id = move_line.get('account_id') # if mvl_acc_id and mvl_acc_id == partner_rec_acc_id: move_line[ 'account_id'] = voucher.od_check_clearing_acc_id and voucher.od_check_clearing_acc_id.id or move_line.get( 'account_id') if voucher.od_partner_id and voucher.od_partner_id.id: move_line[ 'partner_id'] = voucher.od_partner_id and voucher.od_partner_id.id return move_line def od_deallocate(self, cr, uid, ids, context=None): ctx = context.copy() for voucher in self.browse(cr, uid, ids, context): voucher.line_cr_ids.unlink() voucher.line_dr_ids.unlink() voucher_cr_lines = [] voucher_dr_lines = [] partner_id = voucher.partner_id and voucher.partner_id.id journal_id = voucher.journal_id and voucher.journal_id.id price = voucher.amount or 0.0 currency_id = voucher.currency_id and voucher.currency_id.id ttype = voucher.type rate = voucher.payment_rate date = voucher.date ctx.update({'date': date}) payment_rate_currency_id = voucher.payment_rate_currency_id and voucher.payment_rate_currency_id.id voucher_rate = self.pool.get('res.currency').read( cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate'] ctx.update({ 'voucher_special_currency': payment_rate_currency_id, 'voucher_special_currency_rate': rate * voucher_rate, }) res = self.od_recompute_voucher_lines(cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=ctx) cr_lines = res['value'].get('line_cr_ids') dr_lines = res['value'].get('line_dr_ids') # if cr_lines and voucher.od_populate == 'cr': # for line in cr_lines: # voucher_cr_lines.append([0,0,line]) # elif dr_lines and voucher.od_populate == 'dr': # for line in dr_lines: # voucher_dr_lines.append([0,0,line]) # else: for line in cr_lines: voucher_cr_lines.append([0, 0, line]) for line in dr_lines: voucher_dr_lines.append([0, 0, line]) voucher.write({ 'line_cr_ids': voucher_cr_lines, 'line_dr_ids': voucher_dr_lines }) print "!!!!!!!~~~~~~~**", res return res return self.od_recompute_voucher_lines(cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=ctx) # to avoid automatic Allocation def recompute_voucher_lines(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=None): #set default values default = { 'value': { 'line_dr_ids': [], 'line_cr_ids': [], 'pre_line': False }, } return default #automatic alocation is overide to allocate manually def od_recompute_voucher_lines(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=None): if context is None: context = {} if context.get('od_manual_allocate'): return {} """ Returns a dict that contains new values and context @param partner_id: latest value from user input for field partner_id @param args: other arguments @param context: context arguments, like lang, time zone @return: Returns a dict which contains new values, and context """ def _remove_noise_in_o2m(): """if the line is partially reconciled, then we must pay attention to display it only once and in the good o2m. This function returns True if the line is considered as noise and should not be displayed """ if line.reconcile_partial_id: if currency_id == line.currency_id.id: if line.amount_residual_currency <= 0: return True else: if line.amount_residual <= 0: return True return False if context is None: context = {} context_multi_currency = context.copy() currency_pool = self.pool.get('res.currency') move_line_pool = self.pool.get('account.move.line') partner_pool = self.pool.get('res.partner') journal_pool = self.pool.get('account.journal') line_pool = self.pool.get('account.voucher.line') #set default values default = { 'value': { 'line_dr_ids': [], 'line_cr_ids': [], 'pre_line': False }, } # drop existing lines line_ids = ids and line_pool.search(cr, uid, [('voucher_id', '=', ids[0])]) for line in line_pool.browse(cr, uid, line_ids, context=context): if line.type == 'cr': default['value']['line_cr_ids'].append((2, line.id)) else: default['value']['line_dr_ids'].append((2, line.id)) if not partner_id or not journal_id: return default journal = journal_pool.browse(cr, uid, journal_id, context=context) partner = partner_pool.browse(cr, uid, partner_id, context=context) currency_id = currency_id or journal.company_id.currency_id.id total_credit = 0.0 total_debit = 0.0 account_type = None if context.get('account_id'): account_type = self.pool['account.account'].browse( cr, uid, context['account_id'], context=context).type if ttype == 'payment': if not account_type: account_type = 'payable' total_debit = price or 0.0 else: total_credit = price or 0.0 if not account_type: account_type = 'receivable' ##Custom Code move_search_domain = [('state', '=', 'valid'), ('account_id.type', '=', account_type), ('reconcile_id', '=', False), ('partner_id', '=', partner_id)] if context.get('od_group_pay'): move_search_domain = [('state', '=', 'valid'), ('account_id.type', '=', account_type), ('reconcile_id', '=', False), ('partner_id', 'child_of', partner_id)] if not context.get('move_line_ids', False): ids = move_line_pool.search(cr, uid, move_search_domain, context=context) else: ids = context['move_line_ids'] invoice_id = context.get('invoice_id', False) company_currency = journal.company_id.currency_id.id move_lines_found = [] #order the lines by most old first ids.reverse() account_move_lines = move_line_pool.browse(cr, uid, ids, context=context) #compute the total debit/credit and look for a matching open amount or invoice for line in account_move_lines: if _remove_noise_in_o2m(): continue if invoice_id: if line.invoice.id == invoice_id: #if the invoice linked to the voucher line is equal to the invoice_id in context #then we assign the amount on that line, whatever the other voucher lines move_lines_found.append(line.id) elif currency_id == company_currency: #otherwise treatments is the same but with other field names if line.amount_residual == price: #if the amount residual is equal the amount voucher, we assign it to that voucher #line, whatever the other voucher lines move_lines_found.append(line.id) break #otherwise we will split the voucher amount on each line (by most old first) total_credit += line.credit or 0.0 total_debit += line.debit or 0.0 elif currency_id == line.currency_id.id: if line.amount_residual_currency == price: move_lines_found.append(line.id) break total_credit += line.credit and line.amount_currency or 0.0 total_debit += line.debit and line.amount_currency or 0.0 remaining_amount = price #voucher line creation for line in account_move_lines: if _remove_noise_in_o2m(): continue if line.currency_id and currency_id == line.currency_id.id: amount_original = abs(line.amount_currency) amount_unreconciled = abs(line.amount_residual_currency) else: #always use the amount booked in the company currency as the basis of the conversion into the voucher currency amount_original = currency_pool.compute( cr, uid, company_currency, currency_id, line.credit or line.debit or 0.0, context=context_multi_currency) amount_unreconciled = currency_pool.compute( cr, uid, company_currency, currency_id, abs(line.amount_residual), context=context_multi_currency) line_currency_id = line.currency_id and line.currency_id.id or company_currency rs = { 'name': line.move_id.name, 'type': line.credit and 'dr' or 'cr', 'move_line_id': line.id, 'account_id': line.account_id.id, 'amount_original': amount_original, 'amount': 0.0, #custom 'date_original': line.date, 'date_due': line.date_maturity, 'amount_unreconciled': amount_unreconciled, 'currency_id': line_currency_id, 'od_partner_id': line.partner_id and line.partner_id.id, #Custom Code } remaining_amount -= rs['amount'] #in case a corresponding move_line hasn't been found, we now try to assign the voucher amount #on existing invoices: we split voucher amount by most old first, but only for lines in the same currency if not move_lines_found: if currency_id == line_currency_id: if line.credit: amount = min(amount_unreconciled, abs(total_debit)) rs['amount'] = 0.0 total_debit -= amount else: amount = min(amount_unreconciled, abs(total_credit)) rs['amount'] = 0.0 total_credit -= amount if rs['amount_unreconciled'] == rs['amount']: rs['reconcile'] = True if rs['type'] == 'cr': default['value']['line_cr_ids'].append(rs) else: default['value']['line_dr_ids'].append(rs) if len(default['value']['line_cr_ids']) > 0: default['value']['pre_line'] = 1 elif len(default['value']['line_dr_ids']) > 0: default['value']['pre_line'] = 1 default['value'][ 'writeoff_amount'] = self._compute_writeoff_amount( cr, uid, default['value']['line_dr_ids'], default['value']['line_cr_ids'], price, ttype) return default
_name = "stock.fill.inventory" _description = "Import Inventory" def _default_location(self, cr, uid, ids, context=None): try: location = self.pool.get("ir.model.data").get_object(cr, uid, "stock", "stock_location_stock") location.check_access_rule("read", context=context) location_id = location.id except (ValueError, orm.except_orm), e: return False return location_id or False _columns = { "location_id": fields.many2one("stock.location", "Location", required=True), "recursive": fields.boolean( "Include children", help="If checked, products contained in child locations of selected location will be included as well.", ), "set_stock_zero": fields.boolean( "Set to zero", help="If checked, all product quantities will be set to zero to help ensure a real physical inventory is done", ), } _defaults = {"location_id": _default_location} def view_init(self, cr, uid, fields_list, context=None): """ Creates view dynamically and adding fields at runtime. @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param context: A standard dictionary
class MailMessage(osv.Model): _name = "sale.comment" _description = "Sale Comment" _columns = { 'comment_date': fields.datetime('Comment Date'), 'seller_reply_date': fields.datetime('Seller Reply Date'), 'chase_comment_date': fields.datetime('Chase Comment Date'), 'comment_body': fields.html('Comment Contents', help='Automatically sanitized HTML contents'), 'seller_reply_body': fields.html('Seller Reply Contents', help='Automatically sanitized HTML contents'), 'chase_comment_body': fields.html('Chase Comment Contents', help='Automatically sanitized HTML contents'), 'rating': fields.integer("Rating"), 'order_id': fields.many2one('sale.order', "Related Sale Order ID"), 'order_line_id': fields.many2one('sale.order.line', "Related Sale Order Line ID"), 'author_id': fields.many2one('res.partner', 'Author', help="Author of the comment."), 'product_tmp_id': fields.many2one('product.template', "Related Product Template ID"), 'website_published': fields.boolean( 'Published', help="Visible on the website as a comment", copy=False, ), 'is_anonymous': fields.boolean('Is Anonymous', default=False), 'wait_business_reply': fields.boolean('Wait for the business reply', default=True), } def _prepare_comment(self, cr, uid, line_id, context=None): line_obj = self.pool.get('sale.order.line') if context is None: context = {} description = context.get('description') rating = context.get('rating') website_published = context.get('website_published') is_anonymous = context.get('is_anonymous') values = {} for line in line_obj.browse(cr, uid, line_id, context=context): values['comment_date'] = time.strftime("%Y-%m-%d %H:%M:%S") values['comment_body'] = description values['rating'] = rating values['order_id'] = line.order_id.id values['order_line_id'] = line.id values['author_id'] = line.order_partner_id.id values['product_tmp_id'] = line.product_id.product_tmpl_id.id values['website_published'] = website_published values['is_anonymous'] = is_anonymous values['wait_business_reply'] = True return values def create_comment(self, cr, uid, line_id, context=None): """Create the comment by those values :param line_id: sale order line ID(int) """ if context is None: context = {} value = self._prepare_comment(cr, uid, line_id, context=context) comm_ids = self.create(cr, uid, value, context=context) return comm_ids
# -*- coding: utf-8 -*- from openerp.osv import orm, fields def selection_fn(obj, cr, uid, context=None): return list(enumerate(["Corge", "Grault", "Wheee", "Moog"])) def function_fn(model, cr, uid, ids, field_name, arg, context): return dict((id, 3) for id in ids) def function_fn_write(model, cr, uid, id, field_name, field_value, fnct_inv_arg, context): """ just so CreatorCase.export can be used """ pass models = [ ('boolean', fields.boolean()), ('integer', fields.integer()), ('float', fields.float()), ('decimal', fields.float(digits=(16, 3))), ('string.bounded', fields.char('unknown', size=16)), ('string.required', fields.char('unknown', size=None, required=True)), ('string', fields.char('unknown', size=None)), ('date', fields.date()), ('datetime', fields.datetime()), ('text', fields.text()), ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux"), (4, '')])), # here use size=-1 to store the values as integers instead of strings ('selection.function', fields.selection(selection_fn, size=-1)), # just relate to an integer ('many2one', fields.many2one('export.integer')), ('one2many', fields.one2many('export.one2many.child', 'parent_id')),
class project_issue(osv.Model): _name = "project.issue" _description = "Project Issue" _order = "priority desc, create_date desc" _inherit = ['mail.thread', 'ir.needaction_mixin'] _mail_post_access = 'read' def _get_default_partner(self, cr, uid, context=None): if context is None: context = {} if 'default_project_id' in context: project = self.pool.get('project.project').browse( cr, uid, context['default_project_id'], context=context) if project and project.partner_id: return project.partner_id.id return False def _get_default_stage_id(self, cr, uid, context=None): """ Gives default stage_id """ if context is None: context = {} return self.stage_find(cr, uid, [], context.get('default_project_id'), [('fold', '=', False)], context=context) def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): if context is None: context = {} access_rights_uid = access_rights_uid or uid stage_obj = self.pool.get('project.task.type') order = stage_obj._order # lame hack to allow reverting search, should just work in the trivial case if read_group_order == 'stage_id desc': order = "%s desc" % order # retrieve team_id from the context, add them to already fetched columns (ids) if 'default_project_id' in context: search_domain = [ '|', ('project_ids', '=', context['default_project_id']), ('id', 'in', ids) ] else: search_domain = [('id', 'in', ids)] # perform search stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) # restore order of the search result.sort( lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) fold = {} for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): fold[stage.id] = stage.fold or False return result, fold def _compute_day(self, cr, uid, ids, fields, args, context=None): """ @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @param ids: List of Openday’s IDs @return: difference between current date and log date @param context: A standard dictionary for contextual values """ Calendar = self.pool['resource.calendar'] res = dict((res_id, {}) for res_id in ids) for issue in self.browse(cr, uid, ids, context=context): values = { 'day_open': 0.0, 'day_close': 0.0, 'working_hours_open': 0.0, 'working_hours_close': 0.0, 'days_since_creation': 0.0, 'inactivity_days': 0.0, } # if the working hours on the project are not defined, use default ones (8 -> 12 and 13 -> 17 * 5), represented by None calendar_id = None if issue.project_id and issue.project_id.resource_calendar_id: calendar_id = issue.project_id.resource_calendar_id.id dt_create_date = datetime.strptime(issue.create_date, DEFAULT_SERVER_DATETIME_FORMAT) if issue.date_open: dt_date_open = datetime.strptime( issue.date_open, DEFAULT_SERVER_DATETIME_FORMAT) values['day_open'] = (dt_date_open - dt_create_date ).total_seconds() / (24.0 * 3600) values['working_hours_open'] = Calendar._interval_hours_get( cr, uid, calendar_id, dt_create_date, dt_date_open, timezone_from_uid=issue.user_id.id or uid, exclude_leaves=False, context=context) if issue.date_closed: dt_date_closed = datetime.strptime( issue.date_closed, DEFAULT_SERVER_DATETIME_FORMAT) values['day_close'] = (dt_date_closed - dt_create_date ).total_seconds() / (24.0 * 3600) values['working_hours_close'] = Calendar._interval_hours_get( cr, uid, calendar_id, dt_create_date, dt_date_closed, timezone_from_uid=issue.user_id.id or uid, exclude_leaves=False, context=context) days_since_creation = datetime.today() - dt_create_date values['days_since_creation'] = days_since_creation.days if issue.date_action_last: inactive_days = datetime.today() - datetime.strptime( issue.date_action_last, DEFAULT_SERVER_DATETIME_FORMAT) elif issue.date_last_stage_update: inactive_days = datetime.today() - datetime.strptime( issue.date_last_stage_update, DEFAULT_SERVER_DATETIME_FORMAT) else: inactive_days = datetime.today() - datetime.strptime( issue.create_date, DEFAULT_SERVER_DATETIME_FORMAT) values['inactivity_days'] = inactive_days.days # filter only required values for field in fields: res[issue.id][field] = values[field] return res def on_change_project(self, cr, uid, ids, project_id, context=None): if project_id: project = self.pool.get('project.project').browse(cr, uid, project_id, context=context) if project and project.partner_id: return {'value': {'partner_id': project.partner_id.id}} return {'value': {'partner_id': False}} _columns = { 'id': fields.integer('ID', readonly=True), 'name': fields.char('Issue', required=True), 'active': fields.boolean('Active', required=False), 'create_date': fields.datetime('Creation Date', readonly=True, select=True), 'write_date': fields.datetime('Update Date', readonly=True), 'days_since_creation': fields.function(_compute_day, string='Days since creation date', \ multi='compute_day', type="integer", help="Difference in days between creation date and current date"), 'date_deadline': fields.date('Deadline'), 'team_id': fields.many2one('crm.team', 'Sales Team', oldname='section_id',\ select=True, help='Sales team to which Case belongs to.\ Define Responsible user and Email account for mail gateway.' ), 'partner_id': fields.many2one('res.partner', 'Contact', select=1), 'company_id': fields.many2one('res.company', 'Company'), 'description': fields.text('Private Note'), 'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State', track_visibility='onchange', help="A Issue's kanban state indicates special situations affecting it:\n" " * Normal is the default situation\n" " * Blocked indicates something is preventing the progress of this issue\n" " * Ready for next stage indicates the issue is ready to be pulled to the next stage", required=True), 'email_from': fields.char('Email', size=128, help="These people will receive email.", select=1), 'email_cc': fields.char('Watchers Emails', size=256, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), 'date_open': fields.datetime('Assigned', readonly=True, select=True), # Project Issue fields 'date_closed': fields.datetime('Closed', readonly=True, select=True), 'date': fields.datetime('Date'), 'date_last_stage_update': fields.datetime('Last Stage Update', select=True), 'channel': fields.char('Channel', help="Communication channel."), 'tag_ids': fields.many2many('project.tags', string='Tags'), 'priority': fields.selection([('0','Low'), ('1','Normal'), ('2','High')], 'Priority', select=True), 'stage_id': fields.many2one ('project.task.type', 'Stage', track_visibility='onchange', select=True, domain="[('project_ids', '=', project_id)]", copy=False), 'project_id': fields.many2one('project.project', 'Project', track_visibility='onchange', select=True), 'duration': fields.float('Duration'), 'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]", help="You can link this issue to an existing task or directly create a new one from here"), 'day_open': fields.function(_compute_day, string='Days to Assign', multi='compute_day', type="float", store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}), 'day_close': fields.function(_compute_day, string='Days to Close', multi='compute_day', type="float", store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}), 'user_id': fields.many2one('res.users', 'Assigned to', required=False, select=1, track_visibility='onchange'), 'working_hours_open': fields.function(_compute_day, string='Working Hours to assign the Issue', multi='compute_day', type="float", store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}), 'working_hours_close': fields.function(_compute_day, string='Working Hours to close the Issue', multi='compute_day', type="float", store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}), 'inactivity_days': fields.function(_compute_day, string='Days since last action', multi='compute_day', type="integer", help="Difference in days between last action and current date"), 'color': fields.integer('Color Index'), 'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True), 'date_action_last': fields.datetime('Last Action', readonly=1), 'date_action_next': fields.datetime('Next Action', readonly=1), 'legend_blocked': fields.related("stage_id", "legend_blocked", type="char", string='Kanban Blocked Explanation'), 'legend_done': fields.related("stage_id", "legend_done", type="char", string='Kanban Valid Explanation'), 'legend_normal': fields.related("stage_id", "legend_normal", type="char", string='Kanban Ongoing Explanation'), } _defaults = { 'active': 1, 'team_id': lambda s, cr, uid, c: s.pool['crm.team']._get_default_team_id( cr, uid, context=c), 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c), 'company_id': lambda s, cr, uid, c: s.pool['res.users']._get_company( cr, uid, context=c), 'priority': '0', 'kanban_state': 'normal', 'date_last_stage_update': fields.datetime.now, 'user_id': lambda obj, cr, uid, context: uid, } _group_by_full = {'stage_id': _read_group_stage_ids} def copy(self, cr, uid, id, default=None, context=None): issue = self.read(cr, uid, [id], ['name'], context=context)[0] if not default: default = {} default = default.copy() default.update(name=_('%s (copy)') % (issue['name'])) return super(project_issue, self).copy(cr, uid, id, default=default, context=context) def create(self, cr, uid, vals, context=None): context = dict(context or {}) if vals.get('project_id') and not context.get('default_project_id'): context['default_project_id'] = vals.get('project_id') if vals.get('user_id'): vals['date_open'] = fields.datetime.now() if 'stage_id' in vals: vals.update( self.onchange_stage_id(cr, uid, None, vals.get('stage_id'), context=context)['value']) # context: no_log, because subtype already handle this create_context = dict(context, mail_create_nolog=True) return super(project_issue, self).create(cr, uid, vals, context=create_context) def write(self, cr, uid, ids, vals, context=None): # stage change: update date_last_stage_update if 'stage_id' in vals: vals.update( self.onchange_stage_id(cr, uid, ids, vals.get('stage_id'), context=context)['value']) vals['date_last_stage_update'] = fields.datetime.now() if 'kanban_state' not in vals: vals['kanban_state'] = 'normal' # user_id change: update date_start if vals.get('user_id'): vals['date_open'] = fields.datetime.now() return super(project_issue, self).write(cr, uid, ids, vals, context) def onchange_task_id(self, cr, uid, ids, task_id, context=None): if not task_id: return {'value': {}} task = self.pool.get('project.task').browse(cr, uid, task_id, context=context) return { 'value': { 'user_id': task.user_id.id, } } def onchange_partner_id(self, cr, uid, ids, partner_id, context=None): """ This function returns value of partner email address based on partner :param part: Partner's id """ if partner_id: partner = self.pool['res.partner'].browse(cr, uid, partner_id, context) return {'value': {'email_from': partner.email}} return {'value': {'email_from': False}} def get_empty_list_help(self, cr, uid, help, context=None): context = dict(context or {}) context['empty_list_help_model'] = 'project.project' context['empty_list_help_id'] = context.get('default_project_id') context['empty_list_help_document_name'] = _("issues") return super(project_issue, self).get_empty_list_help(cr, uid, help, context=context) # ------------------------------------------------------- # Stage management # ------------------------------------------------------- def onchange_stage_id(self, cr, uid, ids, stage_id, context=None): if not stage_id: return {'value': {}} stage = self.pool['project.task.type'].browse(cr, uid, stage_id, context=context) if stage.fold: return {'value': {'date_closed': fields.datetime.now()}} return {'value': {'date_closed': False}} def stage_find(self, cr, uid, cases, team_id, domain=[], order='sequence', context=None): """ Override of the base.stage method Parameter of the stage search taken from the issue: - type: stage type must be the same or 'both' - team_id: if set, stages must belong to this team or be a default case """ if isinstance(cases, (int, long)): cases = self.browse(cr, uid, cases, context=context) # collect all team_ids team_ids = [] if team_id: team_ids.append(team_id) for task in cases: if task.project_id: team_ids.append(task.project_id.id) # OR all team_ids and OR with case_default search_domain = [] if team_ids: search_domain += [('|')] * (len(team_ids) - 1) for team_id in team_ids: search_domain.append(('project_ids', '=', team_id)) search_domain += list(domain) # perform search, return the first found stage_ids = self.pool.get('project.task.type').search(cr, uid, search_domain, order=order, context=context) if stage_ids: return stage_ids[0] return False # ------------------------------------------------------- # Mail gateway # ------------------------------------------------------- def _track_subtype(self, cr, uid, ids, init_values, context=None): record = self.browse(cr, uid, ids[0], context=context) if 'kanban_state' in init_values and record.kanban_state == 'blocked': return 'project_issue.mt_issue_blocked' elif 'kanban_state' in init_values and record.kanban_state == 'done': return 'project_issue.mt_issue_ready' elif 'user_id' in init_values and record.user_id: # assigned -> new return 'project_issue.mt_issue_new' elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1: # start stage -> new return 'project_issue.mt_issue_new' elif 'stage_id' in init_values: return 'project_issue.mt_issue_stage' return super(project_issue, self)._track_subtype(cr, uid, ids, init_values, context=context) def _notification_group_recipients(self, cr, uid, ids, message, recipients, done_ids, group_data, context=None): """ Override the mail.thread method to handle project users and officers recipients. Indeed those will have specific action in their notification emails: creating tasks, assigning it. """ group_project_user = self.pool['ir.model.data'].xmlid_to_res_id( cr, uid, 'project.group_project_user') for recipient in recipients: if recipient.id in done_ids: continue if recipient.user_ids and group_project_user in recipient.user_ids[ 0].groups_id.ids: group_data['group_project_user'] |= recipient done_ids.add(recipient.id) return super(project_issue, self)._notification_group_recipients(cr, uid, ids, message, recipients, done_ids, group_data, context=context) def _notification_get_recipient_groups(self, cr, uid, ids, message, recipients, context=None): res = super(project_issue, self)._notification_get_recipient_groups(cr, uid, ids, message, recipients, context=context) new_action_id = self.pool['ir.model.data'].xmlid_to_res_id( cr, uid, 'project_issue.project_issue_categ_act0') take_action = self._notification_link_helper(cr, uid, ids, 'assign', context=context) new_action = self._notification_link_helper(cr, uid, ids, 'new', context=context, action_id=new_action_id) task_record = self.browse(cr, uid, ids[0], context=context) actions = [] if not task_record.user_id: actions.append({'url': take_action, 'title': _('I take it')}) else: actions.append({'url': new_action, 'title': _('New Issue')}) res['group_project_user'] = {'actions': actions} return res @api.cr_uid_context def message_get_reply_to(self, cr, uid, ids, default=None, context=None): """ Override to get the reply_to of the parent project. """ issues = self.browse(cr, SUPERUSER_ID, ids, context=context) project_ids = set( [issue.project_id.id for issue in issues if issue.project_id]) aliases = self.pool['project.project'].message_get_reply_to( cr, uid, list(project_ids), default=default, context=context) return dict( (issue.id, aliases.get(issue.project_id and issue.project_id.id or 0, False)) for issue in issues) def message_get_suggested_recipients(self, cr, uid, ids, context=None): recipients = super(project_issue, self).message_get_suggested_recipients( cr, uid, ids, context=context) try: for issue in self.browse(cr, uid, ids, context=context): if issue.partner_id: issue._message_add_suggested_recipient( recipients, partner=issue.partner_id, reason=_('Customer')) elif issue.email_from: issue._message_add_suggested_recipient( recipients, email=issue.email_from, reason=_('Customer Email')) except AccessError: # no read access rights -> just ignore suggested recipients because this imply modifying followers pass return recipients def email_split(self, cr, uid, ids, msg, context=None): email_list = tools.email_split((msg.get('to') or '') + ',' + (msg.get('cc') or '')) # check left-part is not already an alias issue_ids = self.browse(cr, uid, ids, context=context) aliases = [ issue.project_id.alias_name for issue in issue_ids if issue.project_id ] return filter(lambda x: x.split('@')[0] not in aliases, email_list) def message_new(self, cr, uid, msg, custom_values=None, context=None): """ Overrides mail_thread message_new that is called by the mailgateway through message_process. This override updates the document according to the email. """ if custom_values is None: custom_values = {} context = dict(context or {}, state_to='draft') defaults = { 'name': msg.get('subject') or _("No Subject"), 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), 'partner_id': msg.get('author_id', False), 'user_id': False, } defaults.update(custom_values) res_id = super(project_issue, self).message_new(cr, uid, msg, custom_values=defaults, context=context) email_list = self.email_split(cr, uid, [res_id], msg, context=context) partner_ids = self._find_partner_from_emails(cr, uid, [res_id], email_list, force_create=True, context=context) self.message_subscribe(cr, uid, [res_id], partner_ids, context=context) return res_id def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): """ Override to update the issue according to the email. """ email_list = self.email_split(cr, uid, ids, msg, context=context) partner_ids = self._find_partner_from_emails(cr, uid, ids, email_list, force_create=True, context=context) self.message_subscribe(cr, uid, ids, partner_ids, context=context) return super(project_issue, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) @api.cr_uid_ids_context @api.returns('mail.message', lambda value: value.id) def message_post(self, cr, uid, thread_id, subtype=None, context=None, **kwargs): """ Overrides mail_thread message_post so that we can set the date of last action field when a new message is posted on the issue. """ if context is None: context = {} res = super(project_issue, self).message_post(cr, uid, thread_id, subtype=subtype, context=context, **kwargs) if thread_id and subtype: self.write(cr, SUPERUSER_ID, thread_id, {'date_action_last': fields.datetime.now()}, context=context) return res
res[carrier.id] = { 'price': price, 'available': available } return res _columns = { 'name': fields.char('Delivery Method', required=True, translate=True), 'sequence': fields.integer('Sequence', help="Determine the display order"), 'partner_id': fields.many2one('res.partner', 'Transport Company', required=True, help="The partner that is doing the delivery service."), 'product_id': fields.many2one('product.product', 'Delivery Product', required=True), 'grids_id': fields.one2many('delivery.grid', 'carrier_id', 'Delivery Grids'), 'available' : fields.function(get_price, string='Available',type='boolean', multi='price', help="Is the carrier method possible with the current order."), 'price' : fields.function(get_price, string='Price', multi='price'), 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the delivery carrier without removing it."), 'normal_price': fields.float('Normal Price', help="Keep empty if the pricing depends on the advanced pricing per destination"), 'free_if_more_than': fields.boolean('Free If Order Total Amount Is More Than', help="If the order is more expensive than a certain amount, the customer can benefit from a free shipping"), 'amount': fields.float('Amount', help="Amount of the order to benefit from a free shipping, expressed in the company currency"), 'use_detailed_pricelist': fields.boolean('Advanced Pricing per Destination', help="Check this box if you want to manage delivery prices that depends on the destination, the weight, the total of the order, etc."), 'pricelist_ids': fields.one2many('delivery.grid', 'carrier_id', 'Advanced Pricing'), } _defaults = { 'active': 1, 'free_if_more_than': False, 'sequence': 10, } def grid_get(self, cr, uid, ids, contact_id, context=None): contact = self.pool.get('res.partner').browse(cr, uid, contact_id, context=context)
class employee_missions(orm.Model): """ To manage employee_missions """ _inherit = "hr.employee.mission" _columns = { 'fuel_requested': fields.boolean('Fuel Requested', readonly=True), } def request_fuel(self, cr, uid, ids, context=None): """ Request fuel and car for mission from mission if the transport type is car. @param ids: :List of id of mission @return: True or False """ for mission in self.browse(cr, uid, ids, context=context): fuel_request_obj = self.pool.get('fleet.vehicle.log.fuel') if mission.transport in ('1', '4') and not mission.fuel_requested: info = 'Mission:\t' + mission.name + '\nStart date:\t' + mission.start_date + ' - End date:\t' + mission.end_date + '\nTravel path:\t' + mission.travel_path request_dict = { 'notes': info, 'purpose': 'mission', 'payment_method': 'plan', 'plan_type': 'extra_fuel', 'state': 'draft', #'date_of_travel':mission.start_date, #'date_of_return':mission.end_date, } request_id = fuel_request_obj.create(cr, uid, request_dict, context=context) #creating mission request in confirmed_d state #wf_service = netsvc.LocalService("workflow") #wf_service.trg_validate(uid, 'fuel.request', request_id, 'confirmed_s', cr) #wf_service.trg_validate(uid, 'fuel.request', request_id, 'confirmed_d', cr) #for employee in mission.mission_line: #cr.execute("INSERT into fuel_reuest_emp_rel values(%s,%s)",(request_id,employee.employee_id.id,)) return self.write(cr, uid, ids, {'fuel_requested': True}) def mission_approved(self, cr, uid, ids, context=None): """ Workflow method change record state to 'approved' and Transfer Mission mission_fee to move @return: Boolean True """ voucher_obj = self.pool.get('account.voucher') voucher_line_obj = self.pool.get('account.voucher.line') account_period_obj = self.pool.get('account.period') for mission in self.browse(cr, uid, ids, context=context): mission_amount = mission.mission_fee if mission_amount <= 0: raise osv.except_osv(_('Warning!'), _('Mission fee should be more than zero')) if mission.mission_id.fees_account_id and mission.mission_id.journal_id and mission.mission_id.account_analytic_id: date = time.strftime('%Y-%m-%d') period = account_period_obj.find( cr, uid, dt=date, context={'company_id': mission.company_id.id})[0] voucher_dict = { 'company_id': mission.company_id.id, 'journal_id': mission.mission_id.journal_id.id, 'account_id': mission.mission_id.fees_account_id.id, 'period_id': period, 'name': mission.name + ' - ' + mission.start_date, 'amount': mission_amount, 'type': 'purchase', 'date': date, 'reference': 'HR/Mission/' + mission.name + ' - ' + mission.start_date, 'department_id': mission.department_id.id, 'currency': mission.mission_id.fees_currency_id.id, } voucher = voucher_obj.create(cr, uid, voucher_dict, context=context) voucher_line_dict = { 'voucher_id': voucher, 'account_id': mission.mission_id.fees_account_id.id, 'account_analytic_id': mission.mission_id.account_analytic_id.id, 'amount': mission_amount, 'type': 'dr', } voucher_line_obj.create(cr, uid, voucher_line_dict, context=context) vouch = voucher_obj.browse(cr, uid, voucher, context=context) self.request_fuel(cr, uid, ids, context=context) self.create_grant_rights(cr, uid, ids, context=context) return self.write(cr, uid, ids, { 'state': 'approved', 'voucher_number': vouch.number, }) else: raise osv.except_osv( _('Error!'), _("Please enter mission accounting details at the configuration of the mission destination" )) self.create_grant_rights(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state': 'approved'}, context=context)
'contributors': fields.text('Contributors', readonly=True), 'website': fields.char("Website", size=256, readonly=True), # attention: Incorrect field names !! # installed_version refers the latest version (the one on disk) # latest_version refers the installed version (the one in database) # published_version refers the version available on the repository 'installed_version': fields.function(_get_latest_version, string='Latest Version', type='char'), 'latest_version': fields.char('Installed Version', size=64, readonly=True), 'published_version': fields.char('Published Version', size=64, readonly=True), 'url': fields.char('URL', size=128, readonly=True), 'sequence': fields.integer('Sequence'), 'dependencies_id': fields.one2many('ir.module.module.dependency', 'module_id', 'Dependencies', readonly=True), 'auto_install': fields.boolean('Automatic Installation', help='An auto-installable module is automatically installed by the ' 'system when all its dependencies are satisfied. ' 'If the module has no dependency, it is always installed.'), 'state': fields.selection([ ('uninstallable', 'Not Installable'), ('uninstalled', 'Not Installed'), ('installed', 'Installed'), ('to upgrade', 'To be upgraded'), ('to remove', 'To be removed'), ('to install', 'To be installed') ], string='Status', readonly=True, select=True), 'demo': fields.boolean('Demo Data', readonly=True), 'license': fields.selection([ ('GPL-2', 'GPL Version 2'), ('GPL-2 or any later version', 'GPL-2 or later version'), ('GPL-3', 'GPL Version 3'), ('GPL-3 or any later version', 'GPL-3 or later version'),
class sio(osv.osv): _name = 'hr.training_sio' def schedule_iso(self, cr, uid, ids=None, context=None): if context is None: context = {} context = dict(context, mail_create_nolog=True) obj_warning = self.pool.get('warning.schedule') src_warning = obj_warning.search(cr, uid, [('name', '=', 'sio')]) brw_warning = obj_warning.browse(cr, uid, src_warning) lama = brw_warning[0] durasi = lama.date_warning obj = self.pool.get('hr.training_sio') src = obj.search(cr, uid, [('status', '=', True), ('state', '=', 'evaluation')]) for warning in obj.browse(cr, uid, src): tgl = warning.berlaku day_now = datetime.now() tgl_akhir = datetime.strptime(tgl, "%Y-%m-%d") nb_of_days = (tgl_akhir - day_now).days + 1 if nb_of_days <= durasi: obj.write(cr, uid, [warning.id], { 'warning_hari': nb_of_days, 'link_warning': lama.id }) else: obj.write(cr, uid, [warning.id], { 'warning_hari': nb_of_days, 'link_warning': False }) return {'type': 'ir.actions.act_window_close'} _columns = { 'employee_id': fields.many2one('hr.employee', 'Nama Peserta'), 'nik': fields.related('employee_id', 'nik', type='char', relation='hr.employee', string='NIK', readonly=True), 'department_id': fields.related('employee_id', 'department_id', type='many2one', readonly=True, relation='hr.department', string='Departemen', store=True), 'bukti': fields.binary('Bukti'), 'berlaku': fields.date('Masa Berlaku', required=True), 'nama_sertifikat': fields.related('analisa_id', 'nama_sertifikat', type='many2one', readonly=True, relation='sertifikat', string='Sertifikat'), 'iso': fields.related('analisa_id', 'iso', type='many2one', readonly=True, relation='iso', string='Nama SIO'), 'analisa_id': fields.many2one('hr_training.analisa', 'Nama Pelatihan'), 'link_warning': fields.many2one('warning.schedule'), 'warning_hari': fields.integer('Kadaluarsa'), 'status': fields.boolean('status'), 'state': fields.selection(TRAINING_STATES, 'Status', readonly=True, help="Status Training"), 'memo': fields.text('catatan'), } _defaults = { 'status': True, 'warning_hari': 100000, 'berlaku': fields.date.context_today, }
'sequence': fields.integer('Sequence'), 'company': fields.many2one('res.company', 'Company', required=True, ondelete='cascade'), 'ldap_server': fields.char('LDAP Server address', required=True), 'ldap_server_port': fields.integer('LDAP Server port', required=True), 'ldap_binddn': fields.char('LDAP binddn', help=("The user account on the LDAP server that is used to query " "the directory. Leave empty to connect anonymously.")), 'ldap_password': fields.char('LDAP password', help=("The password of the user account on the LDAP server that is " "used to query the directory.")), 'ldap_filter': fields.char('LDAP filter', required=True), 'ldap_base': fields.char('LDAP base', required=True), 'user': fields.many2one('res.users', 'Template User', help="User to copy when creating new users"), 'create_user': fields.boolean('Create user', help="Automatically create local user accounts for new users authenticating via LDAP"), 'ldap_tls': fields.boolean('Use TLS', help="Request secure TLS/SSL encryption when connecting to the LDAP server. " "This option requires a server with STARTTLS enabled, " "otherwise all authentication attempts will fail."), } _defaults = { 'ldap_server': '127.0.0.1', 'ldap_server_port': 389, 'sequence': 10, 'create_user': True, } class res_company(osv.osv):
class ResCompany(orm.Model): _inherit = 'res.company' _columns = { 'colipostefr_account': fields.char( 'Compte Principal', size=6, help=u"Nombre à 6 caractères.\n" u"La valeur par défaut est 964744"), 'colipostefr_world_account': fields.char( 'Compte International', size=6, help=u"Nombre à 6 caractères.\n" u"La valeur par défaut est 964744\n" u"Potentiellement c'est le même N° que le compte principal." u"Dans ce cas, vous pouvez laisser ce champ vide."), 'colipostefr_support_city': fields.char( 'Site de Prise en Charge', size=64, help="Site de prise en charge de la poste pour son client.\n" "Indiquer votre propre centre de rattachement"), 'colipostefr_support_city_code': fields.char( 'Code Site de Prise en Charge', size=6, help="Utile uniquement pour le module colissimo EDI " "(facultatif cependant)"), 'colipostefr_password': fields.char( 'mot de passe site web', help=u"le mot de passe doit être identique " u"à celui de votre espace client.\n" u"uniquement nécessaire pour les envois à l'étranger.\n" u"mettre un mot de passe complexe"), 'colipostefr_unittest_helper': fields.boolean( 'Unit Test Helper', help=u"Seulement utile pour les développeurs.\n" u"Si coché enregistre les données du picking ColiPoste\n" u"dans le dossier temporaire système du serveur.\n" u"Ce fichier peut être utilisé pour créer " u"de nouveaux tests unitaires python"), 'colipostefr_webservice_message': fields.boolean( u'Enregistre les Messages du Webservice', help=u"Pour ColiPoste International. \nSi coché, un commentaire " u"sera créé dans le bon de livraison\nsi la réponse du " u"web service contient un message additionnel."), 'colipostefr_repo_task_id': fields.many2one( 'repository.task', string=u"Tâche EDI", help=u"Liaison à la tâche qui exporte le fichier EDI.\n" u"Ne pas créer de tâche spécifique ici.\n" u"La valeur par défaut pour la principale société d'Odoo " u"ne doit pas changer.\nUtilisez une tâche créée " u"depuis le repository 'La Poste EDI'\n" u"(Configuration > File Exchange > File Repositories " u"> La Poste EDI)"), } _defaults = { 'colipostefr_account': '', 'colipostefr_support_city': '... PFC', 'colipostefr_password': '', 'colipostefr_unittest_helper': False, 'colipostefr_webservice_message': True, }
uid, id, {"url": url, "url_big": url_big, "url_medium": url_medium, "url_small": url_small}, context=context, ) else: return False return self.write(cr, uid, id, {"file_db_store": value}, context=context) _columns = { "name": fields.char("Image Title", size=64), "filename": fields.char("Filename", size=64), "extension": fields.char("file extension", oldname="extention"), "link": fields.boolean( "Link?", help="Images can be linked from files on " "your file system or remote (Preferred)" ), "file_db_store": fields.binary("Image stored in database"), "file": fields.function( _get_image, fnct_inv=_set_image, type="binary", string="File", filters="*.png,*.jpg,*.gif" ), "url": fields.char("File Location"), "url_big": fields.char("File Location Image Size Big"), "url_medium": fields.char("File Location Image Size Medium"), "url_small": fields.char("File Location Image Size Small"), "comments": fields.text("Comments"), "product_id": fields.many2one("product.product", "Product"), } _defaults = {"link": True}
if not ressource_parent_type_id: ressource_parent_type_id=directory.ressource_parent_type_id and directory.ressource_parent_type_id.id or False if not ressource_id: ressource_id=directory.ressource_id and directory.ressource_id or 0 res=self.search(cr,uid,[('id','<>',directory.id),('name','=',name),('parent_id','=',parent_id),('ressource_parent_type_id','=',ressource_parent_type_id),('ressource_id','=',ressource_id), ('revisionid', '=', revisionid)]) if len(res): return False if op=='create': res = self.search(cr, SUPERUSER_ID, [('name','=',name),('parent_id','=',parent_id),('ressource_parent_type_id','=',ressource_parent_type_id),('ressource_id','=',ressource_id), ('revisionid', '=', revisionid)]) if len(res): return False return True # Overridden methods for this entity _columns = { 'usedforspare': fields.boolean('Used for Spare',help="Drawings marked here will be used printing Spare Part Manual report."), 'revisionid': fields.integer('Revision Index', required=True), 'writable': fields.boolean('Writable'), 'datas': fields.function(_data_get,method=True,fnct_inv=_data_set,string='File Content',type="binary"), 'printout': fields.binary('Printout Content', help="Print PDF content."), 'preview': fields.binary('Preview Content', help="Static preview."), 'state':fields.selection(USED_STATES,'Status', help="The status of the product.", readonly="True", required=True), } _defaults = { 'usedforspare': lambda *a: False, 'revisionid': lambda *a: 0, 'writable': lambda *a: True, 'state': lambda *a: 'draft', }