Example #1
0
    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
Example #2
0
    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')
                                                                          'field_id': field.id, 'exception': e, 'stack': stack})
                    continue
        return res

    def _get_checklist_task_instance_ids(self, cr, uid, ids, context={}):
        if isinstance(ids, (int, long)):
            ids = [ids]
        return self.pool.get('checklist.task.instance').search(cr, uid, [('checklist_task_id', 'in', ids)])

    _columns = {
        'checklist_task_id': fields.many2one('checklist.task', 'Checklist Task', required=True, ondelete='cascade'),
        'checklist_id': fields.related('checklist_task_id', 'checklist_id', type='many2one', relation='checklist', string='Checklist'),
        'model_id': fields.related('checklist_id', 'model_id', type='many2one', relation='ir.model', string='Model'),
        'name': fields.related('checklist_task_id', 'name', type='char', size=128, string='Name'),
        'mandatory': fields.related('checklist_task_id', 'mandatory', type='boolean', string='Mandatory', help='Required to make active object'),
        'res_id': fields.integer('Resource ID', select=True, required=True),
        'active': fields.function(_get_activity, type='boolean', string='Active', store=False, multi='activity'),
        'sequence': fields.function(_get_activity, type='integer', string='Priority', store={
            'checklist.task': (_get_checklist_task_instance_ids, ['sequence'], 10),
        }, multi='activity'),
        'field_ids_to_fill': fields.function(_get_activity, type='one2many', relation='checklist.task.field',
                                             string='Fields to fill', store=False, multi='activity'),
        'field_ids_filled': fields.function(_get_activity, type='one2many', relation='checklist.task.field',
                                            string='Filled fields', store=False, multi='activity'),
        'progress_rate': fields.float('Progress Rate', digits=(16, 2)),
    }

    _defaults = {
        'progress_rate': 0.0,
        'sequence': 15,
    }
Example #4
0
class hr_expense_expense(osv.osv):
    def _amount(self, cr, uid, ids, field_name, arg, context=None):
        res = {}
        for expense in self.browse(cr, uid, ids, context=context):
            total = 0.0
            for line in expense.line_ids:
                total += line.unit_amount * line.unit_quantity
            res[expense.id] = total
        return res

    def _get_currency(self, cr, uid, context=None):
        user = self.pool.get('res.users').browse(cr,
                                                 uid, [uid],
                                                 context=context)[0]
        return user.company_id.currency_id.id

    _name = "hr.expense.expense"
    _inherit = ['mail.thread']
    _description = "Expense"
    _order = "id desc"
    _track = {
        'state': {
            'hr_expense.mt_expense_approved':
            lambda self, cr, uid, obj, ctx=None: obj['state'] == 'accepted',
            'hr_expense.mt_expense_refused':
            lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancelled',
            'hr_expense.mt_expense_confirmed':
            lambda self, cr, uid, obj, ctx=None: obj['state'] == 'confirm',
        },
    }

    _columns = {
        'name':
        fields.char('Description',
                    size=128,
                    required=True,
                    readonly=True,
                    states={
                        'draft': [('readonly', False)],
                        'confirm': [('readonly', False)]
                    }),
        'id':
        fields.integer('Sheet ID', readonly=True),
        'date':
        fields.date('Date',
                    select=True,
                    readonly=True,
                    states={
                        'draft': [('readonly', False)],
                        'confirm': [('readonly', False)]
                    }),
        'journal_id':
        fields.many2one('account.journal',
                        'Force Journal',
                        help="The journal used when the expense is done."),
        'employee_id':
        fields.many2one('hr.employee',
                        "Employee",
                        required=True,
                        readonly=True,
                        states={
                            'draft': [('readonly', False)],
                            'confirm': [('readonly', False)]
                        }),
        'user_id':
        fields.many2one('res.users', 'User', required=True),
        'date_confirm':
        fields.date(
            'Confirmation Date',
            select=True,
            help=
            "Date of the confirmation of the sheet expense. It's filled when the button Confirm is pressed."
        ),
        'date_valid':
        fields.date(
            'Validation Date',
            select=True,
            help=
            "Date of the acceptation of the sheet expense. It's filled when the button Accept is pressed."
        ),
        'user_valid':
        fields.many2one('res.users',
                        'Validation By',
                        readonly=True,
                        states={
                            'draft': [('readonly', False)],
                            'confirm': [('readonly', False)]
                        }),
        'account_move_id':
        fields.many2one('account.move', 'Ledger Posting'),
        'line_ids':
        fields.one2many('hr.expense.line',
                        'expense_id',
                        'Expense Lines',
                        readonly=True,
                        states={'draft': [('readonly', False)]}),
        'note':
        fields.text('Note'),
        'amount':
        fields.function(_amount,
                        string='Total Amount',
                        digits_compute=dp.get_precision('Account')),
        'voucher_id':
        fields.many2one('account.voucher', "Employee's Receipt"),
        'currency_id':
        fields.many2one('res.currency',
                        'Currency',
                        required=True,
                        readonly=True,
                        states={
                            'draft': [('readonly', False)],
                            'confirm': [('readonly', False)]
                        }),
        'department_id':
        fields.many2one('hr.department',
                        'Department',
                        readonly=True,
                        states={
                            'draft': [('readonly', False)],
                            'confirm': [('readonly', False)]
                        }),
        'company_id':
        fields.many2one('res.company', 'Company', required=True),
        'state':
        fields.selection(
            [
                ('draft', 'New'),
                ('cancelled', 'Refused'),
                ('confirm', 'Waiting Approval'),
                ('accepted', 'Approved'),
                ('done', 'Waiting Payment'),
                ('paid', 'Paid'),
            ],
            'Status',
            readonly=True,
            track_visibility='onchange',
            help=
            'When the expense request is created the status is \'Draft\'.\n It is confirmed by the user and request is sent to admin, the status is \'Waiting Confirmation\'.\
            \nIf the admin accepts it, the status is \'Accepted\'.\n If the accounting entries are made for the expense request, the status is \'Waiting Payment\'.'
        ),
    }
    _defaults = {
        'company_id':
        lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(
            cr, uid, 'hr.employee', context=c),
        'date':
        fields.date.context_today,
        'state':
        'draft',
        'employee_id':
        _employee_get,
        'user_id':
        lambda cr, uid, id, c={}: id,
        'currency_id':
        _get_currency,
    }

    def copy(self, cr, uid, id, default=None, context=None):
        if default is None:
            default = {}
        default.update(account_move_id=False,
                       voucher_id=False,
                       date_confirm=False,
                       date_valid=False,
                       user_valid=False)
        return super(hr_expense_expense, self).copy(cr,
                                                    uid,
                                                    id,
                                                    default=default,
                                                    context=context)

    def unlink(self, cr, uid, ids, context=None):
        for rec in self.browse(cr, uid, ids, context=context):
            if rec.state != 'draft':
                raise osv.except_osv(_('Warning!'),
                                     _('You can only delete draft expenses!'))
        return super(hr_expense_expense, self).unlink(cr, uid, ids, context)

    def onchange_currency_id(self,
                             cr,
                             uid,
                             ids,
                             currency_id=False,
                             company_id=False,
                             context=None):
        res = {'value': {'journal_id': False}}
        journal_ids = self.pool.get('account.journal').search(
            cr,
            uid, [('type', '=', 'purchase'), ('currency', '=', currency_id),
                  ('company_id', '=', company_id)],
            context=context)
        if journal_ids:
            res['value']['journal_id'] = journal_ids[0]
        return res

    def onchange_employee_id(self, cr, uid, ids, employee_id, context=None):
        emp_obj = self.pool.get('hr.employee')
        department_id = False
        company_id = False
        if employee_id:
            employee = emp_obj.browse(cr, uid, employee_id, context=context)
            department_id = employee.department_id.id
            company_id = employee.company_id.id
        return {
            'value': {
                'department_id': department_id,
                'company_id': company_id
            }
        }

    def expense_confirm(self, cr, uid, ids, context=None):
        for expense in self.browse(cr, uid, ids):
            if expense.employee_id and expense.employee_id.parent_id.user_id:
                self.message_subscribe_users(
                    cr,
                    uid, [expense.id],
                    user_ids=[expense.employee_id.parent_id.user_id.id])
        return self.write(cr,
                          uid,
                          ids, {
                              'state': 'confirm',
                              'date_confirm': time.strftime('%Y-%m-%d')
                          },
                          context=context)

    def expense_accept(self, cr, uid, ids, context=None):
        return self.write(cr,
                          uid,
                          ids, {
                              'state': 'accepted',
                              'date_valid': time.strftime('%Y-%m-%d'),
                              'user_valid': uid
                          },
                          context=context)

    def expense_canceled(self, cr, uid, ids, context=None):
        return self.write(cr,
                          uid,
                          ids, {'state': 'cancelled'},
                          context=context)

    def account_move_get(self, cr, uid, expense_id, context=None):
        '''
        This method prepare the creation of the account move related to the given expense.

        :param expense_id: Id of voucher for which we are creating account_move.
        :return: mapping between fieldname and value of account move to create
        :rtype: dict
        '''
        journal_obj = self.pool.get('account.journal')
        expense = self.browse(cr, uid, expense_id, context=context)
        company_id = expense.company_id.id
        date = expense.date_confirm
        ref = expense.name
        journal_id = False
        if expense.journal_id:
            journal_id = expense.journal_id.id
        else:
            journal_id = journal_obj.search(cr, uid,
                                            [('type', '=', 'purchase'),
                                             ('company_id', '=', company_id)])
            if not journal_id:
                raise osv.except_osv(
                    _('Error!'),
                    _("No expense journal found. Please make sure you have a journal with type 'purchase' configured."
                      ))
            journal_id = journal_id[0]
        return self.pool.get('account.move').account_move_prepare(
            cr,
            uid,
            journal_id,
            date=date,
            ref=ref,
            company_id=company_id,
            context=context)

    def line_get_convert(self, cr, uid, x, part, date, context=None):
        partner_id = self.pool.get('res.partner')._find_accounting_partner(
            part).id
        return {
            'date_maturity':
            x.get('date_maturity', False),
            'partner_id':
            partner_id,
            'name':
            x['name'][:64],
            'date':
            date,
            'debit':
            x['price'] > 0 and x['price'],
            'credit':
            x['price'] < 0 and -x['price'],
            'account_id':
            x['account_id'],
            'analytic_lines':
            x.get('analytic_lines', False),
            'amount_currency':
            x['price'] > 0 and abs(x.get('amount_currency', False))
            or -abs(x.get('amount_currency', False)),
            'currency_id':
            x.get('currency_id', False),
            'tax_code_id':
            x.get('tax_code_id', False),
            'tax_amount':
            x.get('tax_amount', False),
            'ref':
            x.get('ref', False),
            'quantity':
            x.get('quantity', 1.00),
            'product_id':
            x.get('product_id', False),
            'product_uom_id':
            x.get('uos_id', False),
            'analytic_account_id':
            x.get('account_analytic_id', False),
        }

    def compute_expense_totals(self,
                               cr,
                               uid,
                               exp,
                               company_currency,
                               ref,
                               account_move_lines,
                               context=None):
        '''
        internal method used for computation of total amount of an expense in the company currency and
        in the expense currency, given the account_move_lines that will be created. It also do some small
        transformations at these account_move_lines (for multi-currency purposes)
        
        :param account_move_lines: list of dict
        :rtype: tuple of 3 elements (a, b ,c)
            a: total in company currency
            b: total in hr.expense currency
            c: account_move_lines potentially modified
        '''
        cur_obj = self.pool.get('res.currency')
        if context is None:
            context = {}
        context.update({'date': exp.date_confirm or time.strftime('%Y-%m-%d')})
        total = 0.0
        total_currency = 0.0
        for i in account_move_lines:
            if exp.currency_id.id != company_currency:
                i['currency_id'] = exp.currency_id.id
                i['amount_currency'] = i['price']
                i['price'] = cur_obj.compute(cr,
                                             uid,
                                             exp.currency_id.id,
                                             company_currency,
                                             i['price'],
                                             context=context)
            else:
                i['amount_currency'] = False
                i['currency_id'] = False
            total -= i['price']
            total_currency -= i['amount_currency'] or i['price']
        return total, total_currency, account_move_lines

    def action_receipt_create(self, cr, uid, ids, context=None):
        '''
        main function that is called when trying to create the accounting entries related to an expense
        '''
        move_obj = self.pool.get('account.move')
        for exp in self.browse(cr, uid, ids, context=context):
            if not exp.employee_id.address_home_id:
                raise osv.except_osv(
                    _('Error!'), _('The employee must have a home address.'))
            if not exp.employee_id.address_home_id.property_account_payable.id:
                raise osv.except_osv(
                    _('Error!'),
                    _('The employee must have a payable account set on his home address.'
                      ))
            company_currency = exp.company_id.currency_id.id
            diff_currency_p = exp.currency_id.id <> company_currency

            #create the move that will contain the accounting entries
            move_id = move_obj.create(cr,
                                      uid,
                                      self.account_move_get(cr,
                                                            uid,
                                                            exp.id,
                                                            context=context),
                                      context=context)

            #one account.move.line per expense line (+taxes..)
            eml = self.move_line_get(cr, uid, exp.id, context=context)

            #create one more move line, a counterline for the total on payable account
            total, total_currency, eml = self.compute_expense_totals(
                cr, uid, exp, company_currency, exp.name, eml, context=context)
            acc = exp.employee_id.address_home_id.property_account_payable.id
            eml.append({
                'type':
                'dest',
                'name':
                '/',
                'price':
                total,
                'account_id':
                acc,
                'date_maturity':
                exp.date_confirm,
                'amount_currency':
                diff_currency_p and total_currency or False,
                'currency_id':
                diff_currency_p and exp.currency_id.id or False,
                'ref':
                exp.name
            })

            #convert eml into an osv-valid format
            lines = map(
                lambda x:
                (0, 0,
                 self.line_get_convert(cr,
                                       uid,
                                       x,
                                       exp.employee_id.address_home_id,
                                       exp.date_confirm,
                                       context=context)), eml)
            move_obj.write(cr,
                           uid, [move_id], {'line_id': lines},
                           context=context)
            self.write(cr,
                       uid,
                       ids, {
                           'account_move_id': move_id,
                           'state': 'done'
                       },
                       context=context)
        return True

    def move_line_get(self, cr, uid, expense_id, context=None):
        res = []
        tax_obj = self.pool.get('account.tax')
        cur_obj = self.pool.get('res.currency')
        if context is None:
            context = {}
        exp = self.browse(cr, uid, expense_id, context=context)
        company_currency = exp.company_id.currency_id.id

        for line in exp.line_ids:
            mres = self.move_line_get_item(cr, uid, line, context)
            if not mres:
                continue
            res.append(mres)
            tax_code_found = False

            #Calculate tax according to default tax on product
            taxes = []
            #Taken from product_id_onchange in account.invoice
            if line.product_id:
                fposition_id = False
                fpos_obj = self.pool.get('account.fiscal.position')
                fpos = fposition_id and fpos_obj.browse(
                    cr, uid, fposition_id, context=context) or False
                product = line.product_id
                taxes = product.supplier_taxes_id
                #If taxes are not related to the product, maybe they are in the account
                if not taxes:
                    a = product.property_account_expense.id  #Why is not there a check here?
                    if not a:
                        a = product.categ_id.property_account_expense_categ.id
                    a = fpos_obj.map_account(cr, uid, fpos, a)
                    taxes = a and self.pool.get('account.account').browse(
                        cr, uid, a, context=context).tax_ids or False
                tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
            if not taxes:
                continue
            #Calculating tax on the line and creating move?
            for tax in tax_obj.compute_all(cr, uid, taxes, line.unit_amount,
                                           line.unit_quantity, line.product_id,
                                           exp.user_id.partner_id)['taxes']:
                tax_code_id = tax['base_code_id']
                tax_amount = line.total_amount * tax['base_sign']
                if tax_code_found:
                    if not tax_code_id:
                        continue
                    res.append(self.move_line_get_item(cr, uid, line, context))
                    res[-1]['price'] = 0.0
                    res[-1]['account_analytic_id'] = False
                elif not tax_code_id:
                    continue
                tax_code_found = True
                res[-1]['tax_code_id'] = tax_code_id
                res[-1]['tax_amount'] = cur_obj.compute(
                    cr,
                    uid,
                    exp.currency_id.id,
                    company_currency,
                    tax_amount,
                    context={'date': exp.date_confirm})
                ##
                is_price_include = tax_obj.read(cr, uid, tax['id'],
                                                ['price_include'],
                                                context)['price_include']
                if is_price_include:
                    ## We need to deduce the price for the tax
                    res[-1]['price'] = res[-1]['price'] - (
                        tax['amount'] * tax['base_sign'] or 0.0)
                assoc_tax = {
                    'type': 'tax',
                    'name': tax['name'],
                    'price_unit': tax['price_unit'],
                    'quantity': 1,
                    'price': tax['amount'] * tax['base_sign'] or 0.0,
                    'account_id': tax['account_collected_id']
                    or mres['account_id'],
                    'tax_code_id': tax['tax_code_id'],
                    'tax_amount': tax['amount'] * tax['base_sign'],
                }
                res.append(assoc_tax)
        return res

    def move_line_get_item(self, cr, uid, line, context=None):
        company = line.expense_id.company_id
        property_obj = self.pool.get('ir.property')
        if line.product_id:
            acc = line.product_id.property_account_expense
            if not acc:
                acc = line.product_id.categ_id.property_account_expense_categ
            if not acc:
                raise osv.except_osv(
                    _('Error!'),
                    _('No purchase account found for the product %s (or for his category), please configure one.'
                      ) % (line.product_id.name))
        else:
            acc = property_obj.get(cr,
                                   uid,
                                   'property_account_expense_categ',
                                   'product.category',
                                   context={'force_company': company.id})
            if not acc:
                raise osv.except_osv(
                    _('Error!'),
                    _('Please configure Default Expense account for Product purchase: `property_account_expense_categ`.'
                      ))
        return {
            'type': 'src',
            'name': line.name.split('\n')[0][:64],
            'price_unit': line.unit_amount,
            'quantity': line.unit_quantity,
            'price': line.total_amount,
            'account_id': acc.id,
            'product_id': line.product_id.id,
            'uos_id': line.uom_id.id,
            'account_analytic_id': line.analytic_account.id,
        }

    def action_view_receipt(self, cr, uid, ids, context=None):
        '''
        This function returns an action that display existing account.move of given expense ids.
        '''
        assert len(
            ids
        ) == 1, 'This option should only be used for a single id at a time'
        expense = self.browse(cr, uid, ids[0], context=context)
        assert expense.account_move_id
        try:
            dummy, view_id = self.pool.get(
                'ir.model.data').get_object_reference(cr, uid, 'account',
                                                      'view_move_form')
        except ValueError, e:
            view_id = False
        result = {
            'name': _('Expense Account Move'),
            'view_type': 'form',
            'view_mode': 'form',
            'view_id': view_id,
            'res_model': 'account.move',
            'type': 'ir.actions.act_window',
            'nodestroy': True,
            'target': 'current',
            'res_id': expense.account_move_id.id,
        }
        return result
Example #5
0
 # 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'),
Example #6
0
class external_referential(Model):
    _inherit = "external.referential"

    def external_connection(self, cr, uid, id, debug=False, logger=False, context=None):
        if isinstance(id, list):
            id=id[0]
        referential = self.browse(cr, uid, id, context=context)
        try:
            return FileConnection(referential.type_id.code, referential.location, referential.apiusername,\
                            referential.apipass, port=referential. port, allow_dir_creation = True, \
                            home_folder=referential.home_folder)
        except Exception, e:
            raise except_osv(_("Base File Protocole Connection Error"),
                             _("Could not connect to server\nCheck url & user & password.\n %s") % e)

    _columns={
        'port': fields.integer('Port'),
        'home_folder': fields.char('Home Folder', size=64),
    }

    def _prepare_external_referential_fieldnames(self, cr, uid, context=None):
        res = super(external_referential, self)._prepare_external_referential_fieldnames(cr, uid, context=context)
        res.append('protocole')
        return res

    def _prepare_external_referential_vals(self, cr, uid, referential, context=None):
        res = super(external_referential, self)._prepare_external_referential_vals(cr, uid, referential, context=context)
        res['protocole'] = referential.protocole
        return res

class wsafip_connection(osv.osv):
    def _get_state(self, cr, uid, ids, fields_name, arg, context=None):
        r = {}
        for auth in self.browse(cr, uid, ids):
            if False in (auth.uniqueid, auth.generationtime, auth.expirationtime, auth.token, auth.sign):
                _logger.info("Disconnected from AFIP.")
                r[auth.id]='disconnected'
            elif not dateparse(auth.generationtime) - timedelta(0,5) < datetime.now() :
                _logger.warning("clockshifted. Server: %s, Now: %s" %
                            (str(dateparse(auth.generationtime)), str(datetime.now())))
                _logger.warning("clockshifted. Please syncronize your host to a NTP server.")
                r[auth.id]='clockshifted'
            elif datetime.now() < dateparse(auth.expirationtime):
                r[auth.id]='connected'
            else:
                _logger.info("Invalid Connection from AFIP.")
                r[auth.id]='invalid'
            # 'Invalid Partner' si el cuit del partner no es el mismo al de la clave publica/privada.
        return r

    _name = "wsafip.connection"
    _columns = {
        'name': fields.char('Name', size=64),
        'partner_id': fields.many2one('res.partner', 'Partner'),
        'server_id': fields.many2one('wsafip.server', 'Service Server'),
        'logging_id': fields.many2one('wsafip.server', 'Authorization Server'),
        'certificate': fields.many2one('crypto.certificate', 'Certificate Signer'),
        'uniqueid': fields.integer('Unique ID', readonly=True),
        'token': fields.text('Token', readonly=True),
        'sign': fields.text('Sign', readonly=True),
        'generationtime': fields.datetime('Generation Time', readonly=True),
        'expirationtime': fields.datetime('Expiration Time', readonly=True),
        'state': fields.function(_get_state,
                                 method=True,
                                 string='Status',
                                 type='selection',
                                 selection=[
                                         ('clockshifted', 'Clock shifted'),
                                         ('connected', 'Connected'),
                                         ('disconnected', 'Disconnected'),
                                         ('invalid', 'Invalid'),
                                 ],
                                 readonly=True),
        'batch_sequence_id': fields.many2one('ir.sequence', 'Batch Sequence', readonly=False),
    }

    _defaults = {
        'partner_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, [uid], c)[0].company_id.partner_id.id,
    }

    def login(self, cr, uid, ids, context=None):

        state = self._get_state(cr, uid, ids, None, None)

        for ws in self.browse(cr, uid, [ _id for _id, _stat in state.items()
                                        if _stat not in [ 'connected', 'clockshifted' ]]):
            aaclient = Client(ws.logging_id.url+'?WSDL')

            uniqueid=randint(_intmin, _intmax)
            generationtime=(datetime.now(tzlocal()) - timedelta(0,60)).isoformat(),
            expirationtime=(datetime.now(tzlocal()) + timedelta(0,60)).isoformat(),
            msg = _login_message.format(
                uniqueid=uniqueid-_intmin,
                generationtime=(datetime.now(tzlocal()) - timedelta(0,60)).isoformat(),
                expirationtime=(datetime.now(tzlocal()) + timedelta(0,60)).isoformat(),
                service=ws.server_id.code
            )
            msg = ws.certificate.smime(msg)[ws.certificate.id]
            head, body, end = msg.split('\n\n')

            response = aaclient.service.loginCms(in0=body)

            T = ET.fromstring(response)

            auth_data = {
            'source' : T.find('header/source').text,
            'destination' : T.find('header/destination').text,
            'uniqueid' : int(T.find('header/uniqueId').text)+_intmin,
            'generationtime' : dateparse(T.find('header/generationTime').text),
            'expirationtime' : dateparse(T.find('header/expirationTime').text),
            'token' : T.find('credentials/token').text,
            'sign' : T.find('credentials/sign').text,
            }

            del auth_data['source']
            del auth_data['destination']

            _logger.info("Successful Connection to AFIP.")
            self.write(cr, uid, ws.id, auth_data)

    def get_auth(self, cr, uid, ids, context=None):
        r = {}
        for auth in self.browse(cr, uid, ids):
            if auth.partner_id.document_type_id.name != 'CUIT':
                raise osv.except_osv(_('Error'), _('Selected partner has not CUIT. Need one to connect.'))
            r[auth.id] = {
                'Token': auth.token.encode('ascii'),
                'Sign': auth.sign.encode('ascii'),
                'Cuit': auth.partner_id.document_number,
            }
        if len(ids) == 1:
            return r[ids[0]]
        else:
            return r

    def do_login(self, cr, uid, ids, context=None):
        try:
            self.login(cr, uid, ids)
        except X509Error, m:
            raise osv.except_osv(_('Certificate Error'), _(m))
        except Exception, e:
            raise osv.except_osv(_('Unknown Error'), _("%s" % e))
Example #8
0
# -*- 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, '')])),
    ('selection.function', fields.selection(selection_fn)),
    # just relate to an integer
    ('many2one', fields.many2one('export.integer')),
    ('one2many', fields.one2many('export.one2many.child', 'parent_id')),
    ('many2many', fields.many2many('export.many2many.other')),
    ('function', fields.function(function_fn, fnct_inv=function_fn_write, type="integer")),
Example #9
0
        ],'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'),
    }

    def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
        res = super(report_xml, self).fields_view_get(cr, user, view_id, view_type, context, toolbar, submenu)
Example #10
0
def parse(self, cr, uid, context):
    """ Transform the leaves of the expression

        The principle is to pop elements from a leaf stack one at a time.
        Each leaf is processed. The processing is a if/elif list of various
        cases that appear in the leafs (many2one, function fields, ...).
        Two things can happen as a processing result:
        - the leaf has been modified and/or new leafs have to be introduced
          in the expression; they are pushed into the leaf stack, to be
          processed right after
        - the leaf is added to the result

        Some internal var explanation:
            :var obj working_model: model object, model containing the field
                (the name provided in the left operand)
            :var list field_path: left operand seen as a path
                 (foo.bar -> [foo, bar])
            :var obj relational_model: relational model of a field (field._obj)
                ex: res_partner.bank_ids -> res.partner.bank
    """

    def to_ids(value, relational_model, context=None, limit=None):
        """
        Normalize a single id or name, or a list of those,
        into a list of ids
        :param {int,long,basestring,list,tuple} value:
            if int, long -> return [value]
            if basestring, convert it into a list of basestrings, then
            if list of basestring ->
                perform a name_search on relational_model for each name
                return the list of related ids
        """
        names = []
        if isinstance(value, basestring):
            names = [value]
        elif value and isinstance(value, (tuple, list)) and \
                all(isinstance(item, basestring) for item in value):
            names = value
        elif isinstance(value, (int, long)):
            return [value]
        if names:
            name_get_list = [name_get[0] for name in names for name_get in
                             relational_model.name_search(
                                 cr, uid, name, [], 'ilike', context=context,
                                 limit=limit)]
            return list(set(name_get_list))
        return list(value)

    def child_of_domain(left, ids, left_model, parent=None, prefix='',
                        context=None):
        """
        Return a domain implementing the child_of operator for
        [(left,child_of,ids)], either as a range using the parent_left/right
        tree lookup fields (when available),
        or as an expanded [(left,in,child_ids)]
        """
        if left_model._parent_store and (not left_model.pool._init):
            # TODO: Improve joins implemented for many with '.', replace by:
            # doms += ['&',(prefix+'.parent_left','<',o.parent_right),
            # (prefix+'.parent_left','>=',o.parent_left)]
            doms = []
            for o in left_model.browse(cr, uid, ids, context=context):
                if doms:
                    doms.insert(0, OR_OPERATOR)
                doms += [AND_OPERATOR, ('parent_left', '<', o.parent_right),
                         ('parent_left', '>=', o.parent_left)]
            if prefix:
                return [(left, 'in', left_model.search(
                    cr, uid, doms, context=context))]
            return doms
        else:
            def recursive_children(ids, model, parent_field):
                if not ids:
                    return []
                ids2 = model.search(cr, uid, [(parent_field, 'in', ids)],
                                    context=context)
                return ids + recursive_children(ids2, model, parent_field)
            return [(left, 'in', recursive_children(
                ids, left_model, parent or left_model._parent_name))]

    def pop():
        """ Pop a leaf to process. """
        return self.stack.pop()

    def push(leaf):
        """ Push a leaf to be processed right after. """
        self.stack.append(leaf)

    def push_result(leaf):
        """ Push a leaf to the results. This leaf has been fully processed
            and validated. """
        self.result.append(leaf)

    self.result = []
    self.stack = [ExtendedLeaf(leaf, self.root_model)
                  for leaf in self.expression]
    # process from right to left; expression is from left to right
    self.stack.reverse()

    while self.stack:
        # Get the next leaf to process
        leaf = pop()

        # Get working variables
        working_model = leaf.model
        if leaf.is_operator():
            left, operator, right = leaf.leaf, None, None
        elif leaf.is_true_leaf() or leaf.is_false_leaf():
            # because we consider left as a string
            left, operator, right = ('%s' % leaf.leaf[0], leaf.leaf[1],
                                     leaf.leaf[2])
        else:
            left, operator, right = leaf.leaf
        field_path = left.split('.', 1)
        field = working_model._columns.get(field_path[0])
        # Neova Health BEGIN
        if not working_model._columns.get(field_path[0]) and \
                field_path[0] == 'id':
            # field 'id' normally is not in the _columns
            # the problem appeared with call
            # search('t4clinical.task.base', [
            # ('responsible_user_ids','in',uid)])
            # -- returned [], due to this issue was looking for
            # t4clinical.task.base ids in project.task ids
            field = fields.integer('fake id field. quick fix')
            field._obj = working_model._name
        # Neova Health END
        if field and field._obj:
            relational_model = working_model.pool.get(field._obj)
        else:
            relational_model = None

        # ----------------------------------------
        # SIMPLE CASE
        # 1. leaf is an operator
        # 2. leaf is a true/false leaf
        # -> add directly to result
        # ----------------------------------------

        if leaf.is_operator() or leaf.is_true_leaf() or leaf.is_false_leaf():
            push_result(leaf)

        # ----------------------------------------
        # FIELD NOT FOUND
        # -> from inherits'd fields -> work on the related model, and add
        #    a join condition
        # -> ('id', 'child_of', '..') -> use a 'to_ids'
        # -> but is one on the _log_access special fields, add directly to
        #    result
        # TODO: make these fields explicitly available in self.columns instead!
        # -> else: crash
        # ----------------------------------------

        elif not field and field_path[0] in working_model._inherit_fields:
            # comments about inherits'd fields
            #  { 'field_name': ('parent_model', 'm2o_field_to_reach_parent',
            #                    field_column_obj, origina_parent_model), ... }
            next_model = working_model.pool.get(
                working_model._inherit_fields[field_path[0]][0])
            leaf.add_join_context(
                next_model, working_model._inherits[next_model._name], 'id',
                working_model._inherits[next_model._name])
            push(leaf)

        elif left == 'id' and operator == 'child_of':
            ids2 = to_ids(right, working_model, context)
            dom = child_of_domain(left, ids2, working_model)
            for dom_leaf in reversed(dom):
                new_leaf = create_substitution_leaf(leaf, dom_leaf,
                                                    working_model)
                push(new_leaf)

        elif not field and field_path[0] in MAGIC_COLUMNS:
            push_result(leaf)

        elif not field:
            raise ValueError("Invalid field %r in leaf %r" % (left, str(leaf)))

        # ----------------------------------------
        # PATH SPOTTED
        # -> many2one or one2many with _auto_join:
        #    - add a join, then jump into linked field: field.remaining on
        #      src_table is replaced by remaining on dst_table,
        #      and set for re-evaluation
        #    - if a domain is defined on the field, add it into evaluation
        #      on the relational table
        # -> many2one, many2many, one2many: replace by an equivalent computed
        #    domain, given by recursively searching on the path's remaining
        # -> note: hack about fields.property should not be necessary anymore
        #    as after transforming the field, it will go through this loop
        #    once again
        # ----------------------------------------

        elif len(field_path) > 1 and field._type == 'many2one' and \
                field._auto_join:
            # res_partner.state_id = res_partner__state_id.id
            leaf.add_join_context(relational_model, field_path[0], 'id',
                                  field_path[0])
            push(create_substitution_leaf(
                leaf, (field_path[1], operator, right), relational_model))

        elif len(field_path) > 1 and field._type == 'one2many' and \
                field._auto_join:
            # res_partner.id = res_partner__bank_ids.partner_id
            leaf.add_join_context(relational_model, 'id', field._fields_id,
                                  field_path[0])
            domain = field._domain(working_model) if callable(field._domain) \
                else field._domain
            push(create_substitution_leaf(
                leaf, (field_path[1], operator, right), relational_model))
            if domain:
                domain = normalize_domain(domain)
                for elem in reversed(domain):
                    push(create_substitution_leaf(leaf, elem,
                                                  relational_model))
                push(create_substitution_leaf(leaf, AND_OPERATOR,
                                              relational_model))

        elif len(field_path) > 1 and field._auto_join:
            raise NotImplementedError('_auto_join attribute not supported on '
                                      'many2many field %s' % left)

        elif len(field_path) > 1 and field._type == 'many2one':
            right_ids = relational_model.search(
                cr, uid, [(field_path[1], operator, right)], context=context)
            leaf.leaf = (field_path[0], 'in', right_ids)
            push(leaf)

        # Making search easier when there is a left operand
        # as field.o2m or field.m2m
        elif len(field_path) > 1 and field._type in ['many2many', 'one2many']:
            right_ids = relational_model.search(
                cr, uid, [(field_path[1], operator, right)], context=context)
            table_ids = working_model.search(
                cr, uid, [(field_path[0], 'in', right_ids)],
                context=dict(context, active_test=False))
            leaf.leaf = ('id', 'in', table_ids)
            push(leaf)

        # -------------------------------------------------
        # FUNCTION FIELD
        # -> not stored: error if no _fnct_search,
        #    otherwise handle the result domain
        # -> stored: management done in the remaining of parsing
        # -------------------------------------------------

        elif isinstance(field, fields.function) and not field.store \
                and not field._fnct_search:
            # this is a function field that is not stored
            # the function field doesn't provide a search function and
            # doesn't store values in the database, so we must ignore it:
            # we generate a dummy leaf.
            leaf.leaf = TRUE_LEAF
            _logger.error(
                "The field '%s' (%s) can not be searched: non-stored "
                "function field without fnct_search",
                field.string, left)
            # avoid compiling stack trace if not needed
            if _logger.isEnabledFor(logging.DEBUG):
                _logger.debug(''.join(traceback.format_stack()))
            push(leaf)

        elif isinstance(field, fields.function) and not field.store:
            # this is a function field that is not stored
            fct_domain = field.search(cr, uid, working_model, left,
                                      [leaf.leaf], context=context)
            if not fct_domain:
                leaf.leaf = TRUE_LEAF
                push(leaf)
            else:
                # we assume that the expression is valid
                # we create a dummy leaf for forcing the parsing of the
                # resulting expression
                for domain_element in reversed(fct_domain):
                    push(create_substitution_leaf(leaf, domain_element,
                                                  working_model))
                # self.push(
                # create_substitution_leaf(leaf, TRUE_LEAF, working_model))
                # self.push(
                # create_substitution_leaf(leaf, AND_OPERATOR, working_model))

        # -------------------------------------------------
        # RELATIONAL FIELDS
        # -------------------------------------------------

        # Applying recursivity on field(one2many)
        elif field._type == 'one2many' and operator == 'child_of':
            ids2 = to_ids(right, relational_model, context)
            if field._obj != working_model._name:
                dom = child_of_domain(left, ids2, relational_model,
                                      prefix=field._obj)
            else:
                dom = child_of_domain('id', ids2, working_model, parent=left)
            for dom_leaf in reversed(dom):
                push(create_substitution_leaf(leaf, dom_leaf, working_model))

        elif field._type == 'one2many':
            call_null = True

            if right is not False:
                if isinstance(right, basestring):
                    ids2 = [x[0] for x in relational_model.name_search(
                        cr, uid, right, [], operator, context=context,
                        limit=None)
                    ]
                    if ids2:
                        operator = 'in'
                else:
                    if not isinstance(right, list):
                        ids2 = [right]
                    else:
                        ids2 = right
                if not ids2:
                    if operator in ['like', 'ilike', 'in', '=']:
                        # no result found with given search criteria
                        call_null = False
                        push(create_substitution_leaf(leaf, FALSE_LEAF,
                                                      working_model))
                else:
                    ids2 = select_from_where(cr, field._fields_id,
                                             relational_model._table, 'id',
                                             ids2, operator)
                    if ids2:
                        call_null = False
                        o2m_op = 'not in' if operator in \
                            NEGATIVE_TERM_OPERATORS else 'in'
                        push(create_substitution_leaf(
                            leaf, ('id', o2m_op, ids2), working_model))

            if call_null:
                o2m_op = 'in' if operator in \
                    NEGATIVE_TERM_OPERATORS else 'not in'
                push(create_substitution_leaf(
                    leaf, ('id', o2m_op,
                           select_distinct_from_where_not_null(
                               cr, field._fields_id, relational_model._table)),
                    working_model))

        elif field._type == 'many2many':
            rel_table, rel_id1, rel_id2 = field._sql_names(working_model)
            # FIXME
            if operator == 'child_of':
                def _rec_convert(ids):
                    if relational_model == working_model:
                        return ids
                    return select_from_where(cr, rel_id1, rel_table, rel_id2,
                                             ids, operator)

                ids2 = to_ids(right, relational_model, context)
                dom = child_of_domain('id', ids2, relational_model)
                ids2 = relational_model.search(cr, uid, dom, context=context)
                push(create_substitution_leaf(leaf,
                                              ('id', 'in', _rec_convert(ids2)),
                                              working_model))
            else:
                call_null_m2m = True
                if right is not False:
                    if isinstance(right, basestring):
                        res_ids = [x[0] for x in relational_model.name_search(
                            cr, uid, right, [], operator, context=context)]
                        if res_ids:
                            operator = 'in'
                    else:
                        if not isinstance(right, list):
                            res_ids = [right]
                        else:
                            res_ids = right
                    if not res_ids:
                        if operator in ['like', 'ilike', 'in', '=']:
                            # no result found with given search criteria
                            call_null_m2m = False
                            push(create_substitution_leaf(leaf, FALSE_LEAF,
                                                          working_model))
                        else:
                            # operator changed because ids are directly related
                            # to main object
                            operator = 'in'
                    else:
                        call_null_m2m = False
                        m2m_op = 'not in' if operator in \
                            NEGATIVE_TERM_OPERATORS else 'in'
                        push(create_substitution_leaf(
                            leaf, ('id', m2m_op,
                                   select_from_where(
                                       cr, rel_id1, rel_table, rel_id2,
                                       res_ids, operator) or [0]),
                            working_model))

                if call_null_m2m:
                    m2m_op = 'in' if operator in \
                        NEGATIVE_TERM_OPERATORS else 'not in'
                    push(create_substitution_leaf(
                        leaf,
                        ('id', m2m_op, select_distinct_from_where_not_null(
                            cr, rel_id1, rel_table)), working_model))

        elif field._type == 'many2one':
            if operator == 'child_of':
                ids2 = to_ids(right, relational_model, context)
                if field._obj != working_model._name:
                    dom = child_of_domain(left, ids2, relational_model,
                                          prefix=field._obj)
                else:
                    dom = child_of_domain('id', ids2, working_model,
                                          parent=left)
                for dom_leaf in reversed(dom):
                    push(create_substitution_leaf(leaf, dom_leaf,
                                                  working_model))
            else:
                def _get_expression(relational_model, cr, uid, left, right,
                                    operator, context=None):
                    if context is None:
                        context = {}
                    c = context.copy()
                    c['active_test'] = False
                    # Special treatment to ill-formed domains
                    operator = (operator in ['<', '>', '<=', '>=']) and 'in' \
                        or operator

                    dict_op = {'not in': '!=', 'in': '=',
                               '=': 'in', '!=': 'not in'}
                    if isinstance(right, tuple):
                        right = list(right)
                    if (not isinstance(right, list)) and \
                            operator in ['not in', 'in']:
                        operator = dict_op[operator]
                    elif isinstance(right, list) and operator in ['!=', '=']:
                        # for domain (FIELD,'=',['value1','value2'])
                        operator = dict_op[operator]
                    res_ids = [x[0] for x in relational_model.name_search(
                        cr, uid, right, [], operator, limit=None, context=c)]
                    if operator in NEGATIVE_TERM_OPERATORS:
                        # TODO this should not be appended if False in 'right'
                        res_ids.append(False)
                    return left, 'in', res_ids
                # resolve string-based m2o criterion into IDs
                if isinstance(right, basestring) or right and \
                        isinstance(right, (tuple, list)) and \
                        all(isinstance(item, basestring) for item in right):
                    push(create_substitution_leaf(
                        leaf,
                        _get_expression(relational_model, cr, uid, left, right,
                                        operator, context=context),
                        working_model))
                else:
                    # right == [] or right == False and all other cases
                    # are handled by __leaf_to_sql()
                    push_result(leaf)

        # -------------------------------------------------
        # OTHER FIELDS
        # -> datetime fields: manage time part of the datetime
        #    field when it is not there
        # -> manage translatable fields
        # -------------------------------------------------

        else:
            if field._type == 'datetime' and right and len(right) == 10:
                if operator in ('>', '>='):
                    right += ' 00:00:00'
                elif operator in ('<', '<='):
                    right += ' 23:59:59'
                push(create_substitution_leaf(leaf, (left, operator, right),
                                              working_model))

            elif field.translate and right:
                need_wildcard = operator in ('like', 'ilike', 'not like',
                                             'not ilike')
                sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(
                    operator, operator)
                if need_wildcard:
                    right = '%%%s%%' % right

                inselect_operator = 'inselect'
                if sql_operator in NEGATIVE_TERM_OPERATORS:
                    # negate operator (fix lp:1071710)
                    if sql_operator[:3] == 'not':
                        sql_operator = sql_operator[4:]
                    else:
                        sql_operator = '='
                    inselect_operator = 'not inselect'

                unaccent = self._unaccent if sql_operator.endswith('like') \
                    else lambda x: x

                trans_left = unaccent('value')
                quote_left = unaccent(_quote(left))
                instr = unaccent('%s')

                if sql_operator == 'in':
                    # params will be flatten by to_sql() =>
                    # expand the placeholders
                    instr = '(%s)' % ', '.join(['%s'] * len(right))

                subselect = """(SELECT res_id
                                  FROM ir_translation
                                 WHERE name = %s
                                   AND lang = %s
                                   AND type = %s
                                   AND {trans_left} {operator} {right}
                               ) UNION (
                                SELECT id
                                  FROM "{table}"
                                 WHERE {left} {operator} {right}
                               )
                            """.format(trans_left=trans_left,
                                       operator=sql_operator, right=instr,
                                       table=working_model._table,
                                       left=quote_left)

                params = (
                    working_model._name + ',' + left,
                    context.get('lang') or 'en_US',
                    'model',
                    right,
                    right,
                )
                push(create_substitution_leaf(leaf, ('id', inselect_operator,
                                                     (subselect, params)
                                                     ), working_model))

            else:
                push_result(leaf)

    # ----------------------------------------
    # END OF PARSING FULL DOMAIN
    # -> generate joins
    # ----------------------------------------

    joins = set()
    for leaf in self.result:
        joins |= set(leaf.get_join_conditions())
    self.joins = list(joins)
Example #11
0
                file_data = image_file.read()
                self._logo_image = base64.encodestring(file_data)
                return self._logo_image
            finally:
                image_file.close()
        else:
            self._logo_image = base64.encodestring(im.read())
            return self._logo_image

    def _get_image_fn(self, cr, uid, ids, name, args, context=None):
        image = self._get_image(cr, uid, context)
        return dict.fromkeys(ids, image) # ok to use .fromkeys() as the image is same for all 

    _columns = {
        'host':fields.char('Host', size=64, required=True),
        'port':fields.integer('Port', required=True),
        'ooo_restart_cmd': fields.char('OOO restart command', size=256, \
            help='Enter the shell command that will be executed to restart the LibreOffice/OpenOffice background process.'+ \
                'The command will be executed as the user of the OpenERP server process,'+ \
                'so you may need to prefix it with sudo and configure your sudoers file to have this command executed without password.'),
        'state':fields.selection([
            ('init','Init'),
            ('error','Error'),
            ('done','Done'),
            
        ],'State', select=True, readonly=True),
        'msg': fields.text('Message', readonly=True),
        'error_details': fields.text('Error Details', readonly=True),
        'link':fields.char('Installation Manual', size=128, help='Installation (Dependencies and Base system setup)', readonly=True),
        'config_logo': fields.function(_get_image_fn, string='Image', type='binary', method=True),
        
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'])
    ]
Example #13
0
class insurance_followup(osv.osv):
    """ insurance_followup """
    _name = "insurance.followup"
    _description = "insurance followup"

    def _due_date(self, cr, uid, ids, field_name, arg, context=None):
        if context is None:
            context = {}
        if not ids:
            ids = self.search(cr, uid, [])
        res = {}.fromkeys(ids, 0.0)
        if not ids:
            return res
        cur_obj = self.browse(cr, uid, ids, context=context)
        iss_date = datetime.now()
        pay_freq = datetime.now()
        for field in cur_obj:
            if field.issue_date and field.payment_frequency:
                iss_date = datetime.strptime(field.issue_date, '%Y-%m-%d')
                pay_freq = field.payment_frequency
                freq = 12 / pay_freq
            cur_date = datetime.today()
            add_month = iss_date
            while add_month <= cur_date:
                if str(add_month).split(' ')[0] == str(cur_date).split(' ')[0]:
                    break
                add_month = add_month + relativedelta(months=freq)
            res.update({field.id: str(add_month)})
        return res

    _columns = {
        'name':
        fields.char(
            'Name',
            size=64,
            required=True,
            states={'paid': [('readonly', True)]},
        ),
        'surname':
        fields.char(
            'Surname',
            size=64,
            states={'paid': [('readonly', True)]},
        ),
        'address_id':
        fields.char(
            'Address',
            states={'paid': [('readonly', True)]},
        ),
        'phone':
        fields.char(
            'Phone',
            size=64,
            states={'paid': [('readonly', True)]},
        ),
        'email':
        fields.char(
            'Email',
            size=240,
            states={'paid': [('readonly', True)]},
        ),
        'issue_date':
        fields.date('Issue Date',
                    readonly=True,
                    states={'draft': [('readonly', False)]},
                    select=True),
        'date_due':
        fields.function(_due_date,
                        type='date',
                        string="Payment date",
                        store=True),
        'expiry_date':
        fields.date(
            'CREDIT CARD EXPIRATION DATE',
            states={'paid': [('readonly', True)]},
        ),
        'payment_frequency':
        fields.integer('FREQUENCY OF PAYMENT',
                       states={'paid': [('readonly', True)]}),
        'sum_insured':
        fields.integer(
            'SUM INSURED',
            states={'paid': [('readonly', True)]},
        ),
        'no_of_policy':
        fields.integer(
            'Number of Policy',
            states={'paid': [('readonly', True)]},
        ),
        'insurance_issuer_id':
        fields.many2one(
            'insurance.issuer',
            'Insurance Issuer',
            states={'paid': [('readonly', True)]},
        ),
        'prime':
        fields.float(
            'Prime',
            size=64,
            states={'paid': [('readonly', True)]},
        ),
        'form':
        fields.char(
            'Form',
            size=64,
            states={'paid': [('readonly', True)]},
        ),
        'plan':
        fields.char(
            'Plan',
            size=64,
            states={'paid': [('readonly', True)]},
        ),
        'user_id':
        fields.many2one(
            'res.users',
            'Agent',
            states={'paid': [('readonly', True)]},
        ),
        'partner_id':
        fields.many2one(
            'res.partner',
            'Customer',
            states={'paid': [('readonly', True)]},
            required=True,
        ),
        'department_id':
        fields.many2one(
            'insurance.department',
            'Insurance Department',
            states={'paid': [('readonly', True)]},
        ),
        'amount':
        fields.float(
            'Amount',
            required=True,
        ),
        'state':
        fields.selection(
            [
                ('draft', 'Draft'),
                ('open', 'Open'),
                ('paid', 'Paid'),
                ('cancel', 'Cancelled'),
            ],
            'Status',
            select=True,
        ),
        'notes':
        fields.text('Notes'),
        'policy_status':
        fields.boolean('Policy Status'),
        'divisiones':
        fields.char('DIVISIONES'),
        'payment_ids':
        fields.one2many('insurance.payment', 'payment_id',
                        'Insurance Payment'),
        'off_payment':
        fields.boolean('Off Payment?')
    }
    _defaults = {'state': 'draft'}

    def check_date(self, cr, uid, ins_ids=None, context=None):
        ins_ids = self.search(cr,
                              uid,
                              [('state', 'in', ['draft', 'open', 'paid'])],
                              context=context)

        for ins_obj in self.browse(cr, uid, ins_ids, context=context):
            if ins_obj.issue_date and ins_obj.payment_frequency:
                iss_date = datetime.strptime(ins_obj.issue_date, '%Y-%m-%d')
                pay_freq = ins_obj.payment_frequency
                freq = 12 / pay_freq
                cur_date = datetime.today()
                add_month = iss_date
                while add_month <= cur_date:
                    if str(add_month).split(' ')[0] == str(cur_date).split(
                            ' ')[0]:
                        break
                    add_month = add_month + relativedelta(months=freq)
                    if add_month < cur_date:
                        add_month = add_month + relativedelta(months=freq)
                        self.write(cr, uid, ins_ids, {'date_due': add_month},
                                   context)
                vals = {}
                vals = {
                    'name': ins_obj.name,
                    'partner_phone': ins_obj.phone,
                    'date': ins_obj.issue_date,
                    'partner_id': ins_obj.partner_id.id or False,
                    'user_id': ins_obj.user_id.id or False,
                }
                for45 = cur_date + relativedelta(days=45)
                for15 = cur_date + relativedelta(days=15)
                for15 = str(for15).split(' ')[0]
                for45 = str(for45).split(' ')[0]
                if (for45 == ins_obj.date_due) or (for15 == ins_obj.date_due):
                    res = self.pool.get('crm.phonecall').create(
                        cr, uid, vals, context=context)
        return True
Example #14
0
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
Example #15
0
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)
Example #16
0
class ir_translation(osv.osv):
    _name = "ir.translation"
    _log_access = False

    def _get_language(self, cr, uid, context):
        lang_model = self.pool.get('res.lang')
        lang_ids = lang_model.search(cr,
                                     uid, [('translatable', '=', True)],
                                     context=context)
        lang_data = lang_model.read(cr,
                                    uid,
                                    lang_ids, ['code', 'name'],
                                    context=context)
        return [(d['code'], d['name']) for d in lang_data]

    def _get_src(self, cr, uid, ids, name, arg, context=None):
        ''' Get source name for the translation. If object type is model then
        return the value store in db. Otherwise return value store in src field
        '''
        if context is None:
            context = {}
        res = dict.fromkeys(ids, False)
        for record in self.browse(cr, uid, ids, context=context):
            res[record.id] = record.src
            if record.type == 'model':
                model_name, field_name = record.name.split(',')
                model = self.pool.get(model_name)
                if model is None:
                    continue
                field = model._fields.get(field_name)
                if field is None:
                    continue
                if not callable(field.translate):
                    # Pass context without lang, need to read real stored field, not translation
                    context_no_lang = dict(context, lang=None)
                    result = model.read(cr,
                                        uid, [record.res_id], [field_name],
                                        context=context_no_lang)
                    res[record.id] = result[0][field_name] if result else False
        return res

    def _set_src(self, cr, uid, id, name, value, args, context=None):
        ''' When changing source term of a translation, change its value in db for
        the associated object, and the src field
        '''
        if context is None:
            context = {}
        record = self.browse(cr, uid, id, context=context)
        if record.type == 'model':
            model_name, field_name = record.name.split(',')
            model = self.pool.get(model_name)
            field = model._fields[field_name]
            if not callable(field.translate):
                # Make a context without language information, because we want
                # to write on the value stored in db and not on the one
                # associated with the current language. Also not removing lang
                # from context trigger an error when lang is different.
                context_wo_lang = context.copy()
                context_wo_lang.pop('lang', None)
                model.write(cr,
                            uid, [record.res_id], {field_name: value},
                            context=context_wo_lang)
        return self.write(cr, uid, id, {'src': value}, context=context)

    def _search_src(self, cr, uid, obj, name, args, context):
        ''' the source term is stored on 'src' field '''
        res = []
        for field, operator, value in args:
            res.append(('src', operator, value))
        return res

    _columns = {
        'name':
        fields.char('Translated field', required=True),
        'res_id':
        fields.integer('Record ID', select=True),
        'lang':
        fields.selection(_get_language, string='Language'),
        'type':
        fields.selection(TRANSLATION_TYPE, string='Type', select=True),
        'src':
        fields.text('Internal Source'
                    ),  # stored in database, kept for backward compatibility
        'source':
        fields.function(_get_src,
                        fnct_inv=_set_src,
                        fnct_search=_search_src,
                        type='text',
                        string='Source term'),
        'value':
        fields.text('Translation Value'),
        'module':
        fields.char('Module', help="Module this term belongs to", select=True),
        'state':
        fields.selection(
            [('to_translate', 'To Translate'),
             ('inprogress', 'Translation in Progress'),
             ('translated', 'Translated')],
            string="Status",
            help=
            "Automatically set to let administators find new terms that might need to be translated"
        ),

        # aka gettext extracted-comments - we use them to flag openerp-web translation
        # cfr: http://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/PO-Files.html
        'comments':
        fields.text('Translation comments', select=True),
    }

    _defaults = {
        'state': 'to_translate',
    }

    _sql_constraints = [
        ('lang_fkey_res_lang', 'FOREIGN KEY(lang) REFERENCES res_lang(code)',
         'Language code of translation item must be among known languages'),
    ]

    def _auto_init(self, cr, context=None):
        super(ir_translation, self)._auto_init(cr, context)

        cr.execute(
            "SELECT indexname FROM pg_indexes WHERE indexname LIKE 'ir_translation_%'"
        )
        indexes = [row[0] for row in cr.fetchall()]

        # Removed because there is a size limit on btree indexed values (problem with column src):
        # cr.execute('CREATE INDEX ir_translation_ltns ON ir_translation (name, lang, type, src)')
        # cr.execute('CREATE INDEX ir_translation_lts ON ir_translation (lang, type, src)')
        #
        # Removed because hash indexes are not compatible with postgres streaming replication:
        # cr.execute('CREATE INDEX ir_translation_src_hash_idx ON ir_translation USING hash (src)')
        if set(indexes) & set([
                'ir_translation_ltns', 'ir_translation_lts',
                'ir_translation_src_hash_idx'
        ]):
            cr.execute(
                'DROP INDEX IF EXISTS ir_translation_ltns, ir_translation_lts, ir_translation_src_hash_idx'
            )
            cr.commit()

        # Add separate md5 index on src (no size limit on values, and good performance).
        if 'ir_translation_src_md5' not in indexes:
            cr.execute(
                'CREATE INDEX ir_translation_src_md5 ON ir_translation (md5(src))'
            )
            cr.commit()

        if 'ir_translation_ltn' not in indexes:
            cr.execute(
                'CREATE INDEX ir_translation_ltn ON ir_translation (name, lang, type)'
            )
            cr.commit()

    def _check_selection_field_value(self,
                                     cr,
                                     uid,
                                     field,
                                     value,
                                     context=None):
        if field == 'lang':
            return
        return super(ir_translation,
                     self)._check_selection_field_value(cr,
                                                        uid,
                                                        field,
                                                        value,
                                                        context=context)

    def _get_ids(self, cr, uid, name, tt, lang, ids):
        translations = dict.fromkeys(ids, False)
        if ids:
            cr.execute(
                'select res_id,value '
                'from ir_translation '
                'where lang=%s '
                'and type=%s '
                'and name=%s '
                'and res_id IN %s', (lang, tt, name, tuple(ids)))
            for res_id, value in cr.fetchall():
                translations[res_id] = value
        return translations

    def _set_ids(self, cr, uid, name, tt, lang, ids, value, src=None):
        self.clear_caches()
        cr.execute(
            'update ir_translation '
            'set value=%s '
            '  , src=%s '
            '  , state=%s '
            'where lang=%s '
            'and type=%s '
            'and name=%s '
            'and res_id IN %s '
            'returning res_id', (
                value,
                src,
                'translated',
                lang,
                tt,
                name,
                tuple(ids),
            ))

        existing_ids = [x[0] for x in cr.fetchall()]

        for id in list(set(ids) - set(existing_ids)):
            self.create(
                cr, uid, {
                    'lang': lang,
                    'type': tt,
                    'name': name,
                    'res_id': id,
                    'value': value,
                    'src': src,
                    'state': 'translated'
                })
        return len(ids)

    def _get_source_query(self, cr, uid, name, types, lang, source, res_id):
        if source:
            # Note: the extra test on md5(src) is a hint for postgres to use the
            # index ir_translation_src_md5
            query = """SELECT value
                       FROM ir_translation
                       WHERE lang=%s
                        AND type in %s
                        AND src=%s AND md5(src)=md5(%s)"""
            source = tools.ustr(source)
            params = (lang or '', types, source, source)
            if res_id:
                query += " AND res_id in %s"
                params += (res_id, )
            if name:
                query += " AND name=%s"
                params += (tools.ustr(name), )
        else:
            query = """SELECT value
                       FROM ir_translation
                       WHERE lang=%s
                        AND type in %s
                        AND name=%s"""

            params = (lang or '', types, tools.ustr(name))

        return (query, params)

    @tools.ormcache('name', 'types', 'lang', 'source', 'res_id')
    def __get_source(self, cr, uid, name, types, lang, source, res_id):
        # res_id is a tuple or None, otherwise ormcache cannot cache it!
        query, params = self._get_source_query(cr, uid, name, types, lang,
                                               source, res_id)
        cr.execute(query, params)
        res = cr.fetchone()
        trad = res and res[0] or u''
        if source and not trad:
            return tools.ustr(source)
        return trad

    def _get_source(self,
                    cr,
                    uid,
                    name,
                    types,
                    lang,
                    source=None,
                    res_id=None):
        """
        Returns the translation for the given combination of name, type, language
        and source. All values passed to this method should be unicode (not byte strings),
        especially ``source``.

        :param name: identification of the term to translate, such as field name (optional if source is passed)
        :param types: single string defining type of term to translate (see ``type`` field on ir.translation), or sequence of allowed types (strings)
        :param lang: language code of the desired translation
        :param source: optional source term to translate (should be unicode)
        :param res_id: optional resource id or a list of ids to translate (if used, ``source`` should be set)
        :rtype: unicode
        :return: the request translation, or an empty unicode string if no translation was
                 found and `source` was not passed
        """
        # FIXME: should assert that `source` is unicode and fix all callers to always pass unicode
        # so we can remove the string encoding/decoding.
        if not lang:
            return tools.ustr(source or '')
        if isinstance(types, basestring):
            types = (types, )
        if res_id:
            if isinstance(res_id, (int, long)):
                res_id = (res_id, )
            else:
                res_id = tuple(res_id)
        return self.__get_source(cr, uid, name, types, lang, source, res_id)

    @api.model
    def _get_terms_query(self, field, records):
        """ Utility function that makes the query for field terms. """
        query = """ SELECT * FROM ir_translation
                    WHERE lang=%s AND type=%s AND name=%s AND res_id IN %s """
        name = "%s,%s" % (field.model_name, field.name)
        params = (records.env.lang, 'model', name, tuple(records.ids))
        return query, params

    @api.model
    def _get_terms_mapping(self, field, records):
        """ Return a function mapping a ir_translation row (dict) to a value.
        This method is called before querying the database for translations.
        """
        return lambda data: data['value']

    @api.model
    def _get_terms_translations(self, field, records):
        """ Return the terms and translations of a given `field` on `records`.

        :return: {record_id: {source: value}}
        """
        result = {rid: {} for rid in records.ids}
        if records:
            map_trans = self._get_terms_mapping(field, records)
            query, params = self._get_terms_query(field, records)
            self._cr.execute(query, params)
            for data in self._cr.dictfetchall():
                result[data['res_id']][data['src']] = map_trans(data)
        return result

    @api.model
    def _sync_terms_translations(self, field, records):
        """ Synchronize the translations to the terms to translate, after the
        English value of a field is modified. The algorithm tries to match
        existing translations to the terms to translate, provided the distance
        between modified strings is not too large. It allows to not retranslate
        data where a typo has been fixed in the English value.
        """
        if not callable(getattr(field, 'translate', None)):
            return

        trans = self.env['ir.translation']
        outdated = trans
        discarded = trans

        for record in records:
            # get field value and terms to translate
            value = record[field.name]
            terms = set(field.get_trans_terms(value))
            record_trans = trans.search([
                ('type', '=', 'model'),
                ('name', '=', "%s,%s" % (field.model_name, field.name)),
                ('res_id', '=', record.id),
            ])

            if not terms:
                # discard all translations for that field
                discarded += record_trans
                continue

            # remap existing translations on terms when possible
            for trans in record_trans:
                if trans.src == trans.value:
                    discarded += trans
                elif trans.src not in terms:
                    matches = get_close_matches(trans.src, terms, 1, 0.9)
                    if matches:
                        trans.write({'src': matches[0], 'state': trans.state})
                    else:
                        outdated += trans

        # process outdated and discarded translations
        outdated.write({'state': 'to_translate'})
        discarded.unlink()

    @api.model
    @tools.ormcache_context('model_name', keys=('lang', ))
    def get_field_string(self, model_name):
        """ Return the translation of fields strings in the context's language.
        Note that the result contains the available translations only.

        :param model_name: the name of a model
        :return: the model's fields' strings as a dictionary `{field_name: field_string}`
        """
        fields = self.env['ir.model.fields'].search([('model', '=', model_name)
                                                     ])
        return {field.name: field.field_description for field in fields}

    @api.model
    @tools.ormcache_context('model_name', keys=('lang', ))
    def get_field_help(self, model_name):
        """ Return the translation of fields help in the context's language.
        Note that the result contains the available translations only.

        :param model_name: the name of a model
        :return: the model's fields' help as a dictionary `{field_name: field_help}`
        """
        fields = self.env['ir.model.fields'].search([('model', '=', model_name)
                                                     ])
        return {field.name: field.help for field in fields}

    @api.multi
    def check(self, mode):
        """ Check access rights of operation ``mode`` on ``self`` for the
        current user. Raise an AccessError in case conditions are not met.
        """
        if self.env.user._is_admin():
            return

        # collect translated field records (model_ids) and other translations
        trans_ids = []
        model_ids = defaultdict(list)
        model_fields = defaultdict(list)
        for trans in self:
            if trans.type == 'model':
                mname, fname = trans.name.split(',')
                model_ids[mname].append(trans.res_id)
                model_fields[mname].append(fname)
            else:
                trans_ids.append(trans.id)

        # check for regular access rights on other translations
        if trans_ids:
            records = self.browse(trans_ids)
            records.check_access_rights(mode)
            records.check_access_rule(mode)

        # check for read/write access on translated field records
        fmode = 'read' if mode == 'read' else 'write'
        for mname, ids in model_ids.iteritems():
            records = self.env[mname].browse(ids)
            records.check_access_rights(fmode)
            records.check_field_access_rights(fmode, model_fields[mname])
            records.check_access_rule(fmode)

    @api.model
    def create(self, vals):
        if vals.get('type') == 'model' and vals.get('value'):
            # check and sanitize value
            mname, fname = vals['name'].split(',')
            field = self.env[mname]._fields[fname]
            vals['value'] = field.check_trans_value(vals['value'])
        record = super(ir_translation,
                       self.sudo()).create(vals).with_env(self.env)
        record.check('create')
        self.clear_caches()
        return record

    @api.multi
    def write(self, vals):
        if vals.get('value'):
            vals.setdefault('state', 'translated')
            ttype = vals.get('type') or self[:1].type
            if ttype == 'model':
                # check and sanitize value
                name = vals.get('name') or self[:1].name
                mname, fname = name.split(',')
                field = self.env[mname]._fields[fname]
                vals['value'] = field.check_trans_value(vals['value'])
        elif vals.get('src') or not vals.get('value', True):
            vals.setdefault('state', 'to_translate')
        self.check('write')
        result = super(ir_translation, self.sudo()).write(vals)
        self.check('write')
        self.clear_caches()
        return result

    @api.multi
    def unlink(self):
        self.check('unlink')
        self.clear_caches()
        return super(ir_translation, self.sudo()).unlink()

    @api.model
    def insert_missing(self, field, records):
        """ Insert missing translations for `field` on `records`. """
        records = records.with_context(lang=None)
        external_ids = records.get_external_id()  # if no xml_id, empty string
        if callable(field.translate):
            # insert missing translations for each term in src
            query = """ INSERT INTO ir_translation (lang, type, name, res_id, src, value, module)
                        SELECT l.code, 'model', %(name)s, %(res_id)s, %(src)s, %(src)s, %(module)s
                        FROM res_lang l
                        WHERE l.active AND NOT EXISTS (
                            SELECT 1 FROM ir_translation
                            WHERE lang=l.code AND type='model' AND name=%(name)s AND res_id=%(res_id)s AND src=%(src)s AND module=%(module)s
                        );
                    """
            for record in records:
                module = external_ids[record.id].split('.')[0]
                src = record[field.name] or None
                for term in set(field.get_trans_terms(src)):
                    self._cr.execute(
                        query, {
                            'name': "%s,%s" % (field.model_name, field.name),
                            'res_id': record.id,
                            'src': term,
                            'module': module
                        })
        else:
            # insert missing translations for src
            query = """ INSERT INTO ir_translation (lang, type, name, res_id, src, value, module)
                        SELECT l.code, 'model', %(name)s, %(res_id)s, %(src)s, %(src)s, %(module)s
                        FROM res_lang l
                        WHERE l.active AND l.code != 'en_US' AND NOT EXISTS (
                            SELECT 1 FROM ir_translation
                            WHERE lang=l.code AND type='model' AND name=%(name)s AND res_id=%(res_id)s
                        );
                        UPDATE ir_translation SET src=%(src)s
                        WHERE type='model' AND name=%(name)s AND res_id=%(res_id)s AND module=%(module)s;
                    """
            for record in records:
                module = external_ids[record.id].split('.')[0]
                self._cr.execute(
                    query, {
                        'name': "%s,%s" % (field.model_name, field.name),
                        'res_id': record.id,
                        'src': record[field.name] or None,
                        'module': module
                    })
        self.clear_caches()

    @api.model
    def translate_fields(self, model, id, field=None):
        """ Open a view for translating the field(s) of the record (model, id). """
        main_lang = 'en_US'
        if not self.env['res.lang'].search_count([('code', '!=', main_lang)]):
            raise UserError(
                _("Translation features are unavailable until you install an extra translation."
                  ))

        # determine domain for selecting translations
        record = self.env[model].with_context(lang=main_lang).browse(id)
        domain = ['&', ('res_id', '=', id), ('name', '=like', model + ',%')]

        def make_domain(fld, rec):
            name = "%s,%s" % (fld.model_name, fld.name)
            return ['&', ('res_id', '=', rec.id), ('name', '=', name)]

        # insert missing translations, and extend domain for related fields
        for name, fld in record._fields.items():
            if not getattr(fld, 'translate', False):
                continue

            rec = record
            if fld.related:
                try:
                    # traverse related fields up to their data source
                    while fld.related:
                        rec, fld = fld.traverse_related(rec)
                    if rec:
                        domain = ['|'] + domain + make_domain(fld, rec)
                except AccessError:
                    continue

            assert fld.translate and rec._name == fld.model_name
            self.insert_missing(fld, rec)

        action = {
            'name': 'Translate',
            'res_model': 'ir.translation',
            'type': 'ir.actions.act_window',
            'view_mode': 'tree',
            'view_id': self.env.ref('base.view_translation_dialog_tree').id,
            'target': 'new',
            'flags': {
                'search_view': True,
                'action_buttons': True
            },
            'domain': domain,
        }
        if field:
            fld = record._fields[field]
            if not fld.related:
                action['context'] = {
                    'search_default_name':
                    "%s,%s" % (fld.model_name, fld.name),
                }
        return action

    def _get_import_cursor(self, cr, uid, context=None):
        """ Return a cursor-like object for fast inserting translations
        """
        return ir_translation_import_cursor(cr, uid, self, context=context)

    def load_module_terms(self, cr, modules, langs, context=None):
        context = dict(context or {})  # local copy
        # make sure the given languages are active
        lang_obj = self.pool['res.lang']
        for lang in langs:
            lang_obj.load_lang(cr, SUPERUSER_ID, lang)
        # load i18n files
        for module_name in modules:
            modpath = openerp.modules.get_module_path(module_name)
            if not modpath:
                continue
            for lang in langs:
                lang_code = tools.get_iso_codes(lang)
                base_lang_code = None
                if '_' in lang_code:
                    base_lang_code = lang_code.split('_')[0]

                # Step 1: for sub-languages, load base language first (e.g. es_CL.po is loaded over es.po)
                if base_lang_code:
                    base_trans_file = openerp.modules.get_module_resource(
                        module_name, 'i18n', base_lang_code + '.po')
                    if base_trans_file:
                        _logger.info(
                            'module %s: loading base translation file %s for language %s',
                            module_name, base_lang_code, lang)
                        tools.trans_load(cr,
                                         base_trans_file,
                                         lang,
                                         verbose=False,
                                         module_name=module_name,
                                         context=context)
                        context[
                            'overwrite'] = True  # make sure the requested translation will override the base terms later

                    # i18n_extra folder is for additional translations handle manually (eg: for l10n_be)
                    base_trans_extra_file = openerp.modules.get_module_resource(
                        module_name, 'i18n_extra', base_lang_code + '.po')
                    if base_trans_extra_file:
                        _logger.info(
                            'module %s: loading extra base translation file %s for language %s',
                            module_name, base_lang_code, lang)
                        tools.trans_load(cr,
                                         base_trans_extra_file,
                                         lang,
                                         verbose=False,
                                         module_name=module_name,
                                         context=context)
                        context[
                            'overwrite'] = True  # make sure the requested translation will override the base terms later

                # Step 2: then load the main translation file, possibly overriding the terms coming from the base language
                trans_file = openerp.modules.get_module_resource(
                    module_name, 'i18n', lang_code + '.po')
                if trans_file:
                    _logger.info(
                        'module %s: loading translation file (%s) for language %s',
                        module_name, lang_code, lang)
                    tools.trans_load(cr,
                                     trans_file,
                                     lang,
                                     verbose=False,
                                     module_name=module_name,
                                     context=context)
                elif lang_code != 'en_US':
                    _logger.info('module %s: no translation for language %s',
                                 module_name, lang_code)

                trans_extra_file = openerp.modules.get_module_resource(
                    module_name, 'i18n_extra', lang_code + '.po')
                if trans_extra_file:
                    _logger.info(
                        'module %s: loading extra translation file (%s) for language %s',
                        module_name, lang_code, lang)
                    tools.trans_load(cr,
                                     trans_extra_file,
                                     lang,
                                     verbose=False,
                                     module_name=module_name,
                                     context=context)
        return True
Example #17
0
class invite_wizard(osv.osv_memory):
    """ Wizard to invite partners and make them followers. """
    _name = 'mail.wizard.invite'
    _description = 'Invite wizard'

    def default_get(self, cr, uid, fields, context=None):
        result = super(invite_wizard, self).default_get(cr,
                                                        uid,
                                                        fields,
                                                        context=context)
        user_name = self.pool.get('res.users').name_get(cr,
                                                        uid, [uid],
                                                        context=context)[0][1]
        model = result.get('res_model')
        res_id = result.get('res_id')
        if 'message' in fields and model and res_id:
            ir_model = self.pool.get('ir.model')
            model_ids = ir_model.search(
                cr,
                uid, [('model', '=', self.pool[model]._name)],
                context=context)
            model_name = ir_model.name_get(cr, uid, model_ids,
                                           context=context)[0][1]

            document_name = self.pool[model].name_get(cr,
                                                      uid, [res_id],
                                                      context=context)[0][1]
            message = _(
                '<div><p>Hello,</p><p>%s invited you to follow %s document: %s.<p></div>'
            ) % (user_name, model_name, document_name)
            result['message'] = message
        elif 'message' in fields:
            result['message'] = _(
                '<div><p>Hello,</p><p>%s invited you to follow a new document.</p></div>'
            ) % user_name
        return result

    _columns = {
        'res_model':
        fields.char('Related Document Model',
                    size=128,
                    required=True,
                    select=1,
                    help='Model of the followed resource'),
        'res_id':
        fields.integer('Related Document ID',
                       select=1,
                       help='Id of the followed resource'),
        'partner_ids':
        fields.many2many(
            'res.partner',
            string='Recipients',
            help=
            "List of partners that will be added as follower of the current document."
        ),
        'message':
        fields.html('Message'),
        'send_mail':
        fields.boolean(
            'Send Email',
            help=
            "If checked, the partners will receive an email warning they have been "
            "added in the document's followers."),
    }

    def add_followers(self, cr, uid, ids, context=None):
        for wizard in self.browse(cr, uid, ids, context=context):
            model_obj = self.pool[wizard.res_model]
            document = model_obj.browse(cr,
                                        uid,
                                        wizard.res_id,
                                        context=context)

            # filter partner_ids to get the new followers, to avoid sending email to already following partners
            new_follower_ids = [
                p.id for p in wizard.partner_ids
                if p not in document.message_follower_ids
            ]
            model_obj.message_subscribe(cr,
                                        uid, [wizard.res_id],
                                        new_follower_ids,
                                        context=context)

            ir_model = self.pool.get('ir.model')
            model_ids = ir_model.search(cr,
                                        uid, [('model', '=', model_obj._name)],
                                        context=context)
            model_name = ir_model.name_get(cr, uid, model_ids,
                                           context=context)[0][1]

            # send an email if option checked and if a message exists (do not send void emails)
            if wizard.send_mail and wizard.message and not wizard.message == '<br>':  # when deleting the message, cleditor keeps a <br>
                # add signature
                # FIXME 8.0: use notification_email_send, send a wall message and let mail handle email notification + message box
                signature_company = self.pool.get(
                    'mail.notification').get_signature_footer(
                        cr,
                        uid,
                        user_id=uid,
                        res_model=wizard.res_model,
                        res_id=wizard.res_id,
                        context=context)
                wizard.message = tools.append_content_to_html(
                    wizard.message,
                    signature_company,
                    plaintext=False,
                    container_tag='div')

                # send mail to new followers
                # the invite wizard should create a private message not related to any object -> no model, no res_id
                mail_mail = self.pool.get('mail.mail')
                mail_id = mail_mail.create(
                    cr,
                    uid, {
                        'model':
                        wizard.res_model,
                        'res_id':
                        wizard.res_id,
                        'subject':
                        _('Invitation to follow %s: %s') %
                        (model_name, document.name_get()[0][1]),
                        'body_html':
                        '%s' % wizard.message,
                        'auto_delete':
                        True,
                        'recipient_ids': [(4, id) for id in new_follower_ids]
                    },
                    context=context)
                mail_mail.send(cr, uid, [mail_id], context=context)
        return {'type': 'ir.actions.act_window_close'}
Example #18
0
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'
Example #19
0
        return isTrue

    _name = "saas.user.info"
    _description = "saas.user.info"
    _columns = {
        'name': fields.char('Note', required=False,track_visibility='always', help=""),
        'user': fields.many2one('res.partner', 'partner', help=""),
        'host_name': fields.char('host_name',required=True, help=""),
        'host_type': fields.char('host_type',required=False , help=""),
        'server': fields.many2one('saas.server', 'server',required=True, help=""),
        'url_addres': fields.function(_get_full_url, type='char',string='url_addres'),
        'start_date': fields.datetime('start_date',required=False, help=""),
        'end_date': fields.function(_compute_exp_date, type = 'datetime',string='end_date'),
        'product_id':fields.many2one('product.product','product_id',required = False,help=''),
        'state': fields.function(_get_server_state, type='char', string='state'),
        'counter': fields.integer('counter',required=False , help=""),
    }
    _defaults = {
        'start_date': fields.datetime.now,
    }
    _sql_constraints = [
        ('host_name_uniq', 'unique (host_name)', 'host_name must be unique !'),
    ]

class saas_server(osv.osv):
    _inherit = ['mail.thread']
    _name = "saas.server"
    _description = "saas.server"
    _columns = {
        'name': fields.char('name', required=False, help=""),
        'server_addres': fields.char('server_addres', required=True, help=""),
Example #20
0
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,
    }
Example #21
0
            return False

    _name = "rent.order.line"
    _description = "Rent Order Line"
    _columns = {
        "name": fields.text("Description", required=True, readonly=True, states={"draft": [("readonly", False)]}),
        "order_id": fields.many2one(
            "rent.order",
            "Rent Reference",
            required=True,
            ondelete="cascade",
            select=True,
            readonly=True,
            states={"draft": [("readonly", False)]},
        ),
        "sequence": fields.integer("Sequence"),
        "product_id": fields.many2one(
            "product.product", "Product", domain=[("is_rent", "=", True)], change_default=True
        ),
        "invoice_lines": fields.many2many(
            "account.invoice.line",
            "rent_order_line_invoice_rel",
            "order_line_id",
            "invoice_id",
            "Invoice Lines",
            readonly=True,
        ),
        "price_unit": fields.float(
            "Unit Price",
            required=True,
            digits_compute=dp.get_precision("Product Price"),
Example #22
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 {}
Example #23
0
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')),
    ('many2many', fields.many2many('export.many2many.other')),
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}}
Example #25
0
            if res[1]:
                user_id = res[0]
        elif conf['create_user']:
            _logger.debug("Creating new Odoo user \"%s\" from LDAP" % login)
            user_obj = self.pool['res.users']
            values = self.map_ldap_attributes(cr, uid, conf, login, ldap_entry)
            if conf['user']:
                values['active'] = True
                user_id = user_obj.copy(cr, SUPERUSER_ID, conf['user'],
                                        default=values)
            else:
                user_id = user_obj.create(cr, SUPERUSER_ID, values)
        return user_id

    _columns = {
        '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',
Example #26
0
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'])]
Example #27
0
class QueueJob(orm.Model):
    """ Job status and result """
    _name = 'queue.job'
    _description = 'Queue Job'
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _log_access = False

    _order = 'date_created DESC, date_done DESC'

    _removal_interval = 30  # days

    _columns = {
        'worker_id':
        fields.many2one('queue.worker',
                        string='Worker',
                        ondelete='set null',
                        readonly=True),
        'uuid':
        fields.char('UUID', readonly=True, select=True, required=True),
        'user_id':
        fields.many2one('res.users', string='User ID', required=True),
        'name':
        fields.char('Description', readonly=True),
        'func_string':
        fields.char('Task', readonly=True),
        'func':
        fields.text('Pickled Function', readonly=True, required=True),
        'state':
        fields.selection(STATES, string='State', readonly=True, required=True),
        'priority':
        fields.integer('Priority'),
        'exc_info':
        fields.text('Exception Info', readonly=True),
        'result':
        fields.text('Result', readonly=True),
        'date_created':
        fields.datetime('Created Date', readonly=True),
        'date_started':
        fields.datetime('Start Date', readonly=True),
        'date_enqueued':
        fields.datetime('Enqueue Time', readonly=True),
        'date_done':
        fields.datetime('Date Done', readonly=True),
        'eta':
        fields.datetime('Execute only after'),
        'active':
        fields.boolean('Active'),
        'model_name':
        fields.char('Model', readonly=True),
        'retry':
        fields.integer('Current try'),
        'max_retries':
        fields.integer(
            'Max. retries',
            help=
            "The job will fail if the number of tries reach the max. retries.\n"
            "Retries are infinite when empty."),
    }

    _defaults = {
        'active': True,
    }

    def _change_job_state(self,
                          cr,
                          uid,
                          ids,
                          state,
                          result=None,
                          context=None):
        """ Change the state of the `Job` object itself so it
        will change the other fields (date, result, ...)
        """
        if not hasattr(ids, '__iter__'):
            ids = [ids]

        session = ConnectorSession(cr, uid, context=context)
        storage = OpenERPJobStorage(session)
        for job in self.browse(cr, uid, ids, context=context):
            job = storage.load(job.uuid)
            if state == DONE:
                job.set_done(result=result)
            elif state == PENDING:
                job.set_pending(result=result)
            else:
                raise ValueError('State not supported: %s' % state)
            storage.store(job)

    def button_done(self, cr, uid, ids, context=None):
        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
        result = _('Manually set to done by %s') % user.name
        self._change_job_state(cr,
                               uid,
                               ids,
                               DONE,
                               result=result,
                               context=context)
        return True

    def requeue(self, cr, uid, ids, context=None):
        self._change_job_state(cr, uid, ids, PENDING, context=context)
        return True

    def write(self, cr, uid, ids, vals, context=None):
        res = super(QueueJob, self).write(cr, uid, ids, vals, context=context)
        if vals.get('state') == 'failed':
            if not hasattr(ids, '__iter__'):
                ids = [ids]
            # subscribe the users now to avoid to subscribe them
            # at every job creation
            self._subscribe_users(cr, uid, ids, context=context)
            for id in ids:
                msg = self._message_failed_job(cr, uid, id, context=context)
                if msg:
                    self.message_post(cr,
                                      uid,
                                      id,
                                      body=msg,
                                      subtype='connector.mt_job_failed',
                                      context=context)
        return res

    def _subscribe_users(self, cr, uid, ids, context=None):
        """ Subscribe all users having the 'Connector Manager' group """
        group_ref = self.pool.get('ir.model.data').get_object_reference(
            cr, uid, 'connector', 'group_connector_manager')
        if not group_ref:
            return
        group_id = group_ref[1]
        user_ids = self.pool.get('res.users').search(
            cr, uid, [('groups_id', '=', group_id)], context=context)
        self.message_subscribe_users(cr,
                                     uid,
                                     ids,
                                     user_ids=user_ids,
                                     context=context)

    def _message_failed_job(self, cr, uid, id, context=None):
        """ Return a message which will be posted on the job when it is failed.

        It can be inherited to allow more precise messages based on the
        exception informations.

        If nothing is returned, no message will be posted.
        """
        return _("Something bad happened during the execution of the job. "
                 "More details in the 'Exception Information' section.")

    def _needaction_domain_get(self, cr, uid, context=None):
        """ Returns the domain to filter records that require an action
            :return: domain or False is no action
        """
        return [('state', '=', 'failed')]

    def autovacuum(self, cr, uid, context=None):
        """ Delete all jobs (active or not) done since more than
        ``_removal_interval`` days.

        Called from a cron.
        """
        if context is None:
            context = {}
        context = dict(context, active_test=False)
        deadline = datetime.now() - timedelta(days=self._removal_interval)
        deadline_fmt = deadline.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
        job_ids = self.search(cr,
                              uid, [('date_done', '<=', deadline_fmt)],
                              context=context)
        self.unlink(cr, uid, job_ids, context=context)
        return True
Example #28
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'),            
        }
Example #29
0
class mlm_plan(osv.osv):
    _name = "mlm.mlm_plan"
    _columns = {
        'name':
        fields.char('Name', required=True),
        'code':
        fields.char('Code', required=True),
        'max_downline':
        fields.integer(
            'Max Downline',
            help=
            'Number of maximum downlines per node, fill with 0 for unlimited'),
        # help='Berapa jumlah max downline suatu titik, isi dengan 0 untuk unlimited'),

        ###############################################################
        # bonus sponsor
        ###############################################################
        'bonus_sponsor':
        fields.float(
            'Bonus Sponsor Amount',
            help="Sponsor bonus amount, fill with 0 for no sponsor bonus"),
        # help="Berapa nilai bonus sponsor, isi dengan 0 utk tidak ada bonus sponsor"),
        'max_bonus_sponsor_level':
        fields.integer(
            'Max Bonus Sponsor Depth (Up)',
            help=
            'Number of level (up) where the Upline still get the Sponsor Bonus , fill with 0 for unlimited'
        ),
        # help='Berapa jumlah level (keatas) dimana Upline masih dapat Bonus Sponsor, isi dengan 0 untuk unlimited'),
        'bonus_sponsor_percent_decrease':
        fields.float(
            "Bonus Sponsor Percent Decrease",
            help=
            'Percent decrease of the Sponsor Bonus for each up level. Fill with 0.0-1.0. Value of 1.0 means no decrease'
        ),
        # help='Berapa persen penurunan Bonus Sponsor untuk setiap level ke atasnya. Isi dengan 0.0-1.0, nilai 1 artinya tidak ada penurunan'),

        ###############################################################
        # bonus level
        ###############################################################
        'bonus_level':
        fields.float(
            'Bonus Level Amount',
            help="Level bonus amount, fill with 0 for no level bonus"),
        # help="Berapa nilai bonus level, isi dengan 0 untuk tidak ada bonus level"),
        'max_bonus_level_level':
        fields.integer(
            'Max Bonus Level Depth',
            help=
            'Number of level depth where the bonus level is triggered, fill with for unlimited'
        ),
        # help='Berapa jumlah kedalaman level yang masih dapat bonus level, isi dengan 0 untuk unlimited'),
        'bonus_level_percent_decrease':
        fields.float(
            "Bonus Level Percent Decrease",
            help=
            'Percent decrease of the Level Bonus for each up level. Fill with 0.0-1.0. Value of 1.0 means no decrease'
        ),
        # help='Berapa persen penurunan Bonus Level untuk setiap level ke atasnya. Isi dengan 0.0-1.0, nilai 1 artinya tidak ada penurunan'),
        'full_level':
        fields.boolean(
            'Force Full Level?',
            help=
            'The Level Bonus is triggered when the level number of nodes is Full or minimal 1 at left and right side'
        ),
        # help='Bonus level terjadi ketika jumlah level sudah Full atau minimal 1 di kiri dan 1 di kanan'),

        ###############################################################
        # bonus pasangan
        ###############################################################
        'bonus_pasangan':
        fields.float(
            'Bonus Pairing Amount',
            help="Pairing bonus amount, fill with 0 for no pairing bonus"),
        # help="Berapa nilai bonus pasangan, isi dengan 0 untuk tidak ada bonus pasangan"),
        'max_bonus_pasangan_level':
        fields.integer(
            'Max Bonus Pairing Depth',
            help=
            'Number of level depth where the pairing bonus is triggered, fill with for unlimited'
        ),
        # help='Berapa jumlah kedalaman level yang masih dapat bonus pasangan, isi dengan 0 untuk unlimited'),
        'bonus_pasangan_percent_decrease':
        fields.float(
            "Bonus Pairing Percent Decrease",
            help=
            'Percent decrease of the Pairing Bonus for each up level. Fill with 0.0-1.0. Value of 1.0 means no decrease'
        ),
        # help='Berapa persen penurunan Bonus Pairing untuk setiap level ke atasnya. Isi dengan 0.0-1.0, nilai 1 artinya tidak ada penurunan'),
    }
Example #30
0
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
Example #31
0
class hr_expense_line(osv.osv):
    _name = "hr.expense.line"
    _description = "Expense Line"

    def _amount(self, cr, uid, ids, field_name, arg, context=None):
        if not ids:
            return {}
        cr.execute(
            "SELECT l.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_line l WHERE id IN %s GROUP BY l.id ",
            (tuple(ids), ))
        res = dict(cr.fetchall())
        return res

    def _get_uom_id(self, cr, uid, context=None):
        result = self.pool.get('ir.model.data').get_object_reference(
            cr, uid, 'product', 'product_uom_unit')
        return result and result[1] or False

    _columns = {
        'name':
        fields.char('Expense Note', size=128, required=True),
        'date_value':
        fields.date('Date', required=True),
        'expense_id':
        fields.many2one('hr.expense.expense',
                        'Expense',
                        ondelete='cascade',
                        select=True),
        'total_amount':
        fields.function(_amount,
                        string='Total',
                        digits_compute=dp.get_precision('Account')),
        'unit_amount':
        fields.float('Unit Price',
                     digits_compute=dp.get_precision('Product Price')),
        'unit_quantity':
        fields.float(
            'Quantities',
            digits_compute=dp.get_precision('Product Unit of Measure')),
        'product_id':
        fields.many2one('product.product',
                        'Product',
                        domain=[('hr_expense_ok', '=', True)]),
        'uom_id':
        fields.many2one('product.uom', 'Unit of Measure', required=True),
        'description':
        fields.text('Description'),
        'analytic_account':
        fields.many2one('account.analytic.account', 'Analytic account'),
        'ref':
        fields.char('Reference', size=32),
        'sequence':
        fields.integer(
            'Sequence',
            select=True,
            help=
            "Gives the sequence order when displaying a list of expense lines."
        ),
    }
    _defaults = {
        'unit_quantity': 1,
        'date_value': lambda *a: time.strftime('%Y-%m-%d'),
        'uom_id': _get_uom_id,
    }
    _order = "sequence, date_value desc"

    def onchange_product_id(self, cr, uid, ids, product_id, context=None):
        res = {}
        if product_id:
            product = self.pool.get('product.product').browse(cr,
                                                              uid,
                                                              product_id,
                                                              context=context)
            res['name'] = product.name
            amount_unit = product.price_get('standard_price')[product.id]
            res['unit_amount'] = amount_unit
            res['uom_id'] = product.uom_id.id
        return {'value': res}

    def onchange_uom(self, cr, uid, ids, product_id, uom_id, context=None):
        res = {'value': {}}
        if not uom_id or not product_id:
            return res
        product = self.pool.get('product.product').browse(cr,
                                                          uid,
                                                          product_id,
                                                          context=context)
        uom = self.pool.get('product.uom').browse(cr,
                                                  uid,
                                                  uom_id,
                                                  context=context)
        if uom.category_id.id != product.uom_id.category_id.id:
            res['warning'] = {
                'title':
                _('Warning'),
                'message':
                _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure'
                  )
            }
            res['value'].update({'uom_id': product.uom_id.id})
        return res
Example #32
0
class multi_company_default(osv.osv):
    """
    Manage multi company default value
    """
    _name = 'multi_company.default'
    _description = 'Default multi company'
    _order = 'company_id,sequence,id'

    _columns = {
        'sequence':
        fields.integer('Sequence'),
        'name':
        fields.char('Name',
                    size=256,
                    required=True,
                    help='Name it to easily find a record'),
        'company_id':
        fields.many2one('res.company',
                        'Main Company',
                        required=True,
                        help='Company where the user is connected'),
        'company_dest_id':
        fields.many2one('res.company',
                        'Default Company',
                        required=True,
                        help='Company to store the current record'),
        'object_id':
        fields.many2one('ir.model',
                        'Object',
                        required=True,
                        help='Object affected by this rule'),
        'expression':
        fields.char(
            'Expression',
            size=256,
            required=True,
            help=
            'Expression, must be True to match\nuse context.get or user (browse)'
        ),
        'field_id':
        fields.many2one('ir.model.fields',
                        'Field',
                        help='Select field property'),
    }

    _defaults = {
        'expression': 'True',
        'sequence': 100,
    }

    def copy(self, cr, uid, id, default=None, context=None):
        """
        Add (copy) in the name when duplicate record
        """
        if not context:
            context = {}
        if not default:
            default = {}
        company = self.browse(cr, uid, id, context=context)
        default = default.copy()
        default['name'] = company.name + _(' (copy)')
        return super(multi_company_default, self).copy(cr,
                                                       uid,
                                                       id,
                                                       default,
                                                       context=context)
Example #33
0
				all(iline.invoice_id.state != 'cancel' for iline in this.invoice_lines) 
		return res

	def _order_lines_from_invoice(self, cr, uid, ids, context=None):
		# direct access to the m2m table is the less convoluted way to achieve this (and is ok ACL-wise)
		cr.execute("""SELECT DISTINCT sol.id FROM sale_order_invoice_rel rel JOIN
												  sale_order_line sol ON (sol.order_id = rel.order_id)
									WHERE rel.invoice_id = ANY(%s)""", (list(ids),))
		return [i[0] for i in cr.fetchall()]

	_name = 'sale.order.line'
	_description = 'Sales Order Line'
	_columns = {
		'order_id': fields.many2one('sale.order', 'Order Reference', required=True, ondelete='cascade', select=True, readonly=True, states={'draft':[('readonly',False)]}),
		'name': fields.text('Description', required=True, readonly=True, states={'draft': [('readonly', False)]}),
		'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of sales order lines."),
		'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True),
		'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True),
		'invoiced': fields.function(_fnct_line_invoiced, string='Invoiced', type='boolean',
			store={
				'account.invoice': (_order_lines_from_invoice, ['state'], 10),
				'sale.order.line': (lambda self,cr,uid,ids,ctx=None: ids, ['invoice_lines'], 10)}),
		'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price'), readonly=True, states={'draft': [('readonly', False)]}),
		'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Procurement Method', required=True, readonly=True, states={'draft': [('readonly', False)]},
		 help="From stock: When needed, the product is taken from the stock or we wait for replenishment.\nOn order: When needed, the product is purchased or produced."),
		'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Account')),
		'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes', readonly=True, states={'draft': [('readonly', False)]}),
		'address_allotment_id': fields.many2one('res.partner', 'Allotment Partner',help="A partner to whom the particular product needs to be allotted."),
		'product_uom_qty': fields.float('Quantity', digits_compute= dp.get_precision('Product UoS'), required=True, readonly=True, states={'draft': [('readonly', False)]}),
		'product_uom': fields.many2one('product.uom', 'Unit of Measure ', required=True, readonly=True, states={'draft': [('readonly', False)]}),
		'product_uos_qty': fields.float('Quantity (UoS)' ,digits_compute= dp.get_precision('Product UoS'), readonly=True, states={'draft': [('readonly', False)]}),
Example #34
0
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
Example #35
0
        '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"),
        'cc_refund_amt':fields.float('Refund Amt'),
        'is_charged': fields.boolean('CreditCard Charged'),
        'trans_history_ids': fields.one2many('transaction.details', 'voucher_id', 'Transaction History')
    }
    _defaults = {
        'cc_info_hide': lambda * a: True,
        'cc_p_authorize': lambda * a: True,
    }

    def onchange_cc_details(self, cr, uid, ids, cc_details, context={}):
        dic = {}
        if cc_details:
Example #36
0
class ir_attachment(osv.osv):
    """Attachments are used to link binary files or url to any openerp document.

    External attachment storage
    ---------------------------

    The 'data' function field (_data_get,data_set) is implemented using
    _file_read, _file_write and _file_delete which can be overridden to
    implement other storage engines, shuch methods should check for other
    location pseudo uri (example: hdfs://hadoppserver)

    The default implementation is the file:dirname location that stores files
    on the local filesystem using name based on their sha1 hash
    """
    _order = 'id desc'

    def _name_get_resname(self, cr, uid, ids, object, method, context):
        data = {}
        for attachment in self.browse(cr, uid, ids, context=context):
            model_object = attachment.res_model
            res_id = attachment.res_id
            if model_object and res_id:
                model_pool = self.pool[model_object]
                res = model_pool.name_get(cr, uid, [res_id], context)
                res_name = res and res[0][1] or None
                if res_name:
                    field = self._columns.get('res_name', False)
                    if field and len(res_name) > field.size:
                        res_name = res_name[:30] + '...'
                data[attachment.id] = res_name or False
            else:
                data[attachment.id] = False
        return data

    def _storage(self, cr, uid, context=None):
        return self.pool['ir.config_parameter'].get_param(
            cr, SUPERUSER_ID, 'ir_attachment.location', 'file')

    @tools.ormcache(skiparg=3)
    def _filestore(self, cr, uid, context=None):
        return tools.config.filestore(cr.dbname)

    def force_storage(self, cr, uid, context=None):
        """Force all attachments to be stored in the currently configured storage"""
        if not self.pool['res.users'].has_group(cr, uid,
                                                'base.group_erp_manager'):
            raise AccessError(
                _('Only administrators can execute this action.'))

        location = self._storage(cr, uid, context)
        domain = {
            'db': [('store_fname', '!=', False)],
            'file': [('db_datas', '!=', False)],
        }[location]

        ids = self.search(cr, uid, domain, context=context)
        for attach in self.browse(cr, uid, ids, context=context):
            attach.write({'datas': attach.datas})
        return True

    # 'data' field implementation
    def _full_path(self, cr, uid, path):
        # sanitize ath
        path = re.sub('[.]', '', path)
        path = path.strip('/\\')
        return os.path.join(self._filestore(cr, uid), path)

    def _get_path(self, cr, uid, bin_data, sha):
        # retro compatibility
        fname = sha[:3] + '/' + sha
        full_path = self._full_path(cr, uid, fname)
        if os.path.isfile(full_path):
            return fname, full_path  # keep existing path

        # scatter files across 256 dirs
        # we use '/' in the db (even on windows)
        fname = sha[:2] + '/' + sha
        full_path = self._full_path(cr, uid, fname)
        dirname = os.path.dirname(full_path)
        if not os.path.isdir(dirname):
            os.makedirs(dirname)
        return fname, full_path

    def _file_read(self, cr, uid, fname, bin_size=False):
        full_path = self._full_path(cr, uid, fname)
        r = ''
        try:
            if bin_size:
                r = os.path.getsize(full_path)
            else:
                r = open(full_path, 'rb').read().encode('base64')
        except IOError:
            _logger.exception("_read_file reading %s", full_path)
        return r

    def _file_write(self, cr, uid, value, checksum):
        bin_value = value.decode('base64')
        fname, full_path = self._get_path(cr, uid, bin_value, checksum)
        if not os.path.exists(full_path):
            try:
                with open(full_path, 'wb') as fp:
                    fp.write(bin_value)
            except IOError:
                _logger.exception("_file_write writing %s", full_path)
        return fname

    def _file_delete(self, cr, uid, fname):
        count = self.search_count(cr, 1, [('store_fname', '=', fname)])
        full_path = self._full_path(cr, uid, fname)
        if not count and os.path.exists(full_path):
            try:
                os.unlink(full_path)
            except OSError:
                _logger.exception("_file_delete could not unlink %s",
                                  full_path)
            except IOError:
                # Harmless and needed for race conditions
                _logger.exception("_file_delete could not unlink %s",
                                  full_path)

    def _data_get(self, cr, uid, ids, name, arg, context=None):
        if context is None:
            context = {}
        result = {}
        bin_size = context.get('bin_size')
        for attach in self.browse(cr, uid, ids, context=context):
            if attach.store_fname:
                result[attach.id] = self._file_read(cr, uid,
                                                    attach.store_fname,
                                                    bin_size)
            else:
                result[attach.id] = attach.db_datas
        return result

    def _data_set(self, cr, uid, id, name, value, arg, context=None):
        # We dont handle setting data to null
        if not value:
            # reset computed fields
            super(ir_attachment, self).write(cr,
                                             SUPERUSER_ID, [id], {
                                                 'file_size': 0,
                                                 'checksum': None,
                                                 'mimetype': None,
                                                 'index_content': None
                                             },
                                             context=context)
            return True
        if context is None:
            context = {}
        # browse the attachment and get the file to delete
        attach = self.browse(cr, uid, id, context=context)
        fname_to_delete = attach.store_fname
        location = self._storage(cr, uid, context)
        # compute the field depending of datas
        bin_data = value.decode('base64')
        mimetype = self._compute_mimetype(bin_data)
        checksum = self._compute_checksum(bin_data)
        vals = {
            'file_size':
            len(bin_data),
            'checksum':
            checksum,
            'mimetype':
            mimetype,
            'index_content':
            self._index(cr, SUPERUSER_ID, bin_data, attach.datas_fname,
                        mimetype),
        }
        if location != 'db':
            # create the file
            fname = self._file_write(cr, uid, value, checksum)
            vals.update({'store_fname': fname, 'db_datas': False})
        else:
            vals.update({'store_fname': False, 'db_datas': value})
        # SUPERUSER_ID as probably don't have write access, trigger during create
        super(ir_attachment, self).write(cr,
                                         SUPERUSER_ID, [id],
                                         vals,
                                         context=context)

        # After de-referencing the file in the database, check whether we need
        # to garbage-collect it on the filesystem
        if fname_to_delete:
            self._file_delete(cr, uid, fname_to_delete)
        return True

    def _compute_checksum(self, bin_data):
        """ compute the checksum for the given datas
            :param bin_data : datas in its binary form
        """
        if bin_data:
            return hashlib.sha1(bin_data).hexdigest()
        return False

    def _compute_mimetype(self, bin_data):
        """ compute the mimetype of the given datas
            :param bin_data : binary data
            :return mime : string indicating the mimetype, or application/octet-stream by default
        """
        return guess_mimetype(bin_data)

    def _index(self, cr, uid, bin_data, datas_fname, file_type):
        """ compute the index content of the given filename, or binary data.
            This is a python implementation of the unix command 'strings'.
            :param bin_data : datas in binary form
            :return index_content : string containing all the printable character of the binary data
        """
        index_content = False
        if file_type:
            index_content = file_type.split('/')[0]
            if index_content == 'text':  # compute index_content only for text type
                words = re.findall("[^\x00-\x1F\x7F-\xFF]{4,}", bin_data)
                index_content = ustr("\n".join(words))
        return index_content

    _name = 'ir.attachment'
    _columns = {
        'name':
        fields.char('Attachment Name', required=True),
        'datas_fname':
        fields.char('File Name'),
        'description':
        fields.text('Description'),
        'res_name':
        fields.function(_name_get_resname,
                        type='char',
                        string='Resource Name',
                        store=True),
        'res_model':
        fields.char(
            'Resource Model',
            readonly=True,
            help="The database object this attachment will be attached to"),
        'res_id':
        fields.integer('Resource ID',
                       readonly=True,
                       help="The record id this is attached to"),
        'create_date':
        fields.datetime('Date Created', readonly=True),
        'create_uid':
        fields.many2one('res.users', 'Owner', readonly=True),
        'company_id':
        fields.many2one('res.company', 'Company', change_default=True),
        'type':
        fields.selection([
            ('url', 'URL'),
            ('binary', 'Binary'),
        ],
                         'Type',
                         help="Binary File or URL",
                         required=True,
                         change_default=True),
        'url':
        fields.char('Url', size=1024),
        # al: We keep shitty field names for backward compatibility with document
        'datas':
        fields.function(_data_get,
                        fnct_inv=_data_set,
                        string='File Content',
                        type="binary",
                        nodrop=True),
        'store_fname':
        fields.char('Stored Filename'),
        'db_datas':
        fields.binary('Database Data'),
        # computed fields depending on datas
        'file_size':
        fields.integer('File Size', readonly=True),
        'checksum':
        fields.char("Checksum/SHA1", size=40, select=True, readonly=True),
        'mimetype':
        fields.char('Mime Type', readonly=True),
        'index_content':
        fields.text('Indexed Content', readonly=True),
    }

    _defaults = {
        'type':
        'binary',
        'file_size':
        0,
        'mimetype':
        False,
        'company_id':
        lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(
            cr, uid, 'ir.attachment', context=c),
    }

    def _auto_init(self, cr, context=None):
        super(ir_attachment, self)._auto_init(cr, context)
        cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s',
                   ('ir_attachment_res_idx', ))
        if not cr.fetchone():
            cr.execute(
                'CREATE INDEX ir_attachment_res_idx ON ir_attachment (res_model, res_id)'
            )
            cr.commit()

    def check(self, cr, uid, ids, mode, context=None, values=None):
        """Restricts the access to an ir.attachment, according to referred model
        In the 'document' module, it is overriden to relax this hard rule, since
        more complex ones apply there.
        """
        res_ids = {}
        require_employee = False
        if ids:
            if isinstance(ids, (int, long)):
                ids = [ids]
            cr.execute(
                'SELECT DISTINCT res_model, res_id FROM ir_attachment WHERE id = ANY (%s)',
                (ids, ))
            for rmod, rid in cr.fetchall():
                if not (rmod and rid):
                    require_employee = True
                    continue
                res_ids.setdefault(rmod, set()).add(rid)
        if values:
            if values.get('res_model') and values.get('res_id'):
                res_ids.setdefault(values['res_model'],
                                   set()).add(values['res_id'])

        ima = self.pool.get('ir.model.access')
        for model, mids in res_ids.items():
            # ignore attachments that are not attached to a resource anymore when checking access rights
            # (resource was deleted but attachment was not)
            if not self.pool.get(model):
                require_employee = True
                continue
            existing_ids = self.pool[model].exists(cr, uid, mids)
            if len(existing_ids) != len(mids):
                require_employee = True
            ima.check(cr, uid, model, mode)
            self.pool[model].check_access_rule(cr,
                                               uid,
                                               existing_ids,
                                               mode,
                                               context=context)
        if require_employee:
            if not self.pool['res.users'].has_group(cr, uid,
                                                    'base.group_user'):
                raise except_orm(
                    _('Access Denied'),
                    _("Sorry, you are not allowed to access this document."))

    def _search(self,
                cr,
                uid,
                args,
                offset=0,
                limit=None,
                order=None,
                context=None,
                count=False,
                access_rights_uid=None):
        ids = super(ir_attachment,
                    self)._search(cr,
                                  uid,
                                  args,
                                  offset=offset,
                                  limit=limit,
                                  order=order,
                                  context=context,
                                  count=False,
                                  access_rights_uid=access_rights_uid)
        if not ids:
            if count:
                return 0
            return []

        # Work with a set, as list.remove() is prohibitive for large lists of documents
        # (takes 20+ seconds on a db with 100k docs during search_count()!)
        orig_ids = ids
        ids = set(ids)

        # For attachments, the permissions of the document they are attached to
        # apply, so we must remove attachments for which the user cannot access
        # the linked document.
        # Use pure SQL rather than read() as it is about 50% faster for large dbs (100k+ docs),
        # and the permissions are checked in super() and below anyway.
        cr.execute(
            """SELECT id, res_model, res_id FROM ir_attachment WHERE id = ANY(%s)""",
            (list(ids), ))
        targets = cr.dictfetchall()
        model_attachments = {}
        for target_dict in targets:
            if not target_dict['res_model']:
                continue
            # model_attachments = { 'model': { 'res_id': [id1,id2] } }
            model_attachments.setdefault(target_dict['res_model'],
                                         {}).setdefault(
                                             target_dict['res_id'] or 0,
                                             set()).add(target_dict['id'])

        # To avoid multiple queries for each attachment found, checks are
        # performed in batch as much as possible.
        ima = self.pool.get('ir.model.access')
        for model, targets in model_attachments.iteritems():
            if model not in self.pool:
                continue
            if not ima.check(cr, uid, model, 'read', False):
                # remove all corresponding attachment ids
                for attach_id in itertools.chain(*targets.values()):
                    ids.remove(attach_id)
                continue  # skip ir.rule processing, these ones are out already

            # filter ids according to what access rules permit
            target_ids = targets.keys()
            allowed_ids = [0] + self.pool[model].search(
                cr, uid, [('id', 'in', target_ids)], context=context)
            disallowed_ids = set(target_ids).difference(allowed_ids)
            for res_id in disallowed_ids:
                for attach_id in targets[res_id]:
                    ids.remove(attach_id)

        # sort result according to the original sort ordering
        result = [id for id in orig_ids if id in ids]
        return len(result) if count else list(result)

    def read(self,
             cr,
             uid,
             ids,
             fields_to_read=None,
             context=None,
             load='_classic_read'):
        if isinstance(ids, (int, long)):
            ids = [ids]
        self.check(cr, uid, ids, 'read', context=context)
        return super(ir_attachment, self).read(cr,
                                               uid,
                                               ids,
                                               fields_to_read,
                                               context=context,
                                               load=load)

    def write(self, cr, uid, ids, vals, context=None):
        if isinstance(ids, (int, long)):
            ids = [ids]
        self.check(cr, uid, ids, 'write', context=context, values=vals)
        # remove computed field depending of datas
        for field in ['file_size', 'checksum', 'mimetype']:
            vals.pop(field, False)
        return super(ir_attachment, self).write(cr, uid, ids, vals, context)

    def copy(self, cr, uid, id, default=None, context=None):
        self.check(cr, uid, [id], 'write', context=context)
        return super(ir_attachment, self).copy(cr, uid, id, default, context)

    def unlink(self, cr, uid, ids, context=None):
        if isinstance(ids, (int, long)):
            ids = [ids]
        self.check(cr, uid, ids, 'unlink', context=context)

        # First delete in the database, *then* in the filesystem if the
        # database allowed it. Helps avoid errors when concurrent transactions
        # are deleting the same file, and some of the transactions are
        # rolled back by PostgreSQL (due to concurrent updates detection).
        to_delete = [
            a.store_fname for a in self.browse(cr, uid, ids, context=context)
            if a.store_fname
        ]
        res = super(ir_attachment, self).unlink(cr, uid, ids, context)
        for file_path in to_delete:
            self._file_delete(cr, uid, file_path)

        return res

    def create(self, cr, uid, values, context=None):
        # remove computed field depending of datas
        for field in ['file_size', 'checksum', 'mimetype']:
            values.pop(field, False)
        self.check(cr, uid, [], mode='write', context=context, values=values)
        return super(ir_attachment, self).create(cr, uid, values, context)

    def action_get(self, cr, uid, context=None):
        return self.pool.get('ir.actions.act_window').for_xml_id(
            cr, uid, 'base', 'action_attachment', context=context)

    def invalidate_bundle(self, cr, uid, type='%', xmlid=None, context=None):
        assert type in ('%', 'css', 'js'), "Unhandled bundle type"
        xmlid = '%' if xmlid is None else xmlid + '%'
        domain = [('url', '=like', '/web/%s/%s/%%' % (type, xmlid))]
        ids = self.search(cr, uid, domain, context=context)
        if ids:
            self.unlink(cr, uid, ids, context=context)
Example #37
0
                if carrier_grid:
                    try:
                        price = grid_obj.get_price(cr, uid, carrier_grid, order, time.strftime("%Y-%m-%d"), context)
                        available = True
                    except UserError, e:
                        # no suitable delivery method found, probably configuration error
                        _logger.info("Carrier %s: %s", carrier.name, e.name)
                        price = 0.0
                else:
                    price = 0.0
            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",
Example #38
0
class analisa(osv.osv):
    _name = 'hr_training.analisa'
    _rec_name = 'no'

    def action_draft(self, cr, uid, ids, context=None):
        return self.write(cr,
                          uid,
                          ids, {'state': TRAINING_STATES[0][0]},
                          context=context)

    def action_verify(self, cr, uid, ids, context=None):
        return self.write(cr,
                          uid,
                          ids, {'state': TRAINING_STATES[1][0]},
                          context=context)

    def action_reject(self, cr, uid, ids, context=None):
        return self.write(cr,
                          uid,
                          ids, {'state': TRAINING_STATES[4][0]},
                          context=context)

    def action_approve(self, cr, uid, ids, context=None):
        obj = self.browse(cr, uid, ids)[0]
        kode = obj.no
        state = obj.state
        train_obj = self.pool.get('hr_training.train')
        sr = train_obj.search(cr, uid, [('analisa_id', '=', kode)])
        tr = train_obj.browse(cr, uid, sr)
        #yids=[];
        for xids in tr:
            nikid = xids.employee_id.nik
            kod = str(kode) + '/' + str(nikid)
            #yids.append({"nonik" : yes})
            train_obj.write(cr, uid, [xids.id], {'nonik': kod})
        #train_obj.write(cr, uid, [xids.id for ux in tr], {'nonik':yids.nonik})
        return self.write(cr,
                          uid,
                          ids, {'state': TRAINING_STATES[2][0]},
                          context=context)

    '''def action_reject_hr_department(self,cr,uid,ids,context=None): 
    	return self.write(cr,uid,ids,{'state':TRAINING_STATES[2][0]},context=context)'''

    def action_in_prgress(self, cr, uid, ids, context=None):
        return self.write(cr,
                          uid,
                          ids, {'state': TRAINING_STATES[4][0]},
                          context=context)

    def action_approve_hr_department(self, cr, uid, ids, context=None):
        obj = self.browse(cr, uid, ids)[0]
        if obj.tes == "Internal":
            kode = obj.id
            state = TRAINING_STATES[5][0]
            train_obj = self.pool.get('hr_training.train')
            sr = train_obj.search(cr, uid, [('analisa_id', '=', kode)])
            tr = train_obj.browse(cr, uid, sr)
            obj_penilai = self.pool.get("penilaian.pelatih")
            for xids in tr:
                employee = xids.employee_id.id
                analisa = xids.analisa_id.id
                obj_penilai.create(cr, uid, {
                    'name': employee,
                    'analisa_id': analisa
                })
                train_obj.write(cr, uid, [xids.id], {
                    'state': state,
                    'ket': 'Aktif'
                })
            return self.write(cr,
                              uid,
                              ids, {'state': TRAINING_STATES[4][0]},
                              context=context)
        else:
            return self.write(cr,
                              uid,
                              ids, {'state': TRAINING_STATES[3][0]},
                              context=context)

    def action_evaluation(self, cr, uid, ids, context=None):
        obj = self.browse(cr, uid, ids)[0]
        kode = obj.id
        state = TRAINING_STATES[6][0]
        # for Training internal and external
        train_obj = self.pool.get('hr_training.train')
        sr = train_obj.search(cr, uid, [('analisa_id', '=', kode)])
        tr = train_obj.browse(cr, uid, sr)
        obj_penilai = self.pool.get("penilaian.pelatih")
        for xids in tr:
            employee = xids.employee_id.id
            analisa = xids.analisa_id.id
            obj_penilai.create(cr, uid, {
                'name': employee,
                'analisa_id': analisa
            })
            train_obj.write(cr, uid, [xids.id], {
                'state': state,
                'ket': 'Aktif'
            })
        # for Training SIO
        sio_obj = self.pool.get('hr.training_sio')
        sio_src = sio_obj.search(cr, uid, [('analisa_id', '=', kode)])
        for sio in sio_obj.browse(cr, uid, sio_src):
            employee = sio.employee_id.id
            analisa = sio.analisa_id.id
            obj_penilai.create(cr, uid, {
                'name': employee,
                'analisa_id': analisa
            })
            sio_obj.write(cr, uid, [sio.id], {'state': state, 'status': True})
        #import pdb;pdb.set_trace()
        tes = obj.tes
        if tes == 'SIO':
            for employee in obj.sio_ids:
                iso = employee.iso.id
                name = employee.employee_id.id
                analisa = employee.analisa_id.id
                obj = self.pool.get('hr.training_sio')
                src = obj.search(cr, uid, [('employee_id', '=', name),
                                           ('iso', '=', iso),
                                           ('state', '=', 'evaluation'),
                                           ('analisa_id', '!=', analisa),
                                           ('status', '=', True)])
                for sio in obj.browse(cr, uid, src):
                    obj.write(cr, uid, [sio.id], {
                        'status': False,
                        'link_warning': False
                    })
        return self.write(cr, uid, ids, {'state': state}, context=context)

    def create(self, cr, uid, vals, context=None):
        obj = self.pool.get('hr_training.subject')
        sid = vals['subject_id']
        vals['subject'] = obj.browse(cr, uid, sid).name
        kode = self.pool.get('ir.sequence').get(cr, uid,
                                                'hr_training.analisa.nomor')
        kode = str(obj.browse(cr, uid, sid).code) + '/' + str(kode)
        vals['no'] = kode
        return super(analisa, self).create(cr, uid, vals, context)

    def _get_number_of_days(self, date_from, date_to):
        """Returns a float equals to the timedelta between two dates given as string."""
        #import pdb;pdb.set_trace()
        DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
        from_dt = datetime.strptime(date_from, DATETIME_FORMAT)
        to_dt = datetime.strptime(date_to, DATETIME_FORMAT)
        timedelta = to_dt - from_dt
        diff_day = timedelta.days + float(timedelta.seconds) / 86400
        return diff_day

    def onchange_date_from(self, cr, uid, ids, date_to, date_from):
        """
        If there are no date set for date_to, automatically set one 8 hours later than
        the date_from.
        Also update the number_of_days.
        """
        # date_to has to be greater than date_from
        if (date_from and date_to) and (date_from > date_to):
            raise osv.except_osv(
                _('Warning!'),
                _('The start date must be anterior to the end date.'))

        result = {'value': {}}

        # No date_to set so far: automatically compute one 8 hours later
        if date_from and not date_to:
            #import pdb;pdb.set_trace()
            date_to_with_delta = datetime.strptime(
                date_from, tools.DEFAULT_SERVER_DATETIME_FORMAT
            )  #+ datetime.timedelta(hours=8)
            result['value']['date_to'] = str(date_to_with_delta)

        # Compute and update the number of days
        if (date_to and date_from) and (date_from <= date_to):
            diff_day = self._get_number_of_days(date_from, date_to)
            result['value']['durasi'] = round(math.floor(diff_day)) + 1
        else:
            result['value']['durasi'] = 0

        return result

    def onchange_date_to(self, cr, uid, ids, date_to, date_from):
        """
        Update the number_of_days.
        """

        # date_to has to be greater than date_from
        if (date_from and date_to) and (date_from > date_to):
            raise osv.except_osv(
                _('Warning!'),
                _('The start date must be anterior to the end date.'))

        result = {'value': {}}

        # Compute and update the number of days
        if (date_to and date_from) and (date_from <= date_to):
            diff_day = self._get_number_of_days(date_from, date_to)
            result['value']['durasi'] = round(math.floor(diff_day)) + 1
        else:
            result['value']['durasi'] = 0

        return result

    def _compute_number_of_days(self, cr, uid, ids, name, args, context=None):
        result = {}
        for hol in self.browse(cr, uid, ids, context=context):
            result[hol.id] = -hol.lama
        return result

    def unlink(self, cr, uid, ids, context=None):
        for rec in self.browse(cr, uid, ids, context=context):
            if rec.state == 'evaluation':
                raise osv.except_osv(
                    _('Peringatan!'),
                    _('anda tidak bisa menghapus Training karna Status Training Evaluasi.'
                      ))
            no = rec.id
            obj = self.pool.get('hr_training.train')
            src = obj.search(cr, uid, [('analisa_id', '=', no)])
            for ana in obj.browse(cr, uid, src):
                obj.unlink(cr, uid, ana.id, context=context)
        return super(analisa, self).unlink(cr, uid, ids, context)

    def onchange_dep(self, cr, uid, ids, user_id, context=None):
        obj = self.pool.get('hr.employee')
        src = obj.search(cr, uid, [('user_id', '=', user_id)])
        brw = obj.browse(cr, uid, src)
        dep = False
        for department in brw:
            dep = department.department_id.id
        return {
            'value': {
                'department_id': dep,
            }
        }

    _columns = {
        'employee_id':
        fields.many2one('hr.employee', 'Nama Peserta'),
        'department_id':
        fields.many2one('hr.department', 'Department'),
        'bulan':
        fields.selection([('Januari', 'Januari'), ('Februari', 'Februari'),
                          ('Maret', 'Maret'), ('April', 'April'),
                          ('Mei', 'Mei'), ('Juni', 'Juni'), ('Juli', 'Juli'),
                          ('Agustus', 'Agustus'), ('September', 'September'),
                          ('Oktober', 'Oktober'), ('November', 'November'),
                          ('Desember', 'Desember')], 'Bulan'),
        'tes':
        fields.selection([('Internal', 'Internal'), ('Eksternal', 'Eksternal'),
                          ('SIO', 'SIO'), ('non_training', 'Non Training')],
                         'Jenis Pelatihan',
                         readonly=True),
        'presentasi':
        fields.char('Presentasi Pelatihan', 60),
        'no':
        fields.char('Nomor', 10, readonly=True),
        'paket_id':
        fields.many2one('hr_training.paket', 'Paket Pelatihan'),
        'subject_id':
        fields.many2one('hr_training.subject',
                        'Nama Pelatihan',
                        required=True,
                        store=True),
        'penyelenggara':
        fields.char('Lembaga Penyelenggara', 128),
        'mgt_id':
        fields.many2one('hr_training.mgt_company', 'MGT Company'),
        'nama':
        fields.char(
            'Nama Trainer',
            50,
        ),
        'tanggal':
        fields.date('Tanggal Penyelenggaraan'),
        'catatan':
        fields.char(
            'Catatan Umum',
            60,
        ),
        'lama':
        fields.char('Lama', 25),
        'durasi':
        fields.integer('Durasi', store=True),
        'employee_ids':
        fields.one2many('hr_training.train', 'analisa_id', 'Nama Karyawan'),
        'penilai_ids':
        fields.one2many('penilaian.pelatih', 'analisa_id', 'Nama Karyawan'),
        'sio_ids':
        fields.one2many('hr.training_sio', 'analisa_id', 'Nama Karyawan'),
        'date_ids':
        fields.one2many('hr.training.waktu', 'analisa_id',
                        'Schedule Perencanaan'),
        'state':
        fields.selection(TRAINING_STATES,
                         'Status',
                         readonly=True,
                         help="Gives the status of the training."),
        'user_id':
        fields.many2one('res.users',
                        'Creator',
                        'Masukan User ID Anda',
                        readonly=True),
        'description':
        fields.text('Deskripsi Pelatihan'),
        'subject':
        fields.char("Nama Pelatihan", readonly=True),
        'date_from':
        fields.datetime('Tanggal Mulai', ),
        'date_to':
        fields.datetime('Tanggal Berakhir', ),
        'number_of_days':
        fields.function(_compute_number_of_days,
                        string='Jumlah Hari',
                        store=True,
                        readonly=True),
        'pp':
        fields.many2one('peraturan.perundangan', 'Peraturan Perundangan'),
        'pt':
        fields.many2one('peraturan.tentanng', 'Peraturan Tentang'),
        'nama_sertifikat':
        fields.many2one(
            'sertifikat',
            'Nama Sertifikat',
        ),
        'iso':
        fields.many2one('iso', 'Nama SIO'),
        'tempat_pelatihan':
        fields.char('Templat Pelatihan'),
        'budget_info':
        fields.float('Budget Info'),
    }

    _defaults = {
        'state': TRAINING_STATES[0][0],
        'user_id': lambda obj, cr, uid, context: uid,
        'tes': 'SIO',
        'subject_id': 1,
        #'no': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'hr_training.analisa.nomor'),
    }

    _sql_constraints = [('no_uniq', 'unique(no)',
                         'Kode Training tidak boleh sama')]
Example #39
0
            logger.debug('Trying to get connection to SERVER %s, PORT %s, USER %s, PWD %s, DRIVER %s' % (tk_pyodbc.server, tk_pyodbc.port, tk_pyodbc.user, tk_pyodbc.pwd, tk_pyodbc.driver))
            logger.debug("DRIVER=%s;SERVER=%s;UID=%s;PWD=%s;PORT=%s;%s" % (tk_pyodbc.driver, tk_pyodbc.server, tk_pyodbc.user, tk_pyodbc.pwd, tk_pyodbc.port, tk_pyodbc.optional_params))
            conn = pyodbc.connect("DRIVER=%s;SERVER=%s;UID=%s;PWD=%s;PORT=%s;%s" % (tk_pyodbc.driver, tk_pyodbc.server, tk_pyodbc.user, tk_pyodbc.pwd, tk_pyodbc.port, tk_pyodbc.optional_params), timeout=timeout or 0)
            logger.debug("Connection acquired")
            return conn
        except Exception, e:
            raise orm.except_orm(_('Error !'), _('Could not get a valid connection. %s') % e)

    def check_download_connection(self, cr, uid, ids, context=None):
        """
        checks the connection to the sql server
        """
        conn = self.get_connection(cr, uid, ids, context)
        conn.close()
        raise orm.except_orm(_('Success'), _('Connection to Oscar was successful!'))

    _columns = {
        'name': fields.char('Name', size=64, required=True),
        'database_type': fields.selection(__database_types, 'Database Type'),
        'server': fields.char('Server address', size=256, required=False),
        'database': fields.char('Database name', size=256, required=False),
        'port': fields.integer('Database port'),
        'user': fields.char('Username', size=256, required=False),
        'pwd': fields.char('Password', size=256, required=False),
        'driver': fields.char('Driver location', size=256),
        'optional_params': fields.char('Optional parameters', size=256),
    }


tk_pyodbc()
Example #40
0
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,
    }
Example #41
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,
        'smart_budget': 0.0,
class report_event_registration(osv.osv):
    _name = "report.event.registration"
    _description = "Events Analysis"
    _auto = False
    _columns = {
        'event_date': fields.datetime('Event Date', readonly=True),
        'event_id': fields.many2one('event.event', 'Event', required=True),
        'draft_state': fields.integer(' # No of Draft Registrations', size=20),
        'confirm_state': fields.integer(' # No of Confirmed Registrations', size=20),
        'seats_max': fields.integer('Max Seats'),
        'nbevent': fields.integer('Number Of Events'),
        'event_type': fields.many2one('event.type', 'Event Type'),
        'registration_state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Attended'), ('cancel', 'Cancelled')], 'Registration State', readonly=True, required=True),
        'event_state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Event State', readonly=True, required=True),
        'user_id': fields.many2one('res.users', 'Event Responsible', readonly=True),
        'user_id_registration': fields.many2one('res.users', 'Register', readonly=True),
        'name_registration': fields.char('Participant / Contact Name',size=45, readonly=True),
        'company_id': fields.many2one('res.company', 'Company', readonly=True),
    }
    _order = 'event_date desc'

    def init(self, cr):
        """
        Initialize the sql view for the event registration
        """
        tools.drop_view_if_exists(cr, 'report_event_registration')

        # TOFIX this request won't select events that have no registration
        cr.execute(""" CREATE VIEW report_event_registration AS (
            SELECT
                e.id::char || '/' || coalesce(r.id::char,'') AS id,
                e.id AS event_id,
                e.user_id AS user_id,
                r.user_id AS user_id_registration,
                r.name AS name_registration,
                e.company_id AS company_id,
                e.date_begin AS event_date,
                count(e.id) AS nbevent,
                CASE WHEN r.state IN ('draft') THEN r.nb_register ELSE 0 END AS draft_state,
                CASE WHEN r.state IN ('open','done') THEN r.nb_register ELSE 0 END AS confirm_state,
                e.type AS event_type,
                e.seats_max AS seats_max,
                e.state AS event_state,
                r.state AS registration_state
            FROM
                event_event e
                LEFT JOIN event_registration r ON (e.id=r.event_id)

            GROUP BY
                event_id,
                user_id_registration,
                r.id,
                registration_state,
                r.nb_register,
                event_type,
                e.id,
                e.date_begin,
                e.user_id,
                event_state,
                e.company_id,
                e.seats_max,
                name_registration
        )
        """)
Example #43
0
        if res:
            if res[1]:
                user_id = res[0]
        elif conf["create_user"]:
            _logger.debug('Creating new OpenERP user "%s" from LDAP' % login)
            user_obj = self.pool.get("res.users")
            values = self.map_ldap_attributes(cr, uid, conf, login, ldap_entry)
            if conf["user"]:
                user_id = user_obj.copy(cr, SUPERUSER_ID, conf["user"], default={"active": True})
                user_obj.write(cr, SUPERUSER_ID, user_id, values)
            else:
                user_id = user_obj.create(cr, SUPERUSER_ID, values)
        return user_id

    _columns = {
        "sequence": fields.integer("Sequence"),
        "company": fields.many2one("res.company", "Company", required=True, ondelete="cascade"),
        "ldap_server": fields.char("LDAP Server address", size=64, required=True),
        "ldap_server_port": fields.integer("LDAP Server port", required=True),
        "ldap_binddn": fields.char(
            "LDAP binddn",
            size=64,
            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",
            size=64,
            help=("The password of the user account on the LDAP server that is " "used to query the directory."),
Example #44
0
class followup_line(osv.osv):
    def _get_default_template(self, cr, uid, ids, context=None):
        try:
            return self.pool.get('ir.model.data').get_object_reference(
                cr, uid, 'account_followup',
                'email_template_account_followup_default')[1]
        except ValueError:
            return False

    _name = 'account_followup.followup.line'
    _description = 'Follow-up Criteria'
    _columns = {
        'name':
        fields.char('Follow-Up Action', required=True),
        'sequence':
        fields.integer(
            'Sequence',
            help=
            "Gives the sequence order when displaying a list of follow-up lines."
        ),
        'delay':
        fields.integer(
            'Due Days',
            help=
            "The number of days after the due date of the invoice to wait before sending the reminder.  Could be negative if you want to send a polite alert beforehand.",
            required=True),
        'followup_id':
        fields.many2one('account_followup.followup',
                        'Follow Ups',
                        required=True,
                        ondelete="cascade"),
        'description':
        fields.text('Printed Message', translate=True),
        'send_email':
        fields.boolean('Send an Email',
                       help="When processing, it will send an email"),
        'send_letter':
        fields.boolean('Send a Letter',
                       help="When processing, it will print a letter"),
        'manual_action':
        fields.boolean(
            'Manual Action',
            help=
            "When processing, it will set the manual action to be taken for that customer. "
        ),
        'manual_action_note':
        fields.text(
            'Action To Do',
            placeholder="e.g. Give a phone call, check with others , ..."),
        'manual_action_responsible_id':
        fields.many2one('res.users',
                        'Assign a Responsible',
                        ondelete='set null'),
        'email_template_id':
        fields.many2one('email.template',
                        'Email Template',
                        ondelete='set null'),
    }
    _order = 'delay'
    _sql_constraints = [('days_uniq', 'unique(followup_id, delay)',
                         'Days of the follow-up levels must be different')]
    _defaults = {
        'send_email': True,
        'send_letter': True,
        'manual_action': False,
        'description': """
        Dear %(partner_name)s,

Exception made if there was a mistake of ours, it seems that the following amount stays unpaid. Please, take appropriate measures in order to carry out this payment in the next 8 days.

Would your payment have been carried out after this mail was sent, please ignore this message. Do not hesitate to contact our accounting department.

Best Regards,
""",
        'email_template_id': _get_default_template,
    }

    def _check_description(self, cr, uid, ids, context=None):
        for line in self.browse(cr, uid, ids, context=context):
            if line.description:
                try:
                    line.description % {
                        'partner_name': '',
                        'date': '',
                        'user_signature': '',
                        'company_name': ''
                    }
                except:
                    return False
        return True

    _constraints = [
        (_check_description,
         'Your description is invalid, use the right legend or %% if you want to use the percent character.',
         ['description']),
    ]
Example #45
0
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)
Example #46
0
class hr_job(osv.osv):
    _inherit = "hr.job"
    _name = "hr.job"
    _inherits = {'mail.alias': 'alias_id'}

    def _get_attached_docs(self, cr, uid, ids, field_name, arg, context=None):
        res = {}
        attachment_obj = self.pool.get('ir.attachment')
        for job_id in ids:
            applicant_ids = self.pool.get('hr.applicant').search(
                cr, uid, [('job_id', '=', job_id)], context=context)
            res[job_id] = attachment_obj.search(
                cr,
                uid, [
                    '|', '&', ('res_model', '=', 'hr.job'),
                    ('res_id', '=', job_id), '&',
                    ('res_model', '=', 'hr.applicant'),
                    ('res_id', 'in', applicant_ids)
                ],
                context=context)
        return res

    def _count_all(self, cr, uid, ids, field_name, arg, context=None):
        Applicant = self.pool['hr.applicant']
        return {
            job_id: {
                'application_count':
                Applicant.search_count(cr,
                                       uid, [('job_id', '=', job_id)],
                                       context=context),
                'documents_count':
                len(
                    self._get_attached_docs(cr,
                                            uid, [job_id],
                                            field_name,
                                            arg,
                                            context=context)[job_id])
            }
            for job_id in ids
        }

    _columns = {
        'survey_id':
        fields.many2one(
            'survey.survey',
            'Interview Form',
            help=
            "Choose an interview form for this job position and you will be able to print/answer this interview from all applicants who apply for this job"
        ),
        'alias_id':
        fields.many2one(
            'mail.alias',
            'Alias',
            ondelete="restrict",
            required=True,
            help=
            "Email alias for this job position. New emails will automatically "
            "create new applicants for this job position."),
        'address_id':
        fields.many2one('res.partner',
                        'Job Location',
                        help="Address where employees are working"),
        'application_ids':
        fields.one2many('hr.applicant', 'job_id', 'Applications'),
        'application_count':
        fields.function(_count_all,
                        type='integer',
                        string='Applications',
                        multi=True),
        'manager_id':
        fields.related('department_id',
                       'manager_id',
                       type='many2one',
                       string='Department Manager',
                       relation='hr.employee',
                       readonly=True,
                       store=True),
        'document_ids':
        fields.function(_get_attached_docs,
                        type='one2many',
                        relation='ir.attachment',
                        string='Applications'),
        'documents_count':
        fields.function(_count_all,
                        type='integer',
                        string='Documents',
                        multi=True),
        'user_id':
        fields.many2one('res.users',
                        'Recruitment Responsible',
                        track_visibility='onchange'),
        'color':
        fields.integer('Color Index'),
    }

    def _address_get(self, cr, uid, context=None):
        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
        return user.company_id.partner_id.id

    _defaults = {'address_id': _address_get}

    def _auto_init(self, cr, context=None):
        """Installation hook to create aliases for all jobs and avoid constraint errors."""
        return self.pool.get('mail.alias').migrate_to_alias(
            cr,
            self._name,
            self._table,
            super(hr_job, self)._auto_init,
            'hr.applicant',
            self._columns['alias_id'],
            'name',
            alias_prefix='job+',
            alias_defaults={'job_id': 'id'},
            context=context)

    def create(self, cr, uid, vals, context=None):
        alias_context = dict(context,
                             alias_model_name='hr.applicant',
                             alias_parent_model_name=self._name)
        job_id = super(hr_job, self).create(cr,
                                            uid,
                                            vals,
                                            context=alias_context)
        job = self.browse(cr, uid, job_id, context=context)
        self.pool.get('mail.alias').write(cr, uid, [job.alias_id.id], {
            'alias_parent_thread_id': job_id,
            "alias_defaults": {
                'job_id': job_id
            }
        }, context)
        return job_id

    def unlink(self, cr, uid, ids, context=None):
        # Cascade-delete mail aliases as well, as they should not exist without the job position.
        mail_alias = self.pool.get('mail.alias')
        alias_ids = [
            job.alias_id.id
            for job in self.browse(cr, uid, ids, context=context)
            if job.alias_id
        ]
        res = super(hr_job, self).unlink(cr, uid, ids, context=context)
        mail_alias.unlink(cr, uid, alias_ids, context=context)
        return res

    def action_print_survey(self, cr, uid, ids, context=None):
        job = self.browse(cr, uid, ids, context=context)[0]
        survey_id = job.survey_id.id
        return self.pool.get('survey.survey').action_print_survey(
            cr, uid, [survey_id], context=context)

    def action_get_attachment_tree_view(self, cr, uid, ids, context=None):
        #open attachments of job and related applicantions.
        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)
        applicant_ids = self.pool.get('hr.applicant').search(
            cr, uid, [('job_id', 'in', ids)], context=context)
        action['context'] = {
            'default_res_model': self._name,
            'default_res_id': ids[0]
        }
        action['domain'] = str([
            '|', '&', ('res_model', '=', 'hr.job'), ('res_id', 'in', ids), '&',
            ('res_model', '=', 'hr.applicant'), ('res_id', 'in', applicant_ids)
        ])
        return action

    def action_set_no_of_recruitment(self, cr, uid, id, value, context=None):
        return self.write(cr,
                          uid, [id], {'no_of_recruitment': value},
                          context=context)
Example #47
0
                    available = True
                  except UserError, e:
                    # no suitable delivery method found, probably configuration error
                    _logger.info("Carrier %s: %s", carrier.name, e.name)
                    price = 0.0
              else:
                  price = 0.0
            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 = {
class wizard_payment_specification(orm.TransientModel):
    _name = 'wizard.payment.specification'
    _description = 'Wizard Payment Specification'

    def _resume_page(self, cr, uid, ids, field_name, arg, context=None):
        res = {}
        for t_wizard in self.browse(cr, uid, ids, context):
            t_actual = str(t_wizard.actual_page)
            t_total = str(t_wizard.total_pages)
            res[t_wizard.id] = t_actual + ' di ' + t_total
        return res

    def _is_last_page(self, cr, uid, ids, field_name, arg, context=None):
        res = {}
        for t_wizard in self.browse(cr, uid, ids, context):
            t_actual = t_wizard.actual_page
            t_total = t_wizard.total_pages
            if t_actual == t_total:
                res[t_wizard.id] = True
            else:
                res[t_wizard.id] = False
        return res

    _columns = {
        'partner_id':
        fields.many2one('res.partner', 'Supplier'),
        'maturity':
        fields.date('Maturity Maximum'),
        'journal_id':
        fields.many2one('account.journal', 'Journal'),
        'draft_ids':
        fields.one2many('wizard.payment.specification.line',
                        'payment_specification_id',
                        domain=[
                            '|', ('is_selected', '!=', 'accepted'),
                            ('is_selected', '=', None)
                        ],
                        string="Drafts",
                        readonly=True),
        'accepted_draft_ids':
        fields.one2many('wizard.payment.specification.line',
                        'payment_specification_id',
                        domain=[('is_selected', '=', 'accepted')],
                        string="Accepted Drafts",
                        readonly=True),
        'bank_id':
        fields.many2one('res.partner.bank', 'bank'),
        'all_supplier':
        fields.boolean('All suppliers'),
        'it_supplier':
        fields.boolean('Italian suppliers'),
        'ext_supplier':
        fields.boolean('Foreign suppliers'),
        'actual_page':
        fields.integer('Actual Page'),
        'total_pages':
        fields.integer('Total Pages'),
        'pages_resume':
        fields.function(_resume_page, string='Page', type='text'),
        'is_last_page':
        fields.function(_is_last_page, string='Is Last Page', type='boolean'),
    }

    _defaults = {'all_supplier': False, 'actual_page': 1}

    def get_wizard_supplier_filters(self, cr, uid, context=None):
        filters = [('supplier', '=', True)]
        t_country_obj = self.pool.get('res.country')
        t_italy_id = t_country_obj.search(cr, uid, [('name', '=', 'Italy')])
        t_it_suppliers = context.get('default_it_supplier')
        t_ext_suppliers = context.get('default_ext_supplier')

        if (t_it_suppliers and t_ext_suppliers):
            raise orm.except_orm(
                _('Error!'),
                _('Se si vogliono fornitori sia Italiani che esteri lasciare vuote le caselle'
                  ))

        if (t_it_suppliers and (not t_ext_suppliers)):
            f = ('country_id', '=', t_italy_id)
            filters.append(f)

        if (t_ext_suppliers and (not t_it_suppliers)):
            f = ('country_id', '!=', t_italy_id)
            filters.append(f)

        if (len(filters) > 1):
            filters.insert(0, '&')

        return filters

    def get_wizard_filters(self, cr, uid, partner_ids, context=None):
        filters = []
        t_maturity = context.get('default_maturity', None)
        company_obj = self.pool.get('res.company')

        voucher_obj = self.pool.get('account.voucher')
        my_company_id = voucher_obj.get_company(cr, uid, context=None)
        my_company = company_obj.browse(cr, uid, my_company_id)

        t_active = my_company.bonus_active_account_id.id
        t_passive = my_company.bonus_passive_account_id.id

        if (t_maturity):
            f = ('date_maturity', '<=', t_maturity)
            filters.append(f)
        filters.append(('is_wht', '!=', True))
        filters.append(('reconcile_id', '=', None))
        filters.append(('date_maturity', '!=', None))
        filters.append(('credit', '>', 0.0))
        filters.append(('wht_state', 'in', [False, None]))
        filters.append('!')
        filters.append(('name', 'ilike', 'acconto pagabile'))
        filters.append(('account_id', 'not in', [t_active, t_passive]))
        filters.append(('partner_id', 'in', partner_ids))
        return filters

    def check_move_line_in_invoice(self,
                                   cr,
                                   uid,
                                   t_move_line_ids,
                                   context=None):
        move_line_obj = self.pool.get('account.move.line')
        invoice_obj = self.pool.get('account.invoice')
        t_move_line_id = t_move_line_ids[0]
        t_move_line = move_line_obj.browse(cr, uid, t_move_line_id)
        t_move_id = t_move_line.move_id.id
        invoice_ids = invoice_obj.search(cr, uid,
                                         [('move_id', '=', t_move_id)])
        if (invoice_ids):
            t_invoice = invoice_obj.browse(cr, uid, invoice_ids[0])
            if (t_invoice.type == 'in_invoice'
                    or t_invoice.type == 'in_refund'):
                return True
            return False
        return True

    def view_new_report(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        mod_obj = self.pool.get('ir.model.data')
        result = mod_obj.get_object_reference(
            cr, uid, 'account_voucher_makeover',
            'wizard_action_payment_account_voucher_makeover_view')
        view_id = result and result[1] or False

        return {
            'name': _("Payment Action"),
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'wizard.supplier.payment',
            'type': 'ir.actions.act_window',
            'view_id': view_id,
            'context': context,
            'target': 'new',
        }

    def set_confirm_payment_lines(self, cr, uid, ids, context=None):

        t_lines = []
        vals = {}

        wizard_confirm_obj = self.pool.get('wizard.confirm.payment')
        line_obj = self.pool.get('account.move.line')
        invoice_obj = self.pool.get('account.invoice')

        context_partner_id = context.get('default_partner_id', None)
        t_journal_id = context.get('default_journal_id', None)
        t_all_suppliers = context.get('all_supplier', None)
        t_maturity = context.get('default_maturity', None)
        t_it_suppliers = context.get('it_supplier', None)
        t_ext_suppliers = context.get('ext_supplier', None)

        vals['partner_id'] = context_partner_id
        if (t_all_suppliers):
            vals['partner_id'] = None
        vals['maturity'] = t_maturity
        vals['journal_id'] = t_journal_id
        vals['all_supplier'] = t_all_suppliers
        vals['it_supplier'] = t_it_suppliers
        vals['ext_supplier'] = t_ext_suppliers

        res_id = wizard_confirm_obj.create(cr, uid, vals, context=context)

        t_wps = self.browse(cr, uid, ids[0])

        for line in t_wps.accepted_draft_ids:
            t_move_line = line['move_line_id']
            t_state = t_move_line.state
            t_pt = t_move_line.payment_type_move_line
            t_partner_bank_id = None

            dict_line = {
                'partner_id': line.partner_id.id,
                'account_id': line.account_id.id,
                'state': t_state,
                'is_selected': 'accepted',
                'move_line_id': t_move_line.id,
                'confirm_payment_id': res_id,
                'amount': line.amount,
                'amount_partial': line.amount,
                'payment_type': t_pt,
                'partner_bank_id': t_partner_bank_id,
            }
            # se tipo pagamento bonifico allora ricava partner_bank_id dalla fattura
            if t_pt and t_pt == 'B':
                t_move_line_id = t_move_line.id
                t_invoice_id = line_obj.get_invoice(cr, uid, [t_move_line_id],
                                                    context)
                if t_invoice_id and t_invoice_id[t_move_line_id]:
                    t_invoice_id = t_invoice_id[t_move_line_id]
                    t_invoice_data = invoice_obj.browse(cr, uid, t_invoice_id)
                    if t_invoice_data.partner_bank_id:
                        t_partner_bank_id = t_invoice_data.partner_bank_id.id
                        dict_line.update(
                            {'partner_bank_id': t_partner_bank_id})

            t_lines.append((0, 0, dict_line))

        wizard_confirm_obj.write(cr, uid, [res_id], {'line_ids': t_lines})

        return res_id

    def view_confirm_payment(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        mod_obj = self.pool.get('ir.model.data')
        result = mod_obj.get_object_reference(cr, uid,
                                              'account_voucher_makeover',
                                              'wizard_confirm_payment_view')
        view_id = result and result[1] or False

        t_ws = self.browse(cr, uid, ids[0])
        if (not t_ws.accepted_draft_ids and len(t_ws.accepted_draft_ids) < 1):
            raise orm.except_orm(_('Error!'),
                                 _('Selezionare almeno una riga se presente'))

        res_id = self.set_confirm_payment_lines(cr, uid, ids, context)

        return {
            'name': _("Confirm Payment Action"),
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'wizard.confirm.payment',
            'type': 'ir.actions.act_window',
            'res_id': res_id,
            'view_id': view_id,
            'context': context,
            'target': 'inlineview'
        }

    def action_move_all_forward(self, cr, uid, ids, context=None):
        res = None
        t_spec = self.browse(cr, uid, ids[0])
        line_obj = self.pool.get('wizard.payment.specification.line')
        for line in t_spec.draft_ids:
            context.update({'line_id': line.id})
            res = line_obj.move_draft_forward(cr, uid, ids, context=context)
        return res

    def action_move_all_backward(self, cr, uid, ids, context=None):
        res = None
        t_spec = self.browse(cr, uid, ids[0])
        line_obj = self.pool.get('wizard.payment.specification.line')
        for line in t_spec.accepted_draft_ids:
            context.update({'line_id': line.id})
            res = line_obj.move_draft_backward(cr, uid, ids, context=context)
        return res

    def move_page(self, cr, uid, ids, context=None):
        wizard_line_obj = self.pool.get('wizard.payment.specification.line')
        data = self.browse(cr, uid, ids[0], context=context)
        t_maturity = data.maturity
        t_bank = data.bank_id.id
        t_journal = data.journal_id.id
        t_all_supplier = data.all_supplier
        t_it_supplier = data.it_supplier
        t_ext_supplier = data.ext_supplier
        t_total_pages = data.total_pages

        t_skip = context.get('t_skip', None)
        if (data.actual_page == 1 and t_skip == -1):
            raise orm.except_orm(_('Error!'), _('Non puoi andare indietro!'))

        if (data.actual_page >= t_total_pages and t_skip == 1):
            raise orm.except_orm(_('Error!'),
                                 _("Hai raggiunto l'ultima pagina!"))

        t_page = data.actual_page + t_skip

        if (t_all_supplier):
            t_partner = None
        else:
            t_partner = data.partner_id.id

        context.update({
            'default_partner_id': t_partner,
            'default_journal_id': t_journal,
            'default_maturity': t_maturity,
            'default_bank_id': t_bank,
            'default_all_supplier': t_all_supplier,
            'default_it_supplier': t_it_supplier,
            'default_ext_supplier': t_ext_supplier,
            'default_actual_page': t_page,
            'default_total_pages': t_total_pages
        })

        res_id = wizard_line_obj.set_payment_lines(cr, uid, context)

        mod_obj = self.pool.get('ir.model.data')
        result = mod_obj.get_object_reference(
            cr, uid, 'account_voucher_makeover',
            'wizard_payment_specification_view')
        view_id = result and result[1] or False

        return {
            'name': _("Wizard Payment Specification"),
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'wizard.payment.specification',
            'type': 'ir.actions.act_window',
            'res_id': res_id,
            'view_id': view_id,
            'context': context,
            'target': 'inlineview',
        }
Example #49
0
        'description_html': fields.function(_get_desc, string='Description HTML', type='html', method=True, readonly=True),
        'author': fields.char("Author", size=128, readonly=True),
        'maintainer': fields.char('Maintainer', size=128, readonly=True),
        '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([
Example #50
0
class event_track(osv.osv):
    _name = "event.track"
    _description = 'Event Tracks'
    _order = 'priority, date'
    _inherit = ['mail.thread', 'ir.needaction_mixin', 'website.seo.metadata']

    def _website_url(self, cr, uid, ids, field_name, arg, context=None):
        res = dict.fromkeys(ids, '')
        for track in self.browse(cr, uid, ids, context=context):
            res[track.id] = "/event/%s/track/%s" % (slug(
                track.event_id), slug(track))
        return res

    _columns = {
        'name':
        fields.char('Track Title', required=True, translate=True),
        'user_id':
        fields.many2one('res.users', 'Responsible'),
        'speaker_ids':
        fields.many2many('res.partner', string='Speakers'),
        'tag_ids':
        fields.many2many('event.track.tag', string='Tags'),
        'stage_id':
        fields.many2one('event.track.stage', 'Stage'),
        'description':
        fields.html('Track Description', translate=True),
        'date':
        fields.datetime('Track Date'),
        'duration':
        fields.float('Duration', digits=(16, 2)),
        'location_id':
        fields.many2one('event.track.location', 'Location'),
        'event_id':
        fields.many2one('event.event', 'Event', required=True),
        'color':
        fields.integer('Color Index'),
        'priority':
        fields.selection([('3', 'Low'), ('2', 'Medium (*)'),
                          ('1', 'High (**)'), ('0', 'Highest (***)')],
                         'Priority',
                         required=True),
        'website_published':
        fields.boolean('Available in the website'),
        'website_url':
        fields.function(_website_url, string="Website url", type="char"),
        'image':
        fields.related('speaker_ids', 'image', type='binary', readonly=True)
    }

    def set_priority(self, cr, uid, ids, priority, context={}):
        return self.write(cr, uid, ids, {'priority': priority})

    def _default_stage_id(self, cr, uid, context={}):
        stage_obj = self.pool.get('event.track.stage')
        ids = stage_obj.search(cr, uid, [], context=context)
        return ids and ids[0] or False

    _defaults = {
        'user_id': lambda self, cr, uid, ctx: uid,
        'website_published': lambda self, cr, uid, ctx: False,
        'duration': lambda *args: 1.5,
        'stage_id': _default_stage_id,
        'priority': '2'
    }

    def _read_group_stage_ids(self,
                              cr,
                              uid,
                              ids,
                              domain,
                              read_group_order=None,
                              access_rights_uid=None,
                              context=None):
        stage_obj = self.pool.get('event.track.stage')
        result = stage_obj.name_search(cr, uid, '', context=context)
        return result, {}

    _group_by_full = {
        'stage_id': _read_group_stage_ids,
    }
    'use_invoice': fields.boolean('Invoices'),
    'use_purchase': fields.boolean('Purchases'),
    'use_picking': fields.boolean('Picking'),
    'date_start': fields.date(
        'from', help="Starting date for this rule to be valid."),
    'date_end': fields.date(
        'until', help="Ending date for this rule to be valid."),
    'vat_rule': fields.selection(
        [('with', 'With VAT number'),
         ('both', 'With or Without VAT number'),
         ('without', 'Without VAT number')], "VAT Rule",
        help=("Choose if the customer need to have the"
              " field VAT fill for using this fiscal position")),
    'sequence': fields.integer(
        'Priority',
        help='Unused, unless you use account replacement. Within a sequence, the rule with '
             'the highest sequence AND which has an account replacement defined, will apply '
             'across all fiscal domains will.'),

    # --- APPLICATION SECTION ---

    # These are "Tax Clusters" applied with some extra magic. They can contain one ore various (many2many) clusters
    # and typically require to be clustered around several (or one) Fiscal Domains.
    'fiscal_allocation_id': fields.many2many(
        'account.fiscal.allocation',
        'fiscal_allocation_rel',
        'rule_id', 'allocation_id',
        'Fiscal Allocation Sets',
        # TODO this probably might result in problems as templates do not have field company_id
        domain="[('company_id','=',company_id),('fiscal_domain_id','=',fiscal_domain_id)]", select=True),
    'account_invoice_id': fields.many2one('account.account', 'Account on Sales'),
Example #52
0
                    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',
    }

    _sql_constraints = [