class Membership(models.Model):
    _name = 'membership.membership'

    active = fields.Boolean(string="Active", default=True)
    name = fields.Char()
    customer_name = fields.Many2one('res.partner',
                                    string="Customer Name",
                                    required=True)
    product_id = fields.Many2one('product.product',
                                 string='Product',
                                 domain=[('sale_ok', '=', True),
                                         ('is_a_membership', '=', True)],
                                 required=True)
    tax_id = fields.Many2many('account.tax', string='Taxes')
    membership_plan_id = fields.Many2one('membership.plan',
                                         string='Membership Plan')
    trial_period = fields.Boolean(string='Trial period', default=False)
    trial_duration = fields.Integer(string="Trial Duration", required=True)
    trial_plan_unit = fields.Selection([('hour', 'Hour(s)'), ('day', 'Day(s)'),
                                        ('week', 'week(s)')],
                                       string='unit',
                                       required=True,
                                       default='hour')
    price = fields.Float(string="Price", required=True)
    start_date = fields.Date(string="Start Date",
                             readonly=True,
                             track_visibility="onchange")
    end_date = fields.Date(compute='get_end_date',
                           string="End Date",
                           track_visibility='onchange',
                           store=True)
    plan_state = fields.Selection([('draft', 'Draft'),
                                   ('in_progress', 'In-progress'),
                                   ('cancel', 'Cancelled'),
                                   ('close', 'Cancelled'),
                                   ('expired', 'Expired'),
                                   ('renewed', 'Renewed'),
                                   ('update', 'Updated'),
                                   ('pending', 'Pending')],
                                  default='draft',
                                  string='State',
                                  track_visibility="always",
                                  copy=False)
    auto_renewal = fields.Boolean(string='Auto Renewal', default=False)
    so_origin = fields.Many2one('sale.order', string="Order Ref")
    next_payment_date = fields.Datetime(string="Date of Next Payment",
                                        copy=False)
    invoice_ids = fields.Many2many("account.invoice",
                                   string='Invoices',
                                   readonly=True,
                                   copy=False)
    invoice_count = fields.Integer(compute="get_invoiced_count",
                                   readonly=True,
                                   string='Invoices')
    membership_id = fields.Many2one('membership.membership',
                                    string="Membership Id",
                                    copy=False)
    source = fields.Selection([('so', 'Sale Order'), ('manual', 'Manual')],
                              'Related To',
                              default="manual")
    so_origin = fields.Many2one('sale.order', string="Order Ref")
    quantity = fields.Float(string='Quantity', required=True, default=1.0)
    immediate = fields.Boolean(default=True)
    after = fields.Boolean(default=False)
    reason = fields.Char(string="Reason", track_visibility="onchange")
    currency_id = fields.Many2one(
        'res.currency',
        string='Currency',
        default=lambda self: self.env['website'].get_current_website(
        ).get_current_pricelist().currency_id.id)
    last_renewal_date = fields.Date(string="Last renewal Date", readonly=True)

    menu_ids = fields.Many2many('website.menu', string='membership menu')

    @api.multi
    def unlink(self):
        for current_rec in self:
            if current_rec.plan_state not in ('draft', 'cancel'):
                raise UserError(
                    "You can't delete the record because its invoice is create. Try closing it instead"
                )
            super(Membership, current_rec).unlink()
        return True

    @api.onchange('product_id')
    def plan_info(self):
        if self.product_id:
            self.membership_plan_id = self.product_id.membership_plan_id.id
            self.price = self.product_id.lst_price
            self.member_plan_id = self.product_id.membership_plan_id.id
            if self.product_id.membership_plan_id.trial_period:
                self.trial_period = self.product_id.membership_plan_id.trial_period
                self.trial_duration = self.product_id.membership_plan_id.trial_duration
                self.trial_plan_unit = self.product_id.membership_plan_id.trial_plan_unit
            else:
                self.trial_period = False
            if self.product_id.membership_plan_id.auto_renewal:
                self.auto_renewal = self.product_id.membership_plan_id.auto_renewal
            else:
                self.auto_renewal = False

        self.menu_ids = [(6, 0, self.product_id.menu_ids.ids)]

    def confirm_membership(self):
        self.update_membership()
        if (self.plan_state == 'draft'):
            if self.customer_name.all_membership:
                self.trial_period = False

            if self.trial_period:
                if self.trial_plan_unit == 'hour':
                    add_trial = datetime.timedelta(hours=self.trial_duration)
                elif self.trial_plan_unit == 'day':
                    add_trial = datetime.timedelta(days=self.trial_duration)
                elif self.trial_plan_unit == 'week':
                    add_trial = datetime.timedelta(weeks=self.trial_duration)
            else:
                add_trial = datetime.timedelta(days=0)

            self.start_date = (datetime.datetime.today() + add_trial).date()
            self.next_payment_date = str(self.start_date)

            self.plan_state = 'in_progress'
            self.mail_send()
            self.action_invoice_create()
            self.assigningpricelist()

    @api.one
    @api.depends('start_date')
    def get_end_date(self):
        if self.start_date:
            add_duration = datetime.timedelta(days=1)
            if self.membership_plan_id.plan_unit == 'day':
                add_duration = datetime.timedelta(
                    days=self.membership_plan_id.duration)
            elif self.membership_plan_id.plan_unit == 'month':
                add_duration = datetime.timedelta(
                    days=(self.membership_plan_id.duration) * 30)
            elif self.membership_plan_id.plan_unit == 'year':
                add_duration = datetime.timedelta(
                    days=(self.membership_plan_id.duration) * 365)

            start_date = datetime.datetime.strptime(str(self.start_date),
                                                    '%Y-%m-%d')

            self.end_date = (start_date + add_duration).date()

    @api.model
    def create(self, vals):
        vals['name'] = self.env['ir.sequence'].next_by_code(
            'membership.membership')
        res = super(Membership, self).create(vals)
        return res

    def mail_send(self):
        template_id = self.env.ref(
            'website_membership_management.membership_confirm_email')
        template_id.send_mail(self.id, force_send=True)

    def reminder_mail_send(self):
        template_id = self.env.ref(
            'website_membership_management.membership_reminder_email')
        template_id.send_mail(self.id, force_send=True)

    @api.multi
    def _prepare_invoice(self):
        """
        Prepare the dict of values to create the new invoice for a sales order. This method may be
        overridden to implement custom invoice generation (making sure to call super() to establish
        a clean extension chain).
        """
        self.ensure_one()
        journal_id = self.env['account.invoice'].default_get(['journal_id'
                                                              ])['journal_id']
        company_id = self.env['account.invoice'].default_get(['company_id'
                                                              ])['company_id']
        name = fiscal_position_id = pos_id = False
        acc = self.customer_name.property_account_receivable_id.id
        if not journal_id:
            raise UserError(
                _('Please define an accounting sale journal for this company.')
            )
        if self.source in ['api', 'manual']:
            fiscal_position_id = self.customer_name.property_account_position_id.id
        invoice_vals = {
            'name':
            self.so_origin.name or '' if self.source == 'so' else name,
            'origin':
            self.name,
            'type':
            'out_invoice',
            'reference':
            self.membership_plan_id.name or self.name,
            'account_id':
            acc,
            'partner_id':
            self.customer_name.id,
            'journal_id':
            journal_id,
            'currency_id':
            self.so_origin.pricelist_id.currency_id.id or self.currency_id.id
            or self.env['website'].get_current_website().get_current_pricelist(
            ).currency_id.id,
            'payment_term_id':
            self.so_origin.payment_term_id.id or '',
            'fiscal_position_id':
            self.so_origin.fiscal_position_id.id or
            self.so_origin.partner_invoice_id.property_account_position_id.id
            if self.source == 'so' else fiscal_position_id,
            'company_id':
            self.so_origin.company_id.id
            if self.source == 'so' else company_id,
            'user_id':
            self.so_origin.user_id and self.so_origin.user_id.id
            if self.source == 'so' else self._uid,
            'date_invoice':
            str(self.next_payment_date or self.start_date),
            'is_a_membership':
            True,
        }
        return invoice_vals

    @api.model
    def create_automatic_invoice(self):
        self.membership_state()
        memberships = self.search([('start_date', '<=', fields.Datetime.now()),
                                   ('plan_state', '=', 'in_progress'),
                                   ('auto_renewal', '=', True),
                                   ('next_payment_date', '<=',
                                    fields.Datetime.now())])
        for membership in memberships:
            membership.action_invoice_create()
            membership.last_renewal_date = fields.Datetime.now()
        return True

    @api.multi
    def action_invoice_create(self, grouped=False, final=False):
        inv_obj = self.env['account.invoice']
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')
        invoices = {}
        for membership in self:
            if not membership.active:
                raise UserError(
                    _("You can't generate invoice of an Inactive Membership."))
            if membership.plan_state == 'draft':
                raise UserError(
                    "You can't generate invoice of a membership which is in draft state, please confirm it first."
                )

            group_key = membership.id if grouped else (
                membership.customer_name.id,
                membership.product_id.currency_id.id)

            if group_key not in invoices:
                inv_data = membership._prepare_invoice()
                invoice = inv_obj.create(inv_data)
                invoices[group_key] = invoice
            elif group_key in invoices and membership.name not in invoices[
                    group_key].so_origin.split(', '):
                invoices[group_key].write({
                    'origin':
                    invoices[group_key].origin + ', ' + membership.name
                })
            if membership.quantity > 0:
                membership.invoice_line_create(invoices[group_key].id,
                                               membership.quantity)
        if invoices:
            message = 'Invoice Created'

            for inv in invoices.values():
                inv.compute_taxes()
                inv.action_invoice_open()
            self.invoice_ids = [inv.id for inv in invoices.values()]

            if self.next_payment_date:
                next_payment_date = datetime.datetime.strptime(
                    str(self.next_payment_date), '%Y-%m-%d %H:%M:%S')
            else:
                start_date = str(self.start_date + " 00:00:00")
                next_payment_date = datetime.datetime.strptime(
                    start_date, '%Y-%m-%d %H:%M:%S')
            if self.membership_plan_id.plan_unit == 'day':
                next_payment_date = next_payment_date + relativedelta(
                    days=self.membership_plan_id.duration)
            if self.membership_plan_id.plan_unit == 'month':
                next_payment_date = next_payment_date + relativedelta(
                    months=self.membership_plan_id.duration)
            if self.membership_plan_id.plan_unit == 'year':
                next_payment_date = next_payment_date + relativedelta(
                    years=self.membership_plan_id.duration)
            if self.membership_plan_id.plan_unit == 'hour':
                next_payment_date = next_payment_date + timedelta(
                    hours=self.membership_plan_id.duration)
            self.next_payment_date = str(next_payment_date) or False
            wizard_id = self.env['membership.message.wizard'].create(
                {'message': message})
        else:
            wizard_id = self.env['membership.message.wizard'].create(
                {'message': 'Membership Expired.'})
        return {
            'name': ("Message"),
            'view_mode': 'form',
            'view_id': False,
            'view_type': 'form',
            'res_model': 'membership.message.wizard',
            'res_id': wizard_id.id,
            'type': 'ir.actions.act_window',
            'nodestroy': True,
            'target': 'new',
        }

    @api.multi
    def _prepare_invoice_line(self, qty):
        self.ensure_one()
        if self.auto_renewal:
            product = self.product_id.with_context(
                lang=self.customer_name.lang,
                partner=self.customer_name.id,
                quantity=self.quantity,
                date=self.start_date,
                pricelist=self.membership_plan_id.membership_pricelist.id,
                uom=self.product_id.uom_po_id.id or False)
        else:
            product = self.product_id.with_context(
                lang=self.customer_name.lang,
                partner=self.customer_name.id,
                quantity=self.quantity,
                date=self.start_date,
                pricelist=self.so_origin.pricelist_id.id,
                uom=self.product_id.uom_po_id.id or False)

        name = product.name_get()[0][1]
        if product.description_sale:
            name += '\n' + product.description_sale
        res = {}
        account = self.product_id.property_account_income_id or self.product_id.categ_id.property_account_income_categ_id
        if not account:
            raise UserError(_('Please define income account for this product: "%s" (id:%d) - or for its category: "%s".') % \
                            (self.product_id.name, self.product_id.id, self.product_id.categ_id.name))
        fpos = self.customer_name.property_account_position_id
        if fpos:
            account = fpos.map_account(account)
        res = {
            'name': name,
            'origin': self.name,
            'account_id': account.id,
            'price_unit': self.price,
            'quantity': self.quantity,
            'product_id': self.product_id.id or False,
            'invoice_line_tax_ids': [(6, 0, self.tax_id.ids)],
            'pricelist': self.so_origin.pricelist_id,
        }
        return res

    @api.multi
    def invoice_line_create(self, invoice_id, qty):
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')
        for line in self:
            if not float_is_zero(qty, precision_digits=precision):
                vals = line._prepare_invoice_line(qty=qty)
                vals.update({'invoice_id': invoice_id})
                self.env['account.invoice.line'].create(vals)

    @api.multi
    def action_view_invoice(self):
        invoice_ids = self.mapped('invoice_ids')
        imd = self.env['ir.model.data']
        action = imd.xmlid_to_object('account.action_invoice_tree1')
        list_view_id = imd.xmlid_to_res_id('account.invoice_tree')
        form_view_id = imd.xmlid_to_res_id('account.invoice_form')

        result = {
            'name':
            action.name,
            'help':
            action.help,
            'type':
            action.type,
            'views': [[list_view_id, 'tree'], [form_view_id, 'form'],
                      [False, 'graph'], [False, 'kanban'], [False, 'calendar'],
                      [False, 'pivot']],
            'target':
            action.target,
            'context':
            action.context,
            'res_model':
            action.res_model,
        }
        if len(invoice_ids) > 1:
            result['domain'] = "[('id','in',%s)]" % invoice_ids.ids
        elif len(invoice_ids) == 1:
            result['views'] = [(form_view_id, 'form')]
            result['res_id'] = invoice_ids.ids[0]
        else:
            result = {'type': 'ir.actions.act_window_close'}
        return result

    @api.depends('invoice_ids')
    def get_invoiced_count(self):
        for rec in self:
            rec.invoice_count = len(rec.invoice_ids)

    @api.model
    def membership_state(self):
        for cur_rec in self.search([]):
            if cur_rec.plan_state != 'draft' and cur_rec.end_date and not cur_rec.auto_renewal:
                if cur_rec.plan_state == 'in_progress' and str(
                        datetime.datetime.now().date()) >= str(
                            cur_rec.end_date):
                    cur_rec.plan_state = 'expired'
                    cur_rec.revokingpricelist()
                if not (self.env['membership.membership'].search(
                    [('customer_name.name', '=', cur_rec.customer_name.name),
                     ('plan_state', '=', 'in_progress')])):
                    pending_membership = self.env[
                        'membership.membership'].search([
                            ('customer_name.name', '=',
                             cur_rec.customer_name.name),
                            ('plan_state', '=', 'pending')
                        ])
                    if pending_membership:
                        pending_membership.plan_state = 'draft'
                        pending_membership.confirm_membership()
        self.send_reminder_mail()

    @api.multi
    def membership_renew(self):
        for current_rec in self:
            if current_rec.plan_state in ['expired', 'close', 'cancel']:
                current_rec.create_membership()
                current_rec.plan_state = 'renewed'
                wizard_id = self.env['membership.message.wizard'].create(
                    {'message': 'Membership Renewed.'})
                return {
                    'name': ("Message"),
                    'view_mode': 'form',
                    'view_id': False,
                    'view_type': 'form',
                    'res_model': 'membership.message.wizard',
                    'res_id': wizard_id.id,
                    'type': 'ir.actions.act_window',
                    'nodestroy': True,
                    'target': 'new',
                }
            return False

    @api.multi
    def create_membership(self):
        date = datetime.datetime.today()
        if self.trial_period:
            if self.trial_plan_unit == 'day':
                date = date + relativedelta(days=self.trial_duration)
            if self.trial_plan_unit == 'month':
                date = date + relativedelta(months=self.trial_duration)
            if self.trial_plan_unit == 'year':
                date = date + relativedelta(years=self.trial_duration)
            if self.trial_plan_unit == 'hour':
                date = date + relativedelta(hours=self.trial_duration)
        res = self.copy()
        res.start_date = str(date)
        res.membership_id = self.id
        self.state = "renewed"
        return res

    def assigningpricelist(self):
        self.customer_name.property_product_pricelist = self.membership_plan_id.membership_pricelist.id

    def revokingpricelist(self):
        pricelist = self.env['product.pricelist'].search([
            ('name', '=', 'Public Pricelist')
        ])
        self.customer_name.property_product_pricelist = pricelist.id

    def update_membership(self):
        count = self.env['membership.membership'].search([
            ('customer_name.name', '=', self.customer_name.name),
            ('plan_state', '=', 'in_progress')
        ])
        if count:
            if (count.immediate):
                count.reset_to_close()
                count.plan_state = 'update'
            elif count.after:
                self.plan_state = 'pending'
        else:
            return True

    def send_reminder_mail(self):
        for cur_rec in self.search([]):
            if cur_rec.plan_state != 'draft' and cur_rec.end_date:
                advanced_date = (datetime.datetime.now() +
                                 datetime.timedelta(days=10)).date()
                if (advanced_date >= cur_rec.end_date
                        and cur_rec.plan_state == 'in_progress'):
                    cur_rec.reminder_mail_send()

    @api.multi
    def get_cancel_mem(self):
        for current_rec in self:
            if current_rec.plan_state == 'draft':
                current_rec.plan_state = 'cancel'

        return True

    @api.multi
    def pay_cancel_invoice(self):
        for current_rec in self:
            for invoice_id in current_rec.invoice_ids:
                if invoice_id.state == 'draft':
                    res = invoice_id.action_cancel()
                elif invoice_id.state == 'open':
                    journal_id = self.env["ir.default"].get(
                        'res.config.settings', 'journal_id')
                    if not journal_id:
                        raise UserError(
                            _("Default Journal not found please the default journal in configuration under membership"
                              ))
                    journal = self.env['account.journal'].browse(journal_id)
                    res = invoice_id.pay_and_reconcile(pay_journal=journal)
        return True

    @api.multi
    def reset_to_close(self):
        for current_rec in self:
            if current_rec.plan_state not in [
                    'close', 'cancel', 'renewed', 'update'
            ]:
                if current_rec.invoice_ids:
                    self.pay_cancel_invoice()
                    current_rec.revokingpricelist()
                current_rec.plan_state = 'close'
            if self._context.get('close_refund'):
                return current_rec.action_view_invoice()
        return True
Exemple #2
0
class academia_student(models.Model):
	_inherit = ['portal.mixin', 'mail.thread', 'mail.activity.mixin']
	_name = "academia.student"
	_description = "Modelo para formulacion de estudiantes"

	@api.depends('calificaciones_id')
	def calcula_promedio(self):
		acum = 0.0
		for xcal in self.calificaciones_id:
			acum+= xcal.calificacion
		if acum:
			promedio = acum/len(self.calificaciones_id)
			self.promedio = promedio

	@api.depends('invoice_ids')
	def calcula_amount(self):
		acum = 0.0
		for xcal in self.invoice_ids:
			acum+= xcal.amount_total
		if acum:
			self.amount_invoice = acum

	@api.model
	def _get_school_default(self):
		school_id = self.env['res.partner'].search([('name','=','Escuela Comodin')])
		return school_id

	name = fields.Char('Nombre', size = 128, required = True, track_visibility = 'onchange')
	last_name = fields.Char('Apellido', size = 128)
	photo = fields.Binary('Fotografia')
	create_date = fields.Datetime('Fecha de creacion', readonly=True)
	note = fields.Html('Comentarios')
	active = fields.Boolean('Activo', default=True)
	age = fields.Integer ('Edad', copy=False)
	curp = fields.Char('curp', size=18, copy=False)
	state = fields.Selection([	
		('draft','Documento borrador'),
		('process','Proceso'),
		('done', 'Egresado'),
		('cancel', 'Expulsado')],'Estado', default="draft")
	##Relacionales
	partner_id = fields.Many2one('res.partner', 'Escuela', default=_get_school_default, copy=False)
	country = fields.Many2one('res.country', 'Pais', related='partner_id.country_id')
	calificaciones_id = fields.One2many(
		'academia.calificacion',
		'student_id',
		'Calificaciones')

	invoice_ids = fields.Many2many('account.invoice',
		'student_invoice_rel',
		'student_id', 'invoice_id',
		'Facturas')
	
	grado_id = fields.Many2one('academia.grado', 'Grado')

	promedio = fields.Float('Promedio', digits=(14,2), compute="calcula_promedio")

	amount_invoice = fields.Float('Monto Facturado', digits=(14,2), compute="calcula_amount", store=True)

	@api.onchange('grado_id')
	def onchange_grado(self):
		calificaciones_list = []
		for materia in self.grado_id.materia_ids:
			xval = (0,0,{
				'name': materia.materia_id.id,
				'calificacion': 5
				})
			calificaciones_list.append(xval)
		self.update({'calificaciones_id':calificaciones_list})

	@api.one
	@api.constrains('curp')
	def _check_lines(self):
		if len(self.curp) < 18:
			raise exceptions.ValidationError("Curp debe ser de 18 caracteres.")

	@api.model
	def create(self,values):
		if values['name']:
			nombre = values['name']
			exist_ids = self.env['academia.student'].search([('name', '=', self.name)])
			if exist_ids:
				values.update({
					'name': values['name'] + "(copia)",
					})
		res = super(academia_student, self).create(values)
		partner_obj = self.env['res.partner']
		vals_to_partner = {
			'name': res['name']+" " + res['last_name'],
			'company_type': 'student_id',
			'student_id': res['id'],
		}
		print (vals_to_partner)
		partner_id = partner_obj.create(vals_to_partner)
		print("===>partner_id", partner_id)
		return res
	
	_order = "name"	

	_default = 	{
				'active' : True,
				}

	@api.multi
	def done(self):
		self.state = 'done'
		return True

	@api.multi
	def confirm(self):
		self.state = 'process'
		return True

	@api.multi
	def cancel(self):
		self.state = 'cancel'
		return True

	@api.multi
	def draft(self):
		self.state = 'draft'
		return True
Exemple #3
0
class MrpWorkcenterProductivity(models.Model):
    _name = "mrp.workcenter.productivity"
    _description = "Workcenter Productivity Log"
    _order = "id desc"
    _rec_name = "loss_id"
    _check_company_auto = True

    def _get_default_company_id(self):
        company_id = False
        if self.env.context.get('default_company_id'):
            company_id = self.env.context['default_company_id']
        if not company_id and self.env.context.get('default_workorder_id'):
            workorder = self.env['mrp.workorder'].browse(
                self.env.context['default_workorder_id'])
            company_id = workorder.company_id
        if not company_id and self.env.context.get('default_workcenter_id'):
            workcenter = self.env['mrp.workcenter'].browse(
                self.env.context['default_workcenter_id'])
            company_id = workcenter.company_id
        if not company_id:
            company_id = self.env.company
        return company_id

    production_id = fields.Many2one('mrp.production',
                                    string='Manufacturing Order',
                                    related='workorder_id.production_id',
                                    readonly='True')
    workcenter_id = fields.Many2one('mrp.workcenter',
                                    "Work Center",
                                    required=True,
                                    check_company=True)
    company_id = fields.Many2one(
        'res.company',
        required=True,
        index=True,
        default=lambda self: self._get_default_company_id())
    workorder_id = fields.Many2one('mrp.workorder',
                                   'Work Order',
                                   check_company=True)
    user_id = fields.Many2one('res.users',
                              "User",
                              default=lambda self: self.env.uid)
    loss_id = fields.Many2one('mrp.workcenter.productivity.loss',
                              "Loss Reason",
                              ondelete='restrict',
                              required=True)
    loss_type = fields.Selection(string="Effectiveness",
                                 related='loss_id.loss_type',
                                 store=True,
                                 readonly=False)
    description = fields.Text('Description')
    date_start = fields.Datetime('Start Date',
                                 default=fields.Datetime.now,
                                 required=True)
    date_end = fields.Datetime('End Date')
    duration = fields.Float('Duration',
                            compute='_compute_duration',
                            store=True)

    @api.depends('date_end', 'date_start')
    def _compute_duration(self):
        for blocktime in self:
            if blocktime.date_start and blocktime.date_end:
                d1 = fields.Datetime.from_string(blocktime.date_start)
                d2 = fields.Datetime.from_string(blocktime.date_end)
                diff = d2 - d1
                if (blocktime.loss_type not in ('productive', 'performance')
                    ) and blocktime.workcenter_id.resource_calendar_id:
                    r = blocktime.workcenter_id._get_work_days_data_batch(
                        d1, d2)[blocktime.workcenter_id.id]['hours']
                    blocktime.duration = round(r * 60, 2)
                else:
                    blocktime.duration = round(diff.total_seconds() / 60.0, 2)
            else:
                blocktime.duration = 0.0

    def button_block(self):
        self.ensure_one()
        self.workcenter_id.order_ids.end_all()
class PurchaseRequisition(models.Model):
    _name = "purchase.requisition"
    _description = "Purchase Requisition"
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _order = "id desc"

    def _get_type_id(self):
        return self.env['purchase.requisition.type'].search([], limit=1)

    name = fields.Char(string='Agreement Reference', required=True, copy=False, default='New', readonly=True)
    origin = fields.Char(string='Source Document')
    order_count = fields.Integer(compute='_compute_orders_number', string='Number of Orders')
    vendor_id = fields.Many2one('res.partner', string="Vendor", domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    type_id = fields.Many2one('purchase.requisition.type', string="Agreement Type", required=True, default=_get_type_id)
    ordering_date = fields.Date(string="Ordering Date", tracking=True)
    date_end = fields.Datetime(string='Agreement Deadline', tracking=True)
    schedule_date = fields.Date(string='Delivery Date', index=True, help="The expected and scheduled delivery date where all the products are received", tracking=True)
    user_id = fields.Many2one('res.users', string='Purchase Representative', default= lambda self: self.env.user)
    description = fields.Text()
    company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.company)
    purchase_ids = fields.One2many('purchase.order', 'requisition_id', string='Purchase Orders', states={'done': [('readonly', True)]})
    line_ids = fields.One2many('purchase.requisition.line', 'requisition_id', string='Products to Purchase', states={'done': [('readonly', True)]}, copy=True)
    state = fields.Selection(PURCHASE_REQUISITION_STATES,
                              'Status', tracking=True, required=True,
                              copy=False, default='draft')
    state_blanket_order = fields.Selection(PURCHASE_REQUISITION_STATES, compute='_set_state')
    is_quantity_copy = fields.Selection(related='type_id.quantity_copy', readonly=True)
    currency_id = fields.Many2one('res.currency', 'Currency', required=True,
        default=lambda self: self.env.company.currency_id.id)

    @api.depends('state')
    def _set_state(self):
        self.state_blanket_order = self.state

    @api.onchange('vendor_id')
    def _onchange_vendor(self):
        requisitions = self.env['purchase.requisition'].search([
            ('vendor_id', '=', self.vendor_id.id),
            ('state', '=', 'ongoing'),
            ('type_id.quantity_copy', '=', 'none'),
        ])
        if any(requisitions):
            title = _("Warning for %s") % self.vendor_id.name
            message = _("There is already an open blanket order for this supplier. We suggest you to use to complete this open blanket order instead of creating a new one.")
            warning = {
                'title': title,
                'message': message
            }
            return {'warning': warning}

    @api.depends('purchase_ids')
    def _compute_orders_number(self):
        for requisition in self:
            requisition.order_count = len(requisition.purchase_ids)

    def action_cancel(self):
        # try to set all associated quotations to cancel state
        for requisition in self:
            for requisition_line in requisition.line_ids:
                requisition_line.supplier_info_ids.unlink()
            requisition.purchase_ids.button_cancel()
            for po in requisition.purchase_ids:
                po.message_post(body=_('Cancelled by the agreement associated to this quotation.'))
        self.write({'state': 'cancel'})

    def action_in_progress(self):
        self.ensure_one()
        if not all(obj.line_ids for obj in self):
            raise UserError(_("You cannot confirm agreement '%s' because there is no product line.") % self.name)
        if self.type_id.quantity_copy == 'none' and self.vendor_id:
            for requisition_line in self.line_ids:
                if requisition_line.price_unit <= 0.0:
                    raise UserError(_('You cannot confirm the blanket order without price.'))
                if requisition_line.product_qty <= 0.0:
                    raise UserError(_('You cannot confirm the blanket order without quantity.'))
                requisition_line.create_supplier_info()
            self.write({'state': 'ongoing'})
        else:
            self.write({'state': 'in_progress'})
        # Set the sequence number regarding the requisition type
        if self.name == 'New':
            if self.is_quantity_copy != 'none':
                self.name = self.env['ir.sequence'].next_by_code('purchase.requisition.purchase.tender')
            else:
                self.name = self.env['ir.sequence'].next_by_code('purchase.requisition.blanket.order')

    def action_open(self):
        self.write({'state': 'open'})

    def action_draft(self):
        self.ensure_one()
        self.name = 'New'
        self.write({'state': 'draft'})

    def action_done(self):
        """
        Generate all purchase order based on selected lines, should only be called on one agreement at a time
        """
        if any(purchase_order.state in ['draft', 'sent', 'to approve'] for purchase_order in self.mapped('purchase_ids')):
            raise UserError(_('You have to cancel or validate every RfQ before closing the purchase requisition.'))
        for requisition in self:
            for requisition_line in requisition.line_ids:
                requisition_line.supplier_info_ids.unlink()
        self.write({'state': 'done'})

    def _prepare_tender_values(self, product_id, product_qty, product_uom, location_id, name, origin, company_id, values):
        return{
            'origin': origin,
            'date_end': values['date_planned'],
            'user_id': False,
            'warehouse_id': values.get('warehouse_id') and values['warehouse_id'].id or False,
            'company_id': company_id.id,
            'line_ids': [(0, 0, {
                'product_id': product_id.id,
                'product_uom_id': product_uom.id,
                'product_qty': product_qty,
            })],
        }

    def unlink(self):
        if any(requisition.state not in ('draft', 'cancel') for requisition in self):
            raise UserError(_('You can only delete draft requisitions.'))
        # Draft requisitions could have some requisition lines.
        self.mapped('line_ids').unlink()
        return super(PurchaseRequisition, self).unlink()
class OCD(models.Model):

    _name = 'calendar.ocd'
    _description = 'Create an OCD'

    principal = fields.Boolean(_('Principal Task'))
    name = fields.Char(_('Name'), required=True)
    acronym = fields.Char(_('Acronym'), required=True)
    job_ids = fields.Many2many('hr.job', string=_('Members'), required=True)
    start_datetime = fields.Datetime(_('Start DateTime'), required=True)
    stop_datetime = fields.Datetime(_('End Datetime'), required=True)
    location = fields.Char(_('Location'))
    ocd_flag = fields.Integer('Create ocd in event')

    # RECURRENCE FIELD
    rrule = fields.Char('Recurrent Rule',
                        compute='_compute_rrule',
                        inverse='_inverse_rrule',
                        store=True)
    rrule_type = fields.Selection(
        [('daily', 'Day(s)'), ('weekly', 'Week(s)'), ('monthly', 'Month(s)'),
         ('yearly', 'Year(s)')],
        string='Recurrence',
        help="Let the event automatically repeat at that interval")
    recurrency = fields.Boolean('Recurrent', help="Recurrent Meeting")
    recurrent_id = fields.Integer('Recurrent ID')
    recurrent_id_date = fields.Datetime('Recurrent ID date')
    end_type = fields.Selection([('count', 'Number of repetitions'),
                                 ('end_date', 'End date')],
                                string='Recurrence Termination',
                                default='count')
    interval = fields.Integer(string='Repeat Every',
                              default=1,
                              help="Repeat every (Days/Week/Month/Year)")
    count = fields.Integer(string='Repeat', help="Repeat x times", default=1)
    mo = fields.Boolean('Mon')
    tu = fields.Boolean('Tue')
    we = fields.Boolean('Wed')
    th = fields.Boolean('Thu')
    fr = fields.Boolean('Fri')
    sa = fields.Boolean('Sat')
    su = fields.Boolean('Sun')
    month_by = fields.Selection([('date', 'Date of month'),
                                 ('day', 'Day of month')],
                                string='Option',
                                default='date',
                                oldname='select1')
    day = fields.Integer('Date of month', default=1)
    week_list = fields.Selection([('MO', 'Monday'), ('TU', 'Tuesday'),
                                  ('WE', 'Wednesday'), ('TH', 'Thursday'),
                                  ('FR', 'Friday'), ('SA', 'Saturday'),
                                  ('SU', 'Sunday')],
                                 string='Weekday')
    byday = fields.Selection([('1', 'First'), ('2', 'Second'), ('3', 'Third'),
                              ('4', 'Fourth'), ('5', 'Fifth'), ('-1', 'Last')],
                             string='By day')
    final_date = fields.Date('Repeat Until')

    @api.depends('byday', 'recurrency', 'final_date', 'rrule_type', 'month_by',
                 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr',
                 'sa', 'su', 'day', 'week_list')
    def _compute_rrule(self):
        """ Gets Recurrence rule string according to value type RECUR of iCalendar from the values given.
            :return dictionary of rrule value.
        """
        for meeting in self:
            if meeting.recurrency:
                meeting.rrule = meeting._rrule_serialize()
            else:
                meeting.rrule = ''

    @api.multi
    def _inverse_rrule(self):
        for meeting in self:
            if meeting.rrule:
                data = self._rrule_default_values()
                data['recurrency'] = True
                data.update(
                    self._rrule_parse(meeting.rrule, data, meeting.start))
                meeting.update(data)

    @api.constrains('start_datetime', 'stop_datetime')
    def _check_closing_date(self):
        if self.start_datetime and self.stop_datetime and self.stop_datetime < self.start_datetime:
            raise ValidationError(
                _('Ending datetime cannot be set before starting datetime.'))

    def _rrule_default_values(self):
        return {
            'byday': False,
            'recurrency': False,
            'final_date': False,
            'rrule_type': False,
            'month_by': False,
            'interval': 0,
            'count': False,
            'end_type': False,
            'mo': False,
            'tu': False,
            'we': False,
            'th': False,
            'fr': False,
            'sa': False,
            'su': False,
            'day': False,
            'week_list': False
        }

    def _rrule_parse(self, rule_str, data, date_start):
        day_list = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
        rrule_type = ['yearly', 'monthly', 'weekly', 'daily']
        ddate = fields.Datetime.from_string(date_start)
        if 'Z' in rule_str and not ddate.tzinfo:
            ddate = ddate.replace(tzinfo=pytz.timezone('UTC'))
            rule = rrule.rrulestr(rule_str, dtstart=ddate)
        else:
            rule = rrule.rrulestr(rule_str, dtstart=ddate)

        if rule._freq > 0 and rule._freq < 4:
            data['rrule_type'] = rrule_type[rule._freq]
        data['count'] = rule._count
        data['interval'] = rule._interval
        data['final_date'] = rule._until and rule._until.strftime(
            DEFAULT_SERVER_DATETIME_FORMAT)
        #repeat weekly
        if rule._byweekday:
            for i in range(0, 7):
                if i in rule._byweekday:
                    data[day_list[i]] = True
            data['rrule_type'] = 'weekly'
        #repeat monthly by nweekday ((weekday, weeknumber), )
        if rule._bynweekday:
            data['week_list'] = day_list[list(rule._bynweekday)[0][0]].upper()
            data['byday'] = str(list(rule._bynweekday)[0][1])
            data['month_by'] = 'day'
            data['rrule_type'] = 'monthly'

        if rule._bymonthday:
            data['day'] = list(rule._bymonthday)[0]
            data['month_by'] = 'date'
            data['rrule_type'] = 'monthly'

        #repeat yearly but for odoo it's monthly, take same information as monthly but interval is 12 times
        if rule._bymonth:
            data['interval'] = data['interval'] * 12

        #FIXEME handle forever case
        #end of recurrence
        #in case of repeat for ever that we do not support right now
        if not (data.get('count') or data.get('final_date')):
            data['count'] = 100
        if data.get('count'):
            data['end_type'] = 'count'
        else:
            data['end_type'] = 'end_date'
        return data

    @api.multi
    def _rrule_serialize(self):
        """ Compute rule string according to value type RECUR of iCalendar
            :return: string containing recurring rule (empty if no rule)
        """
        if self.interval and self.interval < 0:
            raise UserError(_('interval cannot be negative.'))
        if self.count and self.count <= 0:
            raise UserError(_('Event recurrence interval cannot be negative.'))

        def get_week_string(freq):
            weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
            if freq == 'weekly':
                byday = [field.upper() for field in weekdays if self[field]]
                if byday:
                    return ';BYDAY=' + ','.join(byday)
            return ''

    def set_events_to_ocd(self):

        partner_ocd_ids = self.env['hr.employee'].search([
            ('job_id', 'in', self.job_ids.ids)
        ]).mapped('user_id.partner_id.id')
        lines1 = [(4, id, 0) for id in partner_ocd_ids]
        vals = {
            'principal': self.principal,
            'name': self.acronym,
            'start': self.start_datetime,
            'stop': self.stop_datetime,
            'location': self.location,
            'byday': self.byday,
            'recurrency': self.recurrency,
            'final_date': self.final_date,
            'rrule_type': self.rrule_type,
            'month_by': self.month_by,
            'interval': self.interval,
            'count': self.count,
            'end_type': self.end_type,
            'mo': self.mo,
            'tu': self.tu,
            'we': self.we,
            'th': self.we,
            'fr': self.fr,
            'sa': self.sa,
            'su': self.su,
            'day': self.day,
            'week_list': self.week_list,
            'partner_ids': lines1,
            'ocd': self.id,
        }
        self.env['calendar.event'].create(vals)
        self.ocd_flag = self.env['calendar.event'].search_count([('ocd', '=',
                                                                  self.id)])

    @api.multi
    def write(self, vals):
        ocd_write = super(OCD, self).write(vals)

        ocd_event = self.env['calendar.event'].search([('ocd', '=', self.id)])

        partner_ocd_ids = self.env['hr.employee'].search([
            ('job_id', 'in', self.job_ids.ids)
        ]).mapped('user_id.partner_id.id')
        _logger.info(partner_ocd_ids)

        lines1 = [(6, 0, partner_ocd_ids)]
        val = {
            'principal': self.principal,
            'name': self.acronym,
            'start': self.start_datetime,
            'stop': self.stop_datetime,
            'location': self.location,
            'byday': self.byday,
            'recurrency': self.recurrency,
            'final_date': self.final_date,
            'rrule_type': self.rrule_type,
            'month_by': self.month_by,
            'interval': self.interval,
            'count': self.count,
            'end_type': self.end_type,
            'mo': self.mo,
            'tu': self.tu,
            'we': self.we,
            'th': self.we,
            'fr': self.fr,
            'sa': self.sa,
            'su': self.su,
            'day': self.day,
            'week_list': self.week_list,
            'partner_ids': lines1,
        }

        ocd_event.write(val)

        return True

    @api.multi
    def unlink(self):
        ocd_event = self.env['calendar.event'].search([('ocd', '=', self.id)])
        ocd_event.unlink()
        res = super(OCD, self).unlink()

        return True
Exemple #6
0
class SaleEnquiry(models.Model):
    _name = "sale.enquiry"
    _description = "Sale Enquiry"

    @api.multi
    def get_manager_access(self):
        if self.env.user.has_group('sales_team.group_sale_manager'):
            self.is_manager = True

    @api.multi
    def get_all_approved(self):
        line_count = 0
        approve_count = 0
        for line in self.enquiry_line:
            line_count += 1
            if line.approved:
                approve_count += 1
        if line_count == approve_count:
            self.all_approved = True
        else:
            self.partially_approved = True
            if approve_count:
                self.state = 'partial'

    @api.depends('enquiry_line')
    def _get_order_ids(self):
        for enquiry in self:
            order_ids = enquiry.enquiry_line.mapped('sale_line_id').mapped(
                'order_id')
            enquiry.update({
                'order_count': len(set(order_ids.ids)),
                'order_ids': order_ids.ids
            })

    name = fields.Char(string='Enquiry Reference',
                       required=True,
                       copy=False,
                       readonly=True,
                       states={'draft': [('readonly', False)]},
                       index=True,
                       default=lambda self: _('New'))
    client_order_ref = fields.Char(string='Customer Reference', copy=False)
    state = fields.Selection([
        ('draft', 'Enquiry'),
        ('sent', 'Sent For Approval'),
        ('waiting', 'Waiting'),
        ('partial', 'Partially Approved'),
        ('approved', 'Approved'),
        ('quotation', 'Quotation'),
        ('done', 'done'),
        ('cancel', 'Cancelled'),
    ],
                             string='Status',
                             readonly=True,
                             copy=False,
                             index=True,
                             track_visibility='onchange',
                             track_sequence=3,
                             default='draft')
    date_enquiry = fields.Datetime(string='Enquiry Date',
                                   readonly=True,
                                   index=True,
                                   states={
                                       'draft': [('readonly', False)],
                                       'sent': [('readonly', False)]
                                   },
                                   copy=False,
                                   default=fields.Datetime.now)

    enquiry_line = fields.One2many('sale.enquiry.line',
                                   'enquiry_id',
                                   string='Order Lines',
                                   states={
                                       'cancel': [('readonly', True)],
                                       'done': [('readonly', True)]
                                   },
                                   copy=True,
                                   auto_join=True)
    order_count = fields.Integer(string='Order Count',
                                 compute='_get_order_ids',
                                 readonly=True)
    order_ids = fields.Many2many("sale.order",
                                 string='Quotations',
                                 compute="_get_order_ids",
                                 readonly=True,
                                 copy=False)
    partner_id = fields.Many2one(
        'res.partner',
        string='Customer',
        readonly=True,
        states={
            'draft': [('readonly', False)],
            'sent': [('readonly', False)]
        },
        required=True,
        change_default=True,
        index=True,
        track_visibility='always',
        track_sequence=1,
        help=
        "You can find a customer by its Name, TIN, Email or Internal Reference."
    )

    all_approved = fields.Boolean('All approved',
                                  default=False,
                                  compute='get_all_approved')
    partially_approved = fields.Boolean('Partially Approved',
                                        default=False,
                                        compute='get_all_approved')
    is_manager = fields.Boolean('manager',
                                compute='get_manager_access',
                                default=False)

    @api.multi
    def action_create_quotation(self):

        sale_order = self.env['sale.order'].create({
            'partner_id':
            self.partner_id.id,
            'enquiry_id':
            self.id,
            'payment_type':
            'lc'
        })
        print(self._context)
        if sale_order:
            sale_line_obj = self.env['sale.order.line']
            for line in self.enquiry_line:
                if line.state in ('available', 'approved'):
                    so_line = sale_line_obj.create({
                        'price_unit':
                        line.price_unit,
                        'product_uom_qty':
                        line.product_uom_qty,
                        'order_id':
                        sale_order.id,
                        'discount':
                        0.0,
                        'product_id':
                        line.product_id.id,
                    })
                    line.sale_line_id = so_line.id
        self.state = 'quotation'
        if self._context.get('open_quotation', False):
            return self.action_view_order()
        return {'type': 'ir.actions.act_window_close'}

    @api.multi
    def action_view_order(self):
        order_ids = self.mapped('order_ids')
        action = {
            'name': _('Quotation'),
            'domain': [('id', '=', order_ids.ids)],
            'view_type': 'form',
            'res_model': 'sale.order',
            'view_id': False,
            'view_mode': 'tree,form',
            'type': 'ir.actions.act_window',
        }
        return action

    @api.onchange('all_approved')
    def onchange_all_approved(self):
        if self.all_approved:
            self.state = 'approved'

    @api.multi
    def action_cancel(self):
        self.state = 'cancel'

    @api.multi
    def action_approve_all_prices(self):
        for line in self.enquiry_line:
            line.state = 'approved'
            line.approved = True
        self.all_approved = True
        self.state = 'approved'

    @api.model
    def create(self, vals):
        if vals.get('name', _('New')) == _('New'):
            vals['name'] = self.env['ir.sequence'].next_by_code(
                'sale.enquiry') or _('New')
        enquiry_line = vals.get('enquiry_line')
        for line in enquiry_line:
            if line[2].get('product_id') and line[2].get('price_unit'):
                product_id = self.env['product.product'].browse(
                    line[2].get('product_id'))
                if product_id:
                    if line[2].get('price_unit') <= product_id.lst_price:
                        line[2].update({
                            'state': 'available',
                            'approved': True
                        })
                    else:
                        line[2].update({'state': 'draft'})
        vals.update({'enquiry_line': enquiry_line})
        result = super(SaleEnquiry, self).create(vals)
        return result

    @api.multi
    def action_send_for_clarification(self):
        self.state = 'sent'

    @api.multi
    def action_approve(self):
        self.state = 'approved'
class StockRequest(models.Model):
    _name = "stock.request"
    _description = "Stock Request"
    _inherit = 'stock.request.abstract'

    def _get_default_requested_by(self):
        return self.env['res.users'].browse(self.env.uid)

    name = fields.Char(states={'draft': [('readonly', False)]})
    state = fields.Selection(
        selection=REQUEST_STATES,
        string='Status',
        copy=False,
        default='draft',
        index=True,
        readonly=True,
        track_visibility='onchange',
    )
    requested_by = fields.Many2one(
        'res.users',
        'Requested by',
        required=True,
        track_visibility='onchange',
        default=lambda s: s._get_default_requested_by(),
    )
    expected_date = fields.Datetime(
        'Expected Date',
        default=fields.Datetime.now,
        index=True,
        required=True,
        readonly=True,
        states={'draft': [('readonly', False)]},
        help="Date when you expect to receive the goods.",
    )
    picking_policy = fields.Selection(
        [('direct', 'Receive each product when available'),
         ('one', 'Receive all products at once')],
        string='Shipping Policy',
        required=True,
        readonly=True,
        states={'draft': [('readonly', False)]},
        default='direct',
    )
    move_ids = fields.One2many(
        comodel_name='stock.move',
        compute='_compute_move_ids',
        string='Stock Moves',
        readonly=True,
    )
    picking_ids = fields.One2many(
        'stock.picking',
        compute='_compute_picking_ids',
        string='Pickings',
        readonly=True,
    )
    qty_in_progress = fields.Float(
        'Qty In Progress',
        digits=dp.get_precision('Product Unit of Measure'),
        readonly=True,
        compute='_compute_qty',
        store=True,
        help="Quantity in progress.",
    )
    qty_done = fields.Float(
        'Qty Done',
        digits=dp.get_precision('Product Unit of Measure'),
        readonly=True,
        compute='_compute_qty',
        store=True,
        help="Quantity completed",
    )
    picking_count = fields.Integer(
        string='Delivery Orders',
        compute='_compute_picking_ids',
        readonly=True,
    )
    allocation_ids = fields.One2many(comodel_name='stock.request.allocation',
                                     inverse_name='stock_request_id',
                                     string='Stock Request Allocation')
    order_id = fields.Many2one(
        'stock.request.order',
        readonly=True,
    )
    warehouse_id = fields.Many2one(states={'draft': [('readonly', False)]},
                                   readonly=True)
    location_id = fields.Many2one(states={'draft': [('readonly', False)]},
                                  readonly=True)
    product_id = fields.Many2one(states={'draft': [('readonly', False)]},
                                 readonly=True)
    product_uom_id = fields.Many2one(states={'draft': [('readonly', False)]},
                                     readonly=True)
    product_uom_qty = fields.Float(states={'draft': [('readonly', False)]},
                                   readonly=True)
    procurement_group_id = fields.Many2one(
        states={'draft': [('readonly', False)]}, readonly=True)
    company_id = fields.Many2one(states={'draft': [('readonly', False)]},
                                 readonly=True)
    route_id = fields.Many2one(states={'draft': [('readonly', False)]},
                               readonly=True)

    _sql_constraints = [
        ('name_uniq', 'unique(name, company_id)',
         'Stock Request name must be unique'),
    ]

    @api.depends('allocation_ids')
    def _compute_move_ids(self):
        for request in self.sudo():
            request.move_ids = request.allocation_ids.mapped('stock_move_id')

    @api.depends('allocation_ids')
    def _compute_picking_ids(self):
        for request in self.sudo():
            request.picking_count = 0
            request.picking_ids = self.env['stock.picking']
            request.picking_ids = request.move_ids.filtered(
                lambda m: m.state != 'cancel').mapped('picking_id')
            request.picking_count = len(request.picking_ids)

    @api.depends('allocation_ids', 'allocation_ids.stock_move_id.state',
                 'allocation_ids.stock_move_id.move_line_ids',
                 'allocation_ids.stock_move_id.move_line_ids.qty_done')
    def _compute_qty(self):
        for request in self.sudo():
            done_qty = sum(
                request.allocation_ids.mapped('allocated_product_qty'))
            open_qty = sum(request.allocation_ids.mapped('open_product_qty'))
            request.qty_done = request.product_id.uom_id._compute_quantity(
                done_qty, request.product_uom_id)
            request.qty_in_progress = \
                request.product_id.uom_id._compute_quantity(
                    open_qty, request.product_uom_id)

    @api.constrains('order_id', 'requested_by')
    def check_order_requested_by(self):
        if self.order_id and self.order_id.requested_by != self.requested_by:
            raise ValidationError(_('Requested by must be equal to the order'))

    @api.constrains('order_id', 'warehouse_id')
    def check_order_warehouse_id(self):
        if self.order_id and self.order_id.warehouse_id != self.warehouse_id:
            raise ValidationError(_('Warehouse must be equal to the order'))

    @api.constrains('order_id', 'location_id')
    def check_order_location(self):
        if self.order_id and self.order_id.location_id != self.location_id:
            raise ValidationError(_('Location must be equal to the order'))

    @api.constrains('order_id', 'procurement_group_id')
    def check_order_procurement_group(self):
        if (self.order_id and self.order_id.procurement_group_id !=
                self.procurement_group_id):
            raise ValidationError(
                _('Procurement group must be equal to the order'))

    @api.constrains('order_id', 'company_id')
    def check_order_company(self):
        if self.order_id and self.order_id.company_id != self.company_id:
            raise ValidationError(_('Company must be equal to the order'))

    @api.constrains('order_id', 'expected_date')
    def check_order_expected_date(self):
        if self.order_id and self.order_id.expected_date != self.expected_date:
            raise ValidationError(
                _('Expected date must be equal to the order'))

    @api.constrains('order_id', 'picking_policy')
    def check_order_picking_policy(self):
        if (self.order_id
                and self.order_id.picking_policy != self.picking_policy):
            raise ValidationError(
                _('The picking policy must be equal to the order'))

    @api.multi
    def _action_confirm(self):
        self._action_launch_procurement_rule()
        self.state = 'open'

    @api.multi
    def action_confirm(self):
        self._action_confirm()
        return True

    def action_draft(self):
        self.state = 'draft'
        return True

    def action_cancel(self):
        self.sudo().mapped('move_ids')._action_cancel()
        self.state = 'cancel'
        return True

    def action_done(self):
        self.state = 'done'
        if self.order_id:
            self.order_id.check_done()
        return True

    def check_done(self):
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')
        for request in self:
            allocated_qty = sum(
                request.allocation_ids.mapped('allocated_product_qty'))
            qty_done = request.product_id.uom_id._compute_quantity(
                allocated_qty, request.product_uom_id)
            if float_compare(qty_done,
                             request.product_uom_qty,
                             precision_digits=precision) >= 0:
                request.action_done()
        return True

    def _prepare_procurement_values(self, group_id=False):
        """ Prepare specific key for moves or other components that
        will be created from a procurement rule
        coming from a stock request. This method could be override
        in order to add other custom key that could be used in
        move/po creation.
        """
        return {
            'date_planned': self.expected_date,
            'warehouse_id': self.warehouse_id,
            'stock_request_allocation_ids': self.id,
            'group_id': group_id or self.procurement_group_id.id or False,
            'route_ids': self.route_id,
            'stock_request_id': self.id,
        }

    @api.multi
    def _action_launch_procurement_rule(self):
        """
        Launch procurement group run method with required/custom
        fields genrated by a
        stock request. procurement group will launch '_run_move',
        '_run_buy' or '_run_manufacture'
        depending on the stock request product rule.
        """
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')
        errors = []
        for request in self:
            if (request.state != 'draft'
                    or request.product_id.type not in ('consu', 'product')):
                continue
            qty = 0.0
            for move in request.move_ids.filtered(
                    lambda r: r.state != 'cancel'):
                qty += move.product_qty

            if float_compare(
                    qty, request.product_qty, precision_digits=precision) >= 0:
                continue

            values = request._prepare_procurement_values(
                group_id=request.procurement_group_id)
            try:
                # We launch with sudo because potentially we could create
                # objects that the user is not authorized to create, such
                # as PO.
                self.env['procurement.group'].sudo().run(
                    request.product_id, request.product_uom_qty,
                    request.product_uom_id, request.location_id, request.name,
                    request.name, values)
            except UserError as error:
                errors.append(error.name)
        if errors:
            raise UserError('\n'.join(errors))
        return True

    @api.multi
    def action_view_transfer(self):
        action = self.env.ref('stock.action_picking_tree_all').read()[0]

        pickings = self.mapped('picking_ids')
        if len(pickings) > 1:
            action['domain'] = [('id', 'in', pickings.ids)]
        elif pickings:
            action['views'] = [(self.env.ref('stock.view_picking_form').id,
                                'form')]
            action['res_id'] = pickings.id
        return action

    @api.model
    def create(self, vals):
        upd_vals = vals.copy()
        if upd_vals.get('name', '/') == '/':
            upd_vals['name'] = self.env['ir.sequence'].next_by_code(
                'stock.request')
        return super().create(upd_vals)

    @api.multi
    def unlink(self):
        if self.filtered(lambda r: r.state != 'draft'):
            raise UserError(_('Only requests on draft state can be unlinked'))
        return super(StockRequest, self).unlink()
Exemple #8
0
class Task(models.Model):
    _name = "project.task"
    _description = "Task"
    _date_name = "date_start"
    _inherit = ['mail.thread']
    _mail_post_access = 'read'
    _order = "priority desc, sequence, date_start, name, id"

    @api.model
    def default_get(self, field_list):
        """ Set 'date_assign' if user_id is set. """
        result = super(Task, self).default_get(field_list)
        if 'user_id' in result:
            result['date_assign'] = fields.Datetime.now()
        return result

    def _get_default_partner(self):
        if 'default_project_id' in self.env.context:
            default_project_id = self.env['project.project'].browse(self.env.context['default_project_id'])
            return default_project_id.exists().partner_id

    def _get_default_stage_id(self):
        """ Gives default stage_id """
        project_id = self.env.context.get('default_project_id')
        if not project_id:
            return False
        return self.stage_find(project_id, [('fold', '=', False)])

    @api.model
    def _read_group_stage_ids(self, stages, domain, order):
        search_domain = [('id', 'in', stages.ids)]
        if 'default_project_id' in self.env.context:
            search_domain = ['|', ('project_ids', '=', self.env.context['default_project_id'])] + search_domain

        stage_ids = stages._search(search_domain, order=order, access_rights_uid=SUPERUSER_ID)
        return stages.browse(stage_ids)

    active = fields.Boolean(default=True)
    name = fields.Char(string='Task Title', track_visibility='always', required=True, index=True)
    description = fields.Html(string='Description')
    priority = fields.Selection([
            ('0','Non Starred'),
            ('1','Starred')
        ], default='0', index=True, string="Starred")
    sequence = fields.Integer(string='Sequence', index=True, default=10,
        help="Gives the sequence order when displaying a list of tasks.")
    stage_id = fields.Many2one('project.task.type', string='Stage', track_visibility='onchange', index=True,
        default=_get_default_stage_id, group_expand='_read_group_stage_ids',
        domain="[('project_ids', '=', project_id)]", copy=False)
    tag_ids = fields.Many2many('project.tags', string='Tags', oldname='categ_ids')
    kanban_state = fields.Selection([
        ('normal', 'Grey'),
        ('done', 'Green'),
        ('blocked', 'Red')], string='Kanban State',
        copy=False, default='normal', required=True, track_visibility='onchange',
        help="A task's kanban state indicates special situations affecting it:\n"
             " * Grey is the default situation\n"
             " * Red indicates something is preventing the progress of this task\n"
             " * Green indicates the task is ready to be pulled to the next stage")
    create_date = fields.Datetime(index=True)
    write_date = fields.Datetime(index=True)  #not displayed in the view but it might be useful with base_action_rule module (and it needs to be defined first for that)
    date_start = fields.Datetime(string='Starting Date',
    default=fields.Datetime.now,
    index=True, copy=False)
    date_end = fields.Datetime(string='Ending Date', index=True, copy=False)
    date_assign = fields.Datetime(string='Assigning Date', index=True, copy=False, readonly=True)
    date_deadline = fields.Date(string='Deadline', index=True, copy=False)
    date_last_stage_update = fields.Datetime(string='Last Stage Update',
        default=fields.Datetime.now,
        index=True,
        copy=False,
        readonly=True)
    project_id = fields.Many2one('project.project',
        string='Project',
        default=lambda self: self.env.context.get('default_project_id'),
        index=True,
        track_visibility='onchange',
        change_default=True)
    notes = fields.Text(string='Notes')
    planned_hours = fields.Float(string='Initially Planned Hours', help='Estimated time to do the task, usually set by the project manager when the task is in draft state.')
    remaining_hours = fields.Float(string='Remaining Hours', digits=(16,2), help="Total remaining time, can be re-estimated periodically by the assignee of the task.")
    user_id = fields.Many2one('res.users',
        string='Assigned to',
        default=lambda self: self.env.uid,
        index=True, track_visibility='always')
    partner_id = fields.Many2one('res.partner',
        string='Customer',
        default=_get_default_partner)
    manager_id = fields.Many2one('res.users', string='Project Manager', related='project_id.user_id', readonly=True)
    company_id = fields.Many2one('res.company',
        string='Company',
        default=lambda self: self.env['res.company']._company_default_get())
    color = fields.Integer(string='Color Index')
    user_email = fields.Char(related='user_id.email', string='User Email', readonly=True)
    attachment_ids = fields.One2many('ir.attachment', 'res_id', domain=lambda self: [('res_model', '=', self._name)], auto_join=True, string='Attachments')
    # In the domain of displayed_image_id, we couln't use attachment_ids because a one2many is represented as a list of commands so we used res_model & res_id
    displayed_image_id = fields.Many2one('ir.attachment', domain="[('res_model', '=', 'project.task'), ('res_id', '=', id), ('mimetype', 'ilike', 'image')]", string='Displayed Image')
    legend_blocked = fields.Char(related='stage_id.legend_blocked', string='Kanban Blocked Explanation', readonly=True)
    legend_done = fields.Char(related='stage_id.legend_done', string='Kanban Valid Explanation', readonly=True)
    legend_normal = fields.Char(related='stage_id.legend_normal', string='Kanban Ongoing Explanation', readonly=True)

    @api.onchange('project_id')
    def _onchange_project(self):
        default_partner_id = self.env.context.get('default_partner_id')
        default_partner = self.env['res.partner'].browse(default_partner_id) if default_partner_id else None
        if self.project_id:
            self.partner_id = self.project_id.partner_id or default_partner
            self.stage_id = self.stage_find(self.project_id.id, [('fold', '=', False)])
        else:
            self.partner_id = default_partner
            self.stage_id = False

    @api.onchange('user_id')
    def _onchange_user(self):
        if self.user_id:
            self.date_start = fields.Datetime.now()

    @api.multi
    def copy(self, default=None):
        if default is None:
            default = {}
        if not default.get('name'):
            default['name'] = _("%s (copy)") % self.name
        if 'remaining_hours' not in default:
            default['remaining_hours'] = self.planned_hours
        return super(Task, self).copy(default)

    @api.constrains('date_start', 'date_end')
    def _check_dates(self):
        if any(self.filtered(lambda task: task.date_start and task.date_end and task.date_start > task.date_end)):
            raise ValidationError(_('Error ! Task starting date must be lower than its ending date.'))

    # Override view according to the company definition
    @api.model
    def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
        # read uom as admin to avoid access rights issues, e.g. for portal/share users,
        # this should be safe (no context passed to avoid side-effects)
        obj_tm = self.env.user.company_id.project_time_mode_id
        tm = obj_tm and obj_tm.name or 'Hours'

        res = super(Task, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)

        # read uom as admin to avoid access rights issues, e.g. for portal/share users,
        # this should be safe (no context passed to avoid side-effects)
        obj_tm = self.env.user.company_id.project_time_mode_id
        # using get_object to get translation value
        uom_hour = self.env.ref('product.product_uom_hour', False)
        if not obj_tm or not uom_hour or obj_tm.id == uom_hour.id:
            return res

        eview = etree.fromstring(res['arch'])

        # if the project_time_mode_id is not in hours (so in days), display it as a float field
        def _check_rec(eview):
            if eview.attrib.get('widget', '') == 'float_time':
                eview.set('widget', 'float')
            for child in eview:
                _check_rec(child)
            return True

        _check_rec(eview)

        res['arch'] = etree.tostring(eview)

        # replace reference of 'Hours' to 'Day(s)'
        for f in res['fields']:
            # TODO this NOT work in different language than english
            # the field 'Initially Planned Hours' should be replaced by 'Initially Planned Days'
            # but string 'Initially Planned Days' is not available in translation
            if 'Hours' in res['fields'][f]['string']:
                res['fields'][f]['string'] = res['fields'][f]['string'].replace('Hours', obj_tm.name)
        return res

    @api.model
    def get_empty_list_help(self, help):
        self = self.with_context(
            empty_list_help_id=self.env.context.get('default_project_id'),
            empty_list_help_model='project.project',
            empty_list_help_document_name=_("tasks")
        )
        return super(Task, self).get_empty_list_help(help)

    # ----------------------------------------
    # Case management
    # ----------------------------------------

    def stage_find(self, section_id, domain=[], order='sequence'):
        """ Override of the base.stage method
            Parameter of the stage search taken from the lead:
            - section_id: if set, stages must belong to this section or
              be a default stage; if not set, stages must be default
              stages
        """
        # collect all section_ids
        section_ids = []
        if section_id:
            section_ids.append(section_id)
        section_ids.extend(self.mapped('project_id').ids)
        search_domain = []
        if section_ids:
            search_domain = [('|')] * (len(section_ids) - 1)
            for section_id in section_ids:
                search_domain.append(('project_ids', '=', section_id))
        search_domain += list(domain)
        # perform search, return the first found
        return self.env['project.task.type'].search(search_domain, order=order, limit=1).id

    # ------------------------------------------------
    # CRUD overrides
    # ------------------------------------------------

    @api.model
    def create(self, vals):
        # context: no_log, because subtype already handle this
        context = dict(self.env.context, mail_create_nolog=True)

        # for default stage
        if vals.get('project_id') and not context.get('default_project_id'):
            context['default_project_id'] = vals.get('project_id')
        # user_id change: update date_assign
        if vals.get('user_id'):
            vals['date_assign'] = fields.Datetime.now()
        task = super(Task, self.with_context(context)).create(vals)
        return task

    @api.multi
    def write(self, vals):
        now = fields.Datetime.now()
        # stage change: update date_last_stage_update
        if 'stage_id' in vals:
            vals['date_last_stage_update'] = now
            # reset kanban state when changing stage
            if 'kanban_state' not in vals:
                vals['kanban_state'] = 'normal'
        # user_id change: update date_assign
        if vals.get('user_id'):
            vals['date_assign'] = now

        result = super(Task, self).write(vals)

        return result

    # ---------------------------------------------------
    # Mail gateway
    # ---------------------------------------------------

    @api.multi
    def _track_template(self, tracking):
        res = super(Task, self)._track_template(tracking)
        test_task = self[0]
        changes, tracking_value_ids = tracking[test_task.id]
        if 'stage_id' in changes and test_task.stage_id.mail_template_id:
            res['stage_id'] = (test_task.stage_id.mail_template_id, {'composition_mode': 'mass_mail'})
        return res

    @api.multi
    def _track_subtype(self, init_values):
        self.ensure_one()
        if 'kanban_state' in init_values and self.kanban_state == 'blocked':
            return 'project.mt_task_blocked'
        elif 'kanban_state' in init_values and self.kanban_state == 'done':
            return 'project.mt_task_ready'
        elif 'user_id' in init_values and self.user_id:  # assigned -> new
            return 'project.mt_task_new'
        elif 'stage_id' in init_values and self.stage_id and self.stage_id.sequence <= 1:  # start stage -> new
            return 'project.mt_task_new'
        elif 'stage_id' in init_values:
            return 'project.mt_task_stage'
        return super(Task, self)._track_subtype(init_values)

    @api.multi
    def _notification_recipients(self, message, groups):
        """ Handle project users and managers recipients that can convert assign
        tasks and create new one directly from notification emails. """
        groups = super(Task, self)._notification_recipients(message, groups)

        self.ensure_one()
        if not self.user_id:
            take_action = self._notification_link_helper('assign')
            project_actions = [{'url': take_action, 'title': _('I take it')}]
        else:
            project_actions = []

        new_group = (
            'group_project_user', lambda partner: bool(partner.user_ids) and any(user.has_group('project.group_project_user') for user in partner.user_ids), {
                'actions': project_actions,
            })

        return [new_group] + groups

    @api.model
    def message_get_reply_to(self, res_ids, default=None):
        """ Override to get the reply_to of the parent project. """
        tasks = self.sudo().browse(res_ids)
        project_ids = tasks.mapped('project_id').ids
        aliases = self.env['project.project'].message_get_reply_to(project_ids, default=default)
        return {task.id: aliases.get(task.project_id.id, False) for task in tasks}

    @api.multi
    def email_split(self, msg):
        email_list = tools.email_split((msg.get('to') or '') + ',' + (msg.get('cc') or ''))
        # check left-part is not already an alias
        aliases = self.mapped('project_id.alias_name')
        return filter(lambda x: x.split('@')[0] not in aliases, email_list)

    @api.model
    def message_new(self, msg, custom_values=None):
        """ Override to updates the document according to the email. """
        if custom_values is None:
            custom_values = {}
        defaults = {
            'name': msg.get('subject'),
            'planned_hours': 0.0,
            'partner_id': msg.get('author_id')
        }
        defaults.update(custom_values)

        res = super(Task, self).message_new(msg, custom_values=defaults)
        task = self.browse(res)
        email_list = task.email_split(msg)
        partner_ids = filter(None, task._find_partner_from_emails(email_list, force_create=False))
        task.message_subscribe(partner_ids)
        return res

    @api.multi
    def message_update(self, msg, update_vals=None):
        """ Override to update the task according to the email. """
        if update_vals is None:
            update_vals = {}
        maps = {
            'cost': 'planned_hours',
        }
        for line in msg['body'].split('\n'):
            line = line.strip()
            res = tools.command_re.match(line)
            if res:
                match = res.group(1).lower()
                field = maps.get(match)
                if field:
                    try:
                        update_vals[field] = float(res.group(2).lower())
                    except (ValueError, TypeError):
                        pass

        email_list = self.email_split(msg)
        partner_ids = filter(None, self._find_partner_from_emails(email_list, force_create=False))
        self.message_subscribe(partner_ids)
        return super(Task, self).message_update(msg, update_vals=update_vals)

    @api.multi
    def message_get_suggested_recipients(self):
        recipients = super(Task, self).message_get_suggested_recipients()
        for task in self.filtered('partner_id'):
            reason = _('Customer Email') if task.partner_id.email else _('Customer')
            task._message_add_suggested_recipient(recipients, partner=task.partner_id, reason=reason)
        return recipients

    @api.multi
    def message_get_email_values(self, notif_mail=None):
        res = super(Task, self).message_get_email_values(notif_mail=notif_mail)
        headers = {}
        if res.get('headers'):
            try:
                headers.update(safe_eval(res['headers']))
            except Exception:
                pass
        if self.project_id:
            current_objects = filter(None, headers.get('X-Odoo-Objects', '').split(','))
            current_objects.insert(0, 'project.project-%s, ' % self.project_id.id)
            headers['X-Odoo-Objects'] = ','.join(current_objects)
        if self.tag_ids:
            headers['X-Odoo-Tags'] = ','.join(self.tag_ids.mapped('name'))
        res['headers'] = repr(headers)
        return res
Exemple #9
0
class ProductProduct(models.Model):
    """Book variant of product"""
    _inherit = "product.product"

    @api.model
    def default_get(self, fields):
        '''Overide method to get default category books'''
        res = super(ProductProduct, self).default_get(fields)
        category = self.env['product.category'].search([('name', '=', 'Books')
                                                        ])
        res.update({'categ_id': category.id})
        return res

    @api.multi
    def name_get(self):
        ''' This method Returns the preferred display value
            (text representation) for the records with the given IDs.
        @param self : Object Pointer
        @param cr : Database Cursor
        @param uid : Current Logged in User
        @param ids :list of IDs
        @param context : context arguments, like language, time zone
        @return : tuples with the text representation of requested objects
                  for to-many relationships
         '''

        if not len(self.ids):
            return []

        def _name_get(d):
            name = d.get('name', '')
            barcode = d.get('barcode', False)
            if barcode:
                name = '[%s] %s' % (barcode or '', name)
            return (d['id'], name)

        return map(_name_get, self.read(['name', 'barcode']))

    @api.multi
    def _default_categ(self):
        ''' This method put default category of product
        @param self : Object Pointer
        @param cr : Database Cursor
        @param uid : Current Logged in User
        @param context : context arguments, like language, time zone
        '''

        if self._context is None:
            self._context = {}
        if 'category_id' in self._context and self._context['category_id']:
            return self._context['category_id']
        md = self.env['ir.model.data']
        res = False
        try:
            res = md.get_object_reference('library', 'product_category_1')[1]
        except ValueError:
            res = False
        return res

    @api.multi
    def _tax_incl(self):
        ''' This method include tax in product
        @param self : Object Pointer
        @param cr : Database Cursor
        @param uid : Current Logged in User
        @param ids :list of IDs
        @param field_name : name of fields
        @param arg : other arguments
        @param context : context arguments, like language, time zone
        @return : Dictionary
         '''
        res = {}

        for product in self:
            val = 0.0
            for c in self.env['account.tax'].compute(product.taxes_id,
                                                     product.list_price, 1,
                                                     False):
                val += round(c['amount'], 2)
            res[product.id] = round(val + product.list_price, 2)
        return res

    @api.multi
    def _get_partner_code_name(self, product, parent_id):
        ''' This method get the partner code name
        @param self : Object Pointer
        @param cr : Database Cursor
        @param uid : Current Logged in User
        @param ids :list of IDs
        @param product : name of field
        @param partner_id : name of field
        @param context : context arguments, like language, time zone
        @return : Dictionary
         '''
        for supinfo in product.seller_ids:
            if supinfo.name.id == parent_id:
                return {
                    'code': supinfo.product_code or product.default_code,
                    'name': supinfo.product_name or product.name
                }
        res = {'code': product.default_code, 'name': product.name}
        return res

    @api.multi
    def _product_code(self):
        ''' This method get the product code
        @param self : Object Pointer
        @param cr : Database Cursor
        @param uid : Current Logged in User
        @param ids :list of IDs
        @param name : name of field
        @param arg : other argument
        @param context : context arguments, like language, time zone
        @return : Dictionary
         '''
        res = {}
        parent_id = self._context.get('parent_id', None)
        for p in self:
            res[p.id] = self._get_partner_code_name(p, parent_id)['code']
        return res

    @api.multi
    def copy(self, default=None):
        ''' This method Duplicate record
            with given id updating it with default values
        @param self : Object Pointer
        @param cr : Database Cursor
        @param uid : Current Logged in User
        @param id : id of the record to copy
        @param default : dictionary of field values
               to override in the original values of the copied record
        @param context : standard Dictionary
        @return : id of the newly created record
        '''

        if default is None:
            default = {}
        default.update({'author_ids': []})
        return super(ProductProduct, self).copy(default)

    @api.model
    def create(self, vals):
        ''' This method is Create new student
        @param self : Object Pointer
        @param cr : Database Cursor
        @param uid : Current Logged in User
        @param vals : dictionary of new values to be set
        @param context : standard Dictionary
        @return :ID of newly created record.
        '''
        def _uniq(seq):
            keys = {}
            for e in seq:
                keys[e] = 1
            return keys.keys()

        # add link from editor to supplier:
        if 'editor' in vals:
            editor_id = vals['editor']
            supplier_model = self.env['library.editor.supplier']
            domain = [('name', '=', editor_id)]
            supplier_ids = [
                idn.id for idn in supplier_model.search(domain) if idn.id > 0
            ]
            suppliers = supplier_model.browse(supplier_ids)
            for obj in suppliers:
                supplier = [
                    0, 0, {
                        'pricelist_ids': [],
                        'name': obj.supplier_id.id,
                        'sequence': obj.sequence,
                        'qty': 0,
                        'delay': 1,
                        'product_code': False,
                        'product_name': False
                    }
                ]
                if 'seller_ids' not in vals:
                    vals['seller_ids'] = [supplier]
                else:
                    vals['seller_ids'].append(supplier)
        return super(ProductProduct, self).create(vals)

    @api.multi
    @api.depends('qty_available')
    def _compute_books_available(self):
        '''Computes the available books'''
        book_issue_obj = self.env['library.book.issue']
        for rec in self:
            issue_ids = book_issue_obj.sudo().search([('name', '=', rec.id),
                                                      ('state', 'in',
                                                       ('issue', 'reissue'))])
            occupied_no = 0.0
            if issue_ids:
                occupied_no = len(issue_ids)
            # reduces the quantity when book is issued
            rec.books_available = rec.sudo().qty_available - occupied_no
        return True

    @api.multi
    @api.depends('books_available')
    def _compute_books_availablity(self):
        '''Method to compute availability of book'''
        for rec in self:
            if rec.books_available >= 1:
                rec.availability = 'available'
            else:
                rec.availability = 'notavailable'
        return True

    isbn = fields.Char('ISBN Code',
                       unique=True,
                       help="Shows International Standard Book Number")
    catalog_num = fields.Char('Catalog number',
                              help="Shows Identification number of books")
    lang = fields.Many2one('product.lang', 'Language')
    editor_ids = fields.One2many('book.editor', "book_id", "Editor")
    author = fields.Many2one('library.author', 'Author')
    code = fields.Char(compute_="_product_code",
                       method=True,
                       string='Acronym',
                       store=True)
    catalog_num = fields.Char('Catalog number',
                              help="Reference number of book")
    creation_date = fields.Datetime(
        'Creation date',
        readonly=True,
        help="Record creation date",
        default=lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'))
    date_retour = fields.Datetime('Return Date', help='Book Return date')
    fine_lost = fields.Float('Fine Lost')
    fine_late_return = fields.Float('Late Return')
    tome = fields.Char('TOME',
                       help="Stores information of work in several volume")
    nbpage = fields.Integer('Number of pages')
    rack = fields.Many2one('library.rack',
                           'Rack',
                           help="Shows position of book")
    books_available = fields.Float("Books Available",
                                   compute="_compute_books_available")
    availability = fields.Selection([('available', 'Available'),
                                     ('notavailable', 'Not Available')],
                                    'Book Availability',
                                    default='available',
                                    compute="_compute_books_availablity")
    link_ids = Many2manySym('product.product', 'book_book_rel', 'product_id1',
                            'product_id2', 'Related Books')
    back = fields.Selection([('hard', 'HardBack'), ('paper', 'PaperBack')],
                            'Binding Type',
                            help="Shows books-binding type",
                            default='paper')
    pocket = fields.Char('Pocket')
    num_pocket = fields.Char('Collection No.',
                             help='Shows collection number in which'
                             'book resides')
    num_edition = fields.Integer('No. edition', help="Edition number of book")
    format = fields.Char('Format',
                         help="The general physical appearance of a book")
    #    price_cat = fields.Many2one('library.price.category', "Price category")
    is_ebook = fields.Boolean("Is EBook")
    is_subscription = fields.Boolean("Is Subscription based")
    subscrption_amt = fields.Float("Subscription Amount")
    attach_ebook = fields.Binary("Attach EBook")
    day_to_return_book = fields.Integer('Book Return Days')
    attchment_ids = fields.One2many('book.attachment', 'product_id',
                                    'Book Attachments')

    _sql_constraints = [('unique_barcode', 'unique(barcode)',
                         'barcode field must be unique across\
                          all the products'),
                        ('code_uniq', 'unique (code)',
                         'Code of the product must be unique !')]

    @api.onchange('is_ebook', 'attach_ebook')
    def onchange_availablilty(self):
        if self.is_ebook and self.attach_ebook:
            self.availability = 'available'

    @api.multi
    def action_purchase_order(self):
        purchase_line_obj = self.env['purchase.order.line']
        purchase = purchase_line_obj.search([('product_id', '=', self.id)])
        action = self.env.ref('purchase.purchase_form_action')
        result = action.read()[0]
        if not purchase:
            raise ValidationError(_('There is no Books Purchase !'))
        order = []
        [order.append(order_rec.order_id.id) for order_rec in purchase]
        if len(order) != 1:
            result['domain'] = "[('id', 'in', " + str(order) + ")]"
        else:
            res = self.env.ref('purchase.purchase_order_form', False)
            result['views'] = [(res and res.id or False, 'form')]
            result['res_id'] = purchase.order_id.id
        return result

    @api.multi
    def action_book_req(self):
        '''Method to request book'''
        for rec in self:
            book_req = self.env['library.book.request'].search([('name', '=',
                                                                 rec.id)])
            action = self.env.ref('library.action_lib_book_req')
            result = (action.read()[0])
            if not book_req:
                raise ValidationError(_('There is no Book requested'))
            req = []
            [req.append(request_rec.id) for request_rec in book_req]
            if len(req) != 1:
                result['domain'] = "[('id', 'in', " + str(req) + ")]"
            else:
                res = self.env.ref('library.view_book_library_req_form', False)
                result['views'] = [(res and res.id or False, 'form')]
                result['res_id'] = book_req.id
            return result
Exemple #10
0
class IrSessions(models.Model):
    _name = 'ir.sessions'
    _description = "Sessions"

    user_id = fields.Many2one('res.users',
                              'User',
                              ondelete='cascade',
                              required=True)
    logged_in = fields.Boolean('Logged in', required=True, index=True)
    session_id = fields.Char('Session ID', size=100, required=True)
    session_seconds = fields.Integer('Session duration in seconds')
    multiple_sessions_block = fields.Boolean('Block Multiple Sessions')
    date_login = fields.Datetime('Login', required=True)
    date_logout = fields.Datetime('Logout')
    date_expiration = fields.Datetime('Expiration Date',
                                      required=True,
                                      index=True,
                                      default=lambda *a: fields.Datetime.now())
    logout_type = fields.Selection(LOGOUT_TYPES, 'Logout Type')
    session_duration = fields.Char('Session Duration')
    user_kill_id = fields.Many2one(
        'res.users',
        'Killed by',
    )
    unsuccessful_message = fields.Char('Unsuccessful', size=252)
    ip = fields.Char('Remote IP', size=15)
    ip_location = fields.Char('IP Location', )
    remote_tz = fields.Char('Remote Time Zone', size=32, required=True)
    # Add other fields about the sessions from HEADER...

    _order = 'logged_in desc, date_expiration desc'

    # scheduler function to validate users session
    def validate_sessions(self):
        sessions = self.sudo().search([
            ('date_expiration', '<=',
             fields.datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
            ('logged_in', '=', True)
        ])
        if sessions:
            sessions._close_session(logout_type='to')
        return True

    @api.multi
    def action_close_session(self):
        redirect = self._close_session(logout_type='sk')
        if redirect:
            return werkzeug.utils.redirect(
                '/web/login?db=%s' % self.env.cr.dbname, 303)

    @api.multi
    def _on_session_logout(self, logout_type=None):
        now = fields.datetime.now()
        cr = self.pool.cursor()
        # autocommit: our single update request will be performed atomically.
        # (In this way, there is no opportunity to have two transactions
        # interleaving their cr.execute()..cr.commit() calls and have one
        # of them rolled back due to a concurrent access.)
        cr.autocommit(True)

        for session in self:
            session_duration = str(now - datetime.strptime(
                session.date_login, DEFAULT_SERVER_DATETIME_FORMAT)).split(
                    '.')[0]
            session.sudo().write({
                'logged_in':
                False,
                'date_logout':
                now.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                'logout_type':
                logout_type,
                'user_kill_id':
                SUPERUSER_ID,
                'session_duration':
                session_duration,
            })
        cr.commit()
        cr.close()
        return True

    @api.multi
    def _close_session(self, logout_type=None):
        redirect = False
        for r in self:
            if r.user_id.id == self.env.user.id:
                redirect = True
            session = root.session_store.get(r.session_id)
            session.logout(logout_type=logout_type, env=self.env)
        return redirect
Exemple #11
0
class HrAttendance(models.Model):
    _name = "hr.attendance"
    _description = "Attendance"
    _order = "check_in desc"

    def _default_employee(self):
        return self.env.user.employee_id

    employee_id = fields.Many2one('hr.employee',
                                  string="Employee",
                                  default=_default_employee,
                                  required=True,
                                  ondelete='cascade',
                                  index=True)
    department_id = fields.Many2one('hr.department',
                                    string="Department",
                                    related="employee_id.department_id",
                                    readonly=True)
    check_in = fields.Datetime(string="Check In",
                               default=fields.Datetime.now,
                               required=True)
    check_out = fields.Datetime(string="Check Out")
    worked_hours = fields.Float(string='Worked Hours',
                                compute='_compute_worked_hours',
                                store=True,
                                readonly=True)

    def name_get(self):
        result = []
        for attendance in self:
            if not attendance.check_out:
                result.append(
                    (attendance.id, _("%(empl_name)s from %(check_in)s") % {
                        'empl_name':
                        attendance.employee_id.name,
                        'check_in':
                        format_datetime(
                            self.env, attendance.check_in, dt_format=False),
                    }))
            else:
                result.append(
                    (attendance.id,
                     _("%(empl_name)s from %(check_in)s to %(check_out)s") % {
                         'empl_name':
                         attendance.employee_id.name,
                         'check_in':
                         format_datetime(
                             self.env, attendance.check_in, dt_format=False),
                         'check_out':
                         format_datetime(
                             self.env, attendance.check_out, dt_format=False),
                     }))
        return result

    @api.depends('check_in', 'check_out')
    def _compute_worked_hours(self):
        for attendance in self:
            if attendance.check_out and attendance.check_in:
                delta = attendance.check_out - attendance.check_in
                attendance.worked_hours = delta.total_seconds() / 3600.0
            else:
                attendance.worked_hours = False

    @api.constrains('check_in', 'check_out')
    def _check_validity_check_in_check_out(self):
        """ verifies if check_in is earlier than check_out. """
        for attendance in self:
            if attendance.check_in and attendance.check_out:
                if attendance.check_out < attendance.check_in:
                    raise exceptions.ValidationError(
                        _('"Check Out" time cannot be earlier than "Check In" time.'
                          ))

    @api.constrains('check_in', 'check_out', 'employee_id')
    def _check_validity(self):
        """ Verifies the validity of the attendance record compared to the others from the same employee.
            For the same employee we must have :
                * maximum 1 "open" attendance record (without check_out)
                * no overlapping time slices with previous employee records
        """
        for attendance in self:
            # we take the latest attendance before our check_in time and check it doesn't overlap with ours
            last_attendance_before_check_in = self.env['hr.attendance'].search(
                [
                    ('employee_id', '=', attendance.employee_id.id),
                    ('check_in', '<=', attendance.check_in),
                    ('id', '!=', attendance.id),
                ],
                order='check_in desc',
                limit=1)
            if last_attendance_before_check_in and last_attendance_before_check_in.check_out and last_attendance_before_check_in.check_out > attendance.check_in:
                raise exceptions.ValidationError(
                    _("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s"
                      ) % {
                          'empl_name':
                          attendance.employee_id.name,
                          'datetime':
                          format_datetime(
                              self.env, attendance.check_in, dt_format=False),
                      })

            if not attendance.check_out:
                # if our attendance is "open" (no check_out), we verify there is no other "open" attendance
                no_check_out_attendances = self.env['hr.attendance'].search(
                    [
                        ('employee_id', '=', attendance.employee_id.id),
                        ('check_out', '=', False),
                        ('id', '!=', attendance.id),
                    ],
                    order='check_in desc',
                    limit=1)
                if no_check_out_attendances:
                    raise exceptions.ValidationError(
                        _("Cannot create new attendance record for %(empl_name)s, the employee hasn't checked out since %(datetime)s"
                          ) %
                        {
                            'empl_name':
                            attendance.employee_id.name,
                            'datetime':
                            format_datetime(self.env,
                                            no_check_out_attendances.check_in,
                                            dt_format=False),
                        })
            else:
                # we verify that the latest attendance with check_in time before our check_out time
                # is the same as the one before our check_in time computed before, otherwise it overlaps
                last_attendance_before_check_out = self.env[
                    'hr.attendance'].search([
                        ('employee_id', '=', attendance.employee_id.id),
                        ('check_in', '<', attendance.check_out),
                        ('id', '!=', attendance.id),
                    ],
                                            order='check_in desc',
                                            limit=1)
                if last_attendance_before_check_out and last_attendance_before_check_in != last_attendance_before_check_out:
                    raise exceptions.ValidationError(
                        _("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s"
                          ) % {
                              'empl_name':
                              attendance.employee_id.name,
                              'datetime':
                              format_datetime(
                                  self.env,
                                  last_attendance_before_check_out.check_in,
                                  dt_format=False),
                          })

    @api.model
    def _get_day_start_and_day(self, employee, dt):
        #Returns a tuple containing the datetime in naive UTC of the employee's start of the day
        # and the date it was for that employee
        if not dt.tzinfo:
            date_employee_tz = pytz.utc.localize(dt).astimezone(
                pytz.timezone(employee._get_tz()))
        else:
            date_employee_tz = dt
        start_day_employee_tz = date_employee_tz.replace(hour=0,
                                                         minute=0,
                                                         second=0)
        return (start_day_employee_tz.astimezone(
            pytz.utc).replace(tzinfo=None), start_day_employee_tz.date())

    def _get_attendances_dates(self):
        # Returns a dictionnary {employee_id: set((datetimes, dates))}
        attendances_emp = defaultdict(set)
        for attendance in self.filtered(lambda a: a.employee_id.company_id.
                                        hr_attendance_overtime and a.check_in):
            check_in_day_start = attendance._get_day_start_and_day(
                attendance.employee_id, attendance.check_in)
            if check_in_day_start[0] < datetime.combine(
                    attendance.employee_id.company_id.overtime_start_date,
                    datetime.min.time()):
                continue
            attendances_emp[attendance.employee_id].add(check_in_day_start)
            if attendance.check_out:
                check_out_day_start = attendance._get_day_start_and_day(
                    attendance.employee_id, attendance.check_out)
                attendances_emp[attendance.employee_id].add(
                    check_out_day_start)
        return attendances_emp

    def _update_overtime(self, employee_attendance_dates=None):
        if employee_attendance_dates is None:
            employee_attendance_dates = self._get_attendances_dates()

        overtime_to_unlink = self.env['hr.attendance.overtime']
        overtime_vals_list = []

        for emp, attendance_dates in employee_attendance_dates.items():
            # get_attendances_dates returns the date translated from the local timezone without tzinfo,
            # and contains all the date which we need to check for overtime
            emp_tz = pytz.timezone(emp._get_tz())
            attendance_domain = []
            for attendance_date in attendance_dates:
                attendance_domain = OR([
                    attendance_domain,
                    [
                        ('check_in', '>=', attendance_date[0]),
                        ('check_in', '<',
                         attendance_date[0] + timedelta(hours=24)),
                    ]
                ])
            attendance_domain = AND([[('employee_id', '=', emp.id)],
                                     attendance_domain])

            # Attendances per LOCAL day
            attendances_per_day = defaultdict(
                lambda: self.env['hr.attendance'])
            all_attendances = self.env['hr.attendance'].search(
                attendance_domain)
            for attendance in all_attendances:
                check_in_day_start = attendance._get_day_start_and_day(
                    attendance.employee_id, attendance.check_in)
                attendances_per_day[check_in_day_start[1]] += attendance

            # As _attendance_intervals_batch and _leave_intervals_batch both take localized dates we need to localize those date
            start = pytz.utc.localize(
                min(attendance_dates, key=itemgetter(0))[0])
            stop = pytz.utc.localize(
                max(attendance_dates, key=itemgetter(0))[0] +
                timedelta(hours=24))

            # Retrieve expected attendance intervals
            expected_attendances = emp.resource_calendar_id._attendance_intervals_batch(
                start, stop, emp.resource_id)[emp.resource_id.id]
            # Substract Global Leaves
            expected_attendances -= emp.resource_calendar_id._leave_intervals_batch(
                start, stop, None)[False]

            # working_times = {date: [(start, stop)]}
            working_times = defaultdict(lambda: [])
            for expected_attendance in expected_attendances:
                # Exclude resource.calendar.attendance
                working_times[expected_attendance[0].date()].append(
                    expected_attendance[:2])

            overtimes = self.env['hr.attendance.overtime'].sudo().search([
                ('employee_id', '=', emp.id),
                ('date', 'in', [day_data[1] for day_data in attendance_dates]),
                ('adjustment', '=', False),
            ])

            company_threshold = emp.company_id.overtime_company_threshold / 60.0
            employee_threshold = emp.company_id.overtime_employee_threshold / 60.0

            for day_data in attendance_dates:
                attendance_date = day_data[1]
                attendances = attendances_per_day.get(attendance_date,
                                                      self.browse())
                unfinished_shifts = attendances.filtered(
                    lambda a: not a.check_out)
                overtime_duration = 0
                overtime_duration_real = 0
                # Overtime is not counted if any shift is not closed or if there are no attendances for that day,
                # this could happen when deleting attendances.
                if not unfinished_shifts and attendances:
                    # The employee usually doesn't work on that day
                    if not working_times[attendance_date]:
                        # User does not have any resource_calendar_attendance for that day (week-end for example)
                        overtime_duration = sum(
                            attendances.mapped('worked_hours'))
                        overtime_duration_real = overtime_duration
                    # The employee usually work on that day
                    else:
                        # Compute start and end time for that day
                        planned_start_dt, planned_end_dt = False, False
                        planned_work_duration = 0
                        for calendar_attendance in working_times[
                                attendance_date]:
                            planned_start_dt = min(
                                planned_start_dt, calendar_attendance[0]
                            ) if planned_start_dt else calendar_attendance[0]
                            planned_end_dt = max(
                                planned_end_dt, calendar_attendance[1]
                            ) if planned_end_dt else calendar_attendance[1]
                            planned_work_duration += (
                                calendar_attendance[1] - calendar_attendance[0]
                            ).total_seconds() / 3600.0
                        # Count time before, during and after 'working hours'
                        pre_work_time, work_duration, post_work_time = 0, 0, 0

                        for attendance in attendances:
                            # consider check_in as planned_start_dt if within threshold
                            # if delta_in < 0: Checked in after supposed start of the day
                            # if delta_in > 0: Checked in before supposed start of the day
                            local_check_in = emp_tz.localize(
                                attendance.check_in)
                            delta_in = (planned_start_dt - local_check_in
                                        ).total_seconds() / 3600.0

                            # Started before or after planned date within the threshold interval
                            if (delta_in > 0 and delta_in <= company_threshold) or\
                                (delta_in < 0 and abs(delta_in) <= employee_threshold):
                                local_check_in = planned_start_dt
                            local_check_out = emp_tz.localize(
                                attendance.check_out)

                            # same for check_out as planned_end_dt
                            delta_out = (local_check_out - planned_end_dt
                                         ).total_seconds() / 3600.0
                            # if delta_out < 0: Checked out before supposed start of the day
                            # if delta_out > 0: Checked out after supposed start of the day

                            # Finised before or after planned date within the threshold interval
                            if (delta_out > 0 and delta_out <= company_threshold) or\
                                (delta_out < 0 and abs(delta_out) <= employee_threshold):
                                local_check_out = planned_end_dt

                            # There is an overtime at the start of the day
                            if local_check_in < planned_start_dt:
                                pre_work_time += (
                                    min(planned_start_dt, local_check_out) -
                                    local_check_in).total_seconds() / 3600.0
                            # Interval inside the working hours -> Considered as working time
                            if local_check_in <= planned_end_dt and local_check_out >= planned_start_dt:
                                work_duration += (
                                    min(planned_end_dt, local_check_out) -
                                    max(planned_start_dt, local_check_in)
                                ).total_seconds() / 3600.0
                            # There is an overtime at the end of the day
                            if local_check_out > planned_end_dt:
                                post_work_time += (local_check_out - max(
                                    planned_end_dt,
                                    local_check_in)).total_seconds() / 3600.0

                        # Overtime within the planned work hours + overtime before/after work hours is > company threshold
                        overtime_duration = work_duration - planned_work_duration
                        if pre_work_time > company_threshold:
                            overtime_duration += pre_work_time
                        if post_work_time > company_threshold:
                            overtime_duration += post_work_time
                        # Global overtime including the thresholds
                        overtime_duration_real = sum(
                            attendances.mapped(
                                'worked_hours')) - planned_work_duration

                overtime = overtimes.filtered(
                    lambda o: o.date == attendance_date)
                if not float_is_zero(overtime_duration,
                                     2) or unfinished_shifts:
                    # Do not create if any attendance doesn't have a check_out, update if exists
                    if unfinished_shifts:
                        overtime_duration = 0
                    if not overtime and overtime_duration:
                        overtime_vals_list.append({
                            'employee_id':
                            emp.id,
                            'date':
                            attendance_date,
                            'duration':
                            overtime_duration,
                            'duration_real':
                            overtime_duration_real,
                        })
                    elif overtime:
                        overtime.sudo().write({
                            'duration':
                            overtime_duration,
                            'duration_real':
                            overtime_duration
                        })
                elif overtime:
                    overtime_to_unlink |= overtime
        self.env['hr.attendance.overtime'].sudo().create(overtime_vals_list)
        overtime_to_unlink.sudo().unlink()

    @api.model_create_multi
    def create(self, vals_list):
        res = super().create(vals_list)
        res._update_overtime()
        return res

    def write(self, vals):
        attendances_dates = self._get_attendances_dates()
        super(HrAttendance, self).write(vals)
        if any(field in vals
               for field in ['employee_id', 'check_in', 'check_out']):
            # Merge attendance dates before and after write to recompute the
            # overtime if the attendances have been moved to another day
            for emp, dates in self._get_attendances_dates().items():
                attendances_dates[emp] |= dates
            self._update_overtime(attendances_dates)

    def unlink(self):
        attendances_dates = self._get_attendances_dates()
        super(HrAttendance, self).unlink()
        self._update_overtime(attendances_dates)

    @api.returns('self', lambda value: value.id)
    def copy(self):
        raise exceptions.UserError(_('You cannot duplicate an attendance.'))
Exemple #12
0
class AcruxChatMessages(models.Model):
    _inherit = 'acrux.chat.base.message'
    _name = 'acrux.chat.message'
    _description = 'Chat Message'
    _order = 'date_message desc, id desc'

    name = fields.Char('name', compute='_compute_name', store=True)
    msgid = fields.Char('Message Id')
    contact_id = fields.Many2one('acrux.chat.conversation', 'Contact',
                                 required=True, ondelete='cascade')
    connector_id = fields.Many2one('acrux.chat.connector', related='contact_id.connector_id',
                                   string='Connector', store=True, readonly=True)
    date_message = fields.Datetime('Date', required=True, default=fields.Datetime.now)
    from_me = fields.Boolean('Message From Me')
    company_id = fields.Many2one('res.company', related='contact_id.company_id',
                                 string='Company', store=True, readonly=True)
    ttype = fields.Selection(selection_add=[('contact', 'Contact'),
                                            ('product', 'Product')],
                             ondelete={'contact': 'cascade',
                                       'product': 'cascade'})
    error_msg = fields.Char('Error Message', readonly=True)
    event = fields.Selection([('unanswered', 'Unanswered Message'),
                              ('new_conv', 'New Conversation'),
                              ('res_conv', 'Resume Conversation')],
                             string='Event')
    user_id = fields.Many2one('res.users', string='Sellman', compute='_compute_user_id',
                              store=True)

    @api.depends('contact_id')
    def _compute_user_id(self):
        for r in self:
            user_id = r._get_user_id()
            r.user_id = user_id or self.env.user.id

    def _get_user_id(self):
        user_id = False
        if self.contact_id.sellman_id:
            user_id = self.contact_id.sellman_id.id
        return user_id

    @api.depends('text')
    def _compute_name(self):
        for r in self:
            if r.text:
                r.name = r.text[:10]
            else:
                r.name = '/'

    def conversation_update_time(self):
        for mess in self:
            is_info = bool(mess.ttype and mess.ttype.startswith('info'))
            if not is_info:
                data = {}
                cont = mess.contact_id
                if mess.from_me:
                    data.update({'last_sent': mess.date_message})
                    if cont.last_received:
                        data.update({'last_received_first': False})
                else:
                    # nº message
                    data.update({'last_received': mess.date_message})
                    # 1º message
                    if not cont.last_received_first:
                        data.update({'last_received_first': mess.date_message})
                if data:
                    cont.write(data)

    @api.model
    def create(self, vals):
        if vals.get('contact_id'):
            Conv = self.env['acrux.chat.conversation']
            conv_id = Conv.browse([vals.get('contact_id')])
            if not conv_id.last_received:
                vals.update(event='new_conv')
            elif conv_id.last_received < date_timedelta(minutes=-12 * 60):
                ''' After 12 hours it is resume '''
                vals.update(event='res_conv')
        ret = super(AcruxChatMessages, self).create(vals)
        ret.conversation_update_time()
        return ret

    @api.model
    def clean_number(self, number):
        return number.replace('+', '').replace(' ', '')

    @api.model
    def unlink_attachment(self, attach_to_del_ids, only_old=True):
        data = [('id', 'in', attach_to_del_ids)]
        if only_old:
            data.append(('delete_old', '=', True))
        to_del = self.env['ir.attachment'].sudo().search(data)
        erased_ids = to_del.ids
        to_del.unlink()
        return erased_ids

    def unlink(self):
        ''' Delete attachment too '''
        mess_ids = self.filtered(lambda x: x.res_model == 'ir.attachment' and x.res_id)
        attach_to_del = mess_ids.mapped('res_id')
        ret = super(AcruxChatMessages, self).unlink()
        if attach_to_del:
            self.unlink_attachment(attach_to_del)
        return ret

    def getJsDitc(self):
        out = self.read(['id', 'text', 'ttype', 'date_message', 'from_me', 'res_model',
                         'res_id', 'error_msg'])
        for x in out:
            x['date_message'] = date2local(self, x['date_message'])
        return out

    @api.model
    def get_url_image(self, res_model, res_id, field='image_chat', prod_id=None):
        url = False
        if not prod_id:
            prod_id = self.env[res_model].search([('id', '=', res_id)], limit=1)
        prod_id = prod_id if len(prod_id) == 1 else False
        if prod_id:
            field_obj = getattr(prod_id, field)
            if not field_obj:
                return prod_id, False
            check_weight = self.message_check_weight(field=field_obj)
            if check_weight:
                hash_id = hashlib.sha1(str((prod_id.write_date or prod_id.create_date or '')).encode('utf-8')).hexdigest()[0:7]
                url = '/web/static/chatresource/%s/%s_%s/%s' % (prod_id._name, prod_id.id, hash_id, field)
                base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
                url = base_url.rstrip('/') + url
        return prod_id, url

    @api.model
    def get_url_attach(self, att_id):
        url = False
        attach_id = self.env['ir.attachment'].sudo().search([('id', '=', att_id)], limit=1)
        attach_id = attach_id if len(attach_id) == 1 else False
        if attach_id:
            self.message_check_weight(value=attach_id.file_size, raise_on=True)
            access_token = attach_id.generate_access_token()[0]
            url = '/web/chatresource/%s/%s' % (attach_id.id, access_token)
            base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
            url = base_url.rstrip('/') + url
        return attach_id, url

    def message_parse(self):
        ''' Return message formated '''
        self.ensure_one()
        message = False
        if self.ttype == 'text':
            message = self.ca_ttype_text()
        elif self.ttype in ['image', 'video', 'file']:
            message = self.ca_ttype_file()
        elif self.ttype == 'audio':
            message = self.ca_ttype_audio()
        elif self.ttype == 'product':
            message = self.ca_ttype_product()
        elif self.ttype == 'sale_order':
            message = self.ca_ttype_sale()
        elif self.ttype == 'location':
            message = self.ca_ttype_location()
        elif self.ttype == 'contact':
            raise ValidationError('Not implemented')
        message.update({
            'to': self.clean_number(self.contact_id.number),
            'id': str(self.id),
        })
        return message

    def message_send(self):
        '''Return msgid
        In: {'type': string (required) ['text', 'image', 'video', 'file', 'audio', 'location'],
             'text': string (required),
             'from': string,
             'to': string,
             'filename': string,
             'url': string,
             'address': string,
             'latitude': string,
             'longitude': string,
             }
        Out: {'msg_id': [string, False],
              }
        '''
        self.ensure_one()
        ret = False
        connector_id = self.contact_id.connector_id
        if not self.ttype.startswith('info'):
            self.message_check_allow_send()
            data = self.message_parse() or {}
            result = connector_id.ca_request('send', data)
            msg_id = result.get('msg_id', False)
            if msg_id:
                self.msgid = msg_id
                return msg_id
            else:
                raise ValidationError('Server error.')
        else:
            return ret

    def message_check_allow_send(self):
        ''' Check elapsed time '''
        self.ensure_one()
        if self.text and len(self.text) >= 4000:
            raise ValidationError(_('Message is to large (4.000 caracters).'))
        connector_id = self.contact_id.connector_id
        if connector_id.connector_type == 'gupshup':
            last_received = self.contact_id.last_received
            max_hours = connector_id.time_to_respond
            if max_hours and max_hours > 0:
                if not last_received:
                    raise ValidationError(_('The client must have started a conversation.'))
                diff_hours = date_delta_seconds(last_received) / 3600
                if diff_hours >= max_hours:
                    raise ValidationError(_('The time to respond exceeded (%s hours). '
                                          'The limit is %s hours.') % (int(round(diff_hours)), max_hours))

    def message_check_weight(self, field=None, value=None, raise_on=False):
        ''' Check size '''
        self.ensure_one()
        ret = True
        limit = int(self.env['ir.config_parameter'].sudo().get_param('acrux_max_weight_kb') or '0')
        if limit > 0:
            limit *= 1024  # el parametro esta en kb pero el value pasa en bytes
            if field:
                value = len(base64.b64decode(field) if field else b'')
            if (value or 0) >= limit:
                if raise_on:
                    msg = '%s Kb' % limit if limit < 1000 else '%s Mb' % (limit / 1000)
                    raise ValidationError(_('Attachment exceeds the maximum size allowed (%s).') % msg)
                return False
        return ret

    @api.model
    def ca_ttype_text(self):
        ret = {
            'type': 'text',
            'text': self.text
        }
        return ret

    @api.model
    def ca_ttype_audio(self):
        if not self.res_id or self.res_model != 'ir.attachment':
            raise ValidationError('Attachment type is required.')
        attach_id, url = self.get_url_attach(self.res_id)
        if not attach_id:
            raise ValidationError('Attachment is required.')
        if not url:
            raise ValidationError('URL Attachment is required.')
        ret = {
            'type': 'audio',
            'url': url
        }
        return ret

    @api.model
    def ca_ttype_file(self):
        if not self.res_id or self.res_model != 'ir.attachment':
            raise ValidationError('Attachment type is required.')
        attach_id, url = self.get_url_attach(self.res_id)
        if not attach_id:
            raise ValidationError('Attachment is required.')
        if not url:
            raise ValidationError('URL Attachment is required.')
        ret = {
            'type': self.ttype,
            'text': self.text or '',
            'filename': attach_id.name,
            'url': url
        }
        return ret

    @api.model
    def ca_ttype_product(self):
        url = False
        filename = ''
        image_field = 'image_chat'  # to set dynamic: self.res_filed
        if not self.res_id or self.res_model != 'product.product':
            raise ValidationError('Product type is required.')
        prod_id = self.env[self.res_model].browse(self.res_id)
        if not prod_id:
            raise ValidationError('Product is required.')

        # caption ----------
        # or prod_id.name_get()[0][1]
        list_price = formatLang(self.env, prod_id.list_price, currency_obj=self.env.user.company_id.currency_id)
        caption = '%s\n%s / %s' % (self.text or prod_id.name.strip(),
                                   list_price, prod_id.uom_id.name)

        # image ----------
        field_image = getattr(prod_id, image_field)
        if field_image:
            filename = secure_filename(prod_id.name)
            attach = get_binary_attach(self.env, self.res_model, self.res_id, image_field,
                                       fields_ret=['mimetype'])
            mimetype = attach and attach['mimetype']
            if mimetype:
                ext = mimetype.split('/')
                if len(ext) == 2:
                    filename = secure_filename('%s.%s' % (prod_id.name, ext[1]))

            prod_id, url = self.get_url_image(res_model=self.res_model, res_id=self.res_id,
                                              field=image_field, prod_id=prod_id)
        # send ----------
        if not url:
            # Simple text message
            ret = {
                'type': 'text',
                'text': caption
            }
            return ret
        else:
            ret = {
                'type': 'file',
                'text': caption,
                'filename': filename,
                'url': url
            }
        return ret

    @api.model
    def ca_ttype_sale(self):
        if self.res_model != 'sale.order':
            raise ValidationError('Order type is required.')
        return self.ca_ttype_file()

    @api.model
    def ca_ttype_location(self):
        ''' Text format:
                name
                address
                latitude, longitude
        '''
        parse = self.text.split('\n')
        if len(parse) != 3:
            return self.ca_ttype_text()
        cords = parse[2].split(',')
        ret = {
            'type': 'location',
            'address': '%s\n%s' % (parse[0].strip(), parse[1].strip()),
            'latitude': cords[0].strip('( '),
            'longitude': cords[1].strip(') '),
        }
        return ret
class HelpdeskTicket(models.Model):
    _inherit = 'helpdesk.ticket'

    date_logged = fields.Datetime(string='Date logged')
Exemple #14
0
class Employee(models.Model):

    _name = "hr.employee"
    _description = "Employee"
    _order = 'name_related'
    _inherits = {'resource.resource': "resource_id"}
    _inherit = ['mail.thread']

    _mail_post_access = 'read'

    @api.model
    def _default_image(self):
        image_path = get_module_resource('hr', 'static/src/img',
                                         'default_image.png')
        return tools.image_resize_image_big(
            open(image_path, 'rb').read().encode('base64'))

    # we need a related field in order to be able to sort the employee by name
    name_related = fields.Char(related='resource_id.name',
                               string="Resource Name",
                               readonly=True,
                               store=True)
    country_id = fields.Many2one('res.country', string='Nationality (Country)')
    birthday = fields.Date('Date of Birth')
    ssnid = fields.Char('SSN No', help='Social Security Number')
    sinid = fields.Char('SIN No', help='Social Insurance Number')
    identification_id = fields.Char(string='Identification No')
    gender = fields.Selection([('male', 'Male'), ('female', 'Female'),
                               ('other', 'Other')])
    marital = fields.Selection([('single', 'Single'), ('married', 'Married'),
                                ('widower', 'Widower'),
                                ('divorced', 'Divorced')],
                               string='Marital Status')
    department_id = fields.Many2one('hr.department', string='Department')
    address_id = fields.Many2one('res.partner', string='Working Address')
    address_home_id = fields.Many2one('res.partner', string='Home Address')
    bank_account_id = fields.Many2one(
        'res.partner.bank',
        string='Bank Account Number',
        domain="[('partner_id', '=', address_home_id)]",
        help='Employee bank salary account')
    work_phone = fields.Char('Work Phone')
    mobile_phone = fields.Char('Work Mobile')
    work_email = fields.Char('Work Email')
    work_location = fields.Char('Work Location')
    notes = fields.Text('Notes')
    parent_id = fields.Many2one('hr.employee', string='Manager')
    category_ids = fields.Many2many(
        'hr.employee.category',
        'employee_category_rel',
        'emp_id',
        'category_id',
    )
    child_ids = fields.One2many('hr.employee',
                                'parent_id',
                                string='Subordinates')
    resource_id = fields.Many2one('resource.resource',
                                  string='Resource',
                                  ondelete='cascade',
                                  required=True,
                                  auto_join=True)
    coach_id = fields.Many2one('hr.employee', string='Coach')
    job_id = fields.Many2one('hr.job', string='Job Title')
    passport_id = fields.Char('Passport No')
    color = fields.Integer('Color Index', default=0)
    city = fields.Char(related='address_id.city')
    login = fields.Char(related='user_id.login', readonly=True)
    last_login = fields.Datetime(related='user_id.login_date',
                                 string='Latest Connection',
                                 readonly=True)

    # image: all image fields are base64 encoded and PIL-supported
    image = fields.Binary(
        "Photo",
        default=_default_image,
        attachment=True,
        help=
        "This field holds the image used as photo for the employee, limited to 1024x1024px."
    )
    image_medium = fields.Binary(
        "Medium-sized photo",
        attachment=True,
        help="Medium-sized photo of the employee. It is automatically "
        "resized as a 128x128px image, with aspect ratio preserved. "
        "Use this field in form views or some kanban views.")
    image_small = fields.Binary(
        "Small-sized photo",
        attachment=True,
        help="Small-sized photo of the employee. It is automatically "
        "resized as a 64x64px image, with aspect ratio preserved. "
        "Use this field anywhere a small image is required.")

    @api.constrains('parent_id')
    def _check_parent_id(self):
        for employee in self:
            if not employee._check_recursion():
                raise ValidationError(
                    _('Error! You cannot create recursive hierarchy of Employee(s).'
                      ))

    @api.onchange('address_id')
    def _onchange_address(self):
        self.work_phone = self.address_id.phone
        self.mobile_phone = self.address_id.mobile

    @api.onchange('company_id')
    def _onchange_company(self):
        address = self.company_id.partner_id.address_get(['default'])
        self.address_id = address['default'] if address else False

    @api.onchange('department_id')
    def _onchange_department(self):
        self.parent_id = self.department_id.manager_id

    @api.onchange('user_id')
    def _onchange_user(self):
        self.work_email = self.user_id.email
        self.name = self.user_id.name
        self.image = self.user_id.image

    @api.model
    def create(self, vals):
        tools.image_resize_images(vals)
        return super(Employee, self).create(vals)

    @api.multi
    def write(self, vals):
        if 'address_home_id' in vals:
            account_id = vals.get('bank_account_id') or self.bank_account_id.id
            if account_id:
                self.env['res.partner.bank'].browse(
                    account_id).partner_id = vals['address_home_id']
        tools.image_resize_images(vals)
        return super(Employee, self).write(vals)

    @api.multi
    def unlink(self):
        resources = self.mapped('resource_id')
        super(Employee, self).unlink()
        return resources.unlink()

    @api.multi
    def action_follow(self):
        """ Wrapper because message_subscribe_users take a user_ids=None
            that receive the context without the wrapper.
        """
        return self.message_subscribe_users()

    @api.multi
    def action_unfollow(self):
        """ Wrapper because message_unsubscribe_users take a user_ids=None
            that receive the context without the wrapper.
        """
        return self.message_unsubscribe_users()

    @api.model
    def _message_get_auto_subscribe_fields(self,
                                           updated_fields,
                                           auto_follow_fields=None):
        """ Overwrite of the original method to always follow user_id field,
            even when not track_visibility so that a user will follow it's employee
        """
        if auto_follow_fields is None:
            auto_follow_fields = ['user_id']
        user_field_lst = []
        for name, field in self._fields.items():
            if name in auto_follow_fields and name in updated_fields and field.comodel_name == 'res.users':
                user_field_lst.append(name)
        return user_field_lst

    @api.multi
    def _message_auto_subscribe_notify(self, partner_ids):
        # Do not notify user it has been marked as follower of its employee.
        return
Exemple #15
0
class SyncLink(models.Model):

    _name = "sync.link"
    _description = "Resource Links"
    _order = "id desc"

    relation = fields.Char("Relation Name", required=True)
    system1 = fields.Char("System 1", required=True)
    # index system2 only to make search "Odoo links"
    system2 = fields.Char("System 2", required=True, index=True)
    ref1 = fields.Char("Ref 1", required=True)
    ref2 = fields.Char("Ref 2", required=True)
    date = fields.Datetime(string="Sync Date",
                           default=fields.Datetime.now,
                           required=True)
    model = fields.Char("Odoo Model", index=True)

    def _auto_init(self):
        res = super(SyncLink, self)._auto_init()
        tools.create_unique_index(
            self._cr,
            "sync_link_refs_uniq_index",
            self._table,
            ["relation", "system1", "system2", "ref1", "ref2"],
        )
        return res

    @api.model
    def _log(self, *args, **kwargs):
        log = self.env.context.get("log_function")
        if not log:
            return
        kwargs.setdefault("name", "sync.link")
        kwargs.setdefault("level", LOG_DEBUG)
        return log(*args, **kwargs)

    # External links
    @api.model
    def refs2vals(self, external_refs):
        external_refs = sorted(external_refs.items(),
                               key=lambda code_value: code_value[0])
        system1, ref1 = external_refs[0]
        system2, ref2 = external_refs[1]
        vals = {
            "system1": system1,
            "system2": system2,
            "ref1": ref1,
            "ref2": ref2,
        }
        for k in ["ref1", "ref2"]:
            if vals[k] is None:
                continue
            if isinstance(vals[k], list):
                vals[k] = [str(i) for i in vals[k]]
            else:
                vals[k] = str(vals[k])
        return vals

    @api.model
    def _set_link_external(self,
                           relation,
                           external_refs,
                           sync_date=None,
                           allow_many2many=False,
                           model=None):
        vals = self.refs2vals(external_refs)
        # Check for existing records
        if allow_many2many:
            existing = self._search_links_external(relation, external_refs)
        else:
            # check existing links for a part of external_refs
            refs1 = external_refs.copy()
            refs2 = external_refs.copy()
            for i, k in enumerate(external_refs.keys()):
                if i:
                    refs1[k] = None
                else:
                    refs2[k] = None

            existing = self._search_links_external(
                relation, refs1) or self._search_links_external(
                    relation, refs2)

            if existing and not (existing.ref1 == vals["ref1"]
                                 and existing.ref2 == vals["ref2"]):
                raise ValidationError(
                    _("%s link already exists: %s=%s, %s=%s") % (
                        relation,
                        existing.system1,
                        existing.ref1,
                        existing.system1,
                        existing.ref2,
                    ))

        if existing:
            self._log("{} Use existing link: {}".format(relation, vals))
            existing.update_links(sync_date)
            return existing

        if sync_date:
            vals["date"] = sync_date
        vals["relation"] = relation
        if model:
            vals["model"] = model
        self._log("Create link: %s" % vals)
        return self.create(vals)

    @api.model
    def _get_link_external(self, relation, external_refs):
        links = self._search_links_external(relation, external_refs)
        if len(links) > 1:
            raise ValidationError(
                _("get_link found multiple links. Use search_links for many2many relations"
                  ))
        self._log("Get link: {} {} -> {}".format(relation, external_refs,
                                                 links))
        return links

    @api.model
    def _search_links_external(self,
                               relation,
                               external_refs,
                               model=None,
                               make_logs=False):
        vals = self.refs2vals(external_refs)
        domain = [("relation", "=", relation)]
        if model:
            domain.append(("model", "=", model))
        for k, v in vals.items():
            if not v:
                continue
            operator = "in" if isinstance(v, list) else "="
            domain.append((k, operator, v))
        links = self.search(domain)
        if make_logs:
            self._log("Search links: {} -> {}".format(domain, links))
        return links

    def get(self, system):
        res = []
        for r in self:
            if r.system1 == system:
                res.append(r.ref1)
            elif r.system2 == system:
                res.append(r.ref2)
            else:
                raise ValueError(
                    _("Cannot find value for %s. Found: %s and %s") %
                    (system, r.system1, r.system2))
        return res

    # Odoo links
    @property
    def odoo(self):
        res = None
        for r in self:
            record = self.env[r.model].browse(int(getattr(r, ODOO_REF)))
            if res:
                res |= record
            else:
                res = record
        return res

    @property
    def external(self):
        res = [getattr(r, EXTERNAL_REF) for r in self]
        if len(res) == 1:
            return res[0]
        return res

    def _set_link_odoo(self,
                       record,
                       relation,
                       ref,
                       sync_date=None,
                       allow_many2many=False):
        refs = {ODOO: record.id, EXTERNAL: ref}
        return self._set_link_external(relation, refs, sync_date,
                                       allow_many2many, record._name)

    def _get_link_odoo(self, relation, ref):
        refs = {ODOO: None, EXTERNAL: ref}
        return self._get_link_external(relation, refs)

    def _search_links_odoo(self, records, relation, refs=None):
        refs = {ODOO: records.ids, EXTERNAL: refs}
        return self._search_links_external(relation,
                                           refs,
                                           model=records._name,
                                           make_logs=True)

    # Common API
    def _get_link(self, rel, ref_info):
        if isinstance(ref_info, dict):
            # External link
            external_refs = ref_info
            return self._get_link_external(rel, external_refs)
        else:
            # Odoo link
            ref = ref_info
            return self._get_link_odoo(rel, ref)

    @property
    def sync_date(self):
        return min(r.date for r in self)

    def update_links(self, sync_date=None):
        if not sync_date:
            sync_date = fields.Datetime.now()
        self.write({"date": sync_date})
        return self

    def __xor__(self, other):
        return (self | other) - (self & other)

    def unlink(self):
        self._log("Delete links: %s" % self)
        return super(SyncLink, self).unlink()

    @api.model
    def _get_eval_context(self):
        env = self.env

        def set_link(rel,
                     external_refs,
                     sync_date=None,
                     allow_many2many=False):
            # Works for external links only
            return env["sync.link"]._set_link_external(rel, external_refs,
                                                       sync_date,
                                                       allow_many2many)

        def search_links(rel, external_refs):
            # Works for external links only
            return env["sync.link"]._search_links_external(rel,
                                                           external_refs,
                                                           make_logs=True)

        def get_link(rel, ref_info):
            return env["sync.link"]._get_link(rel, ref_info)

        return {
            "set_link": set_link,
            "search_links": search_links,
            "get_link": get_link,
        }
Exemple #16
0
class Wizard(models.TransientModel):
    _name = 'wps.wizard'

    @api.model
    def get_default_date_model(self):
        return pytz.UTC.localize(datetime.now()).astimezone(timezone(self.env.user.tz or 'UTC'))

    report_file = fields.Char()
    name = fields.Char(string="File Name")
    args = []
    date = fields.Datetime()
    time = fields.Datetime()
    start_date = fields.Date(string='Start Date', required=True)
    end_date = fields.Date(string="End Date", required=True)
    days = fields.Integer(string="Days of Payment", readonly=True, store=True)
    salary_month = fields.Selection([('01', 'January'),
                                     ('02', 'February'),
                                     ('03', 'March'),
                                     ('04', 'April'),
                                     ('05', 'May'),
                                     ('06', 'June'),
                                     ('07', 'July'),
                                     ('08', 'August'),
                                     ('09', 'September'),
                                     ('10', 'October'),
                                     ('11', 'November'),
                                     ('12', 'December')
                                     ], string="Month of Salary", readonly=True)

    datas = fields.Binary('File', readonly=True)
    datas_fname = fields.Char('Filename', readonly=True)
    hr_department_ids = fields.Many2many('hr.department', string='Department(s)')
    category_ids = fields.Many2many('hr.employee.category', string='Tags')
    analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account')
    analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
    payment_method = fields.Many2one('mis.hr.paymentmethod', string="Payment Method")
    employee_id = fields.Many2one('hr.employee', string='Employee', domian="[('status', '!=', 'draft')]")

    def _get_hr_tags(self):
        if self.category_ids:
            return ('employee_id.category_ids', 'in', self.category_ids.ids)
        else:
            return ('company_id', '=', self.env.company.id)

    def _get_employee(self):
        if self.employee_id:
            return ('employee_id', '=', self.employee_id.id)
        else:
            return (1, '=', 1)

    def _get_analytic_account(self):
        if self.analytic_account_id:
            return ('employee_id.contract_ids.analytic_account_id', '=', self.analytic_account_id.id)
        else:
            return ('company_id', '=', self.env.company.id)

    def _get_payment_method(self):
        if self.payment_method:
            return ('employee_id.payment_method', '=', self.payment_method.id)
        else:
            return ('company_id', '=', self.env.company.id)

    def _get_analytic_tag_ids(self):
        if self.analytic_tag_ids:
            return ('employee_id.contract_ids.x_analytic_tag_ids', 'in', self.analytic_tag_ids.ids)
        else:
            return ('company_id', '=', self.env.company.id)

    def _get_department_ids(self):
        if self.hr_department_ids:
            return ('employee_id.department_id', 'in', self.hr_department_ids.ids)
        else:
            return ('company_id', '=', self.env.company.id)

    def _get_filtered_domain(self):
        return [('date_to', '>=', self.start_date), ('date_to', '<=', self.end_date),
                self._get_hr_tags(), self._get_analytic_account(), self._get_analytic_tag_ids(),
                self._get_department_ids(), self._get_payment_method(), self._get_employee(),
                ('company_id', '=', self.env.company.id)]

    @api.onchange('start_date', 'end_date')
    def on_date_change(self):
        if self.start_date and self.end_date:
            start = str(self.start_date).split('-')
            end = str(self.end_date).split('-')
            self.days = 1 + (date(year=int(end[0]), month=int(end[1]), day=int(end[2]))
                             - date(year=int(start[0]), month=int(start[1]), day=int(start[2]))).days
            if start[1] == end[1]:
                self.salary_month = start[1]

    def print_xlsx(self):
        if not self.env['res.users'].browse(self.env.uid).tz:
            raise UserError(_('Please Set a User Timezone'))
        if self.start_date and self.end_date:
            start = str(self.start_date).split('-')
            end = str(self.end_date).split('-')
            # if not start[1] == end[1]:
            # raise UserError(_('The Dates Can of Same Month Only'))
        # return self.env['hr.employee'].search(self._get_available_contracts_domain())
        slips = self.env['hr.payslip'].search(self._get_filtered_domain())

        if not slips:
            raise UserError(_('There are no payslip Created for the selected month'))

        user = self.env['res.users'].browse(self.env.uid)
        if user.tz:
            tz = pytz.timezone(user.tz) or pytz.utc
            date = pytz.utc.localize(datetime.now()).astimezone(tz)
            time = pytz.utc.localize(datetime.now()).astimezone(tz)
        else:
            date = datetime.now()
            time = datetime.now()

        datetime_string = self.get_default_date_model().strftime("%Y-%m-%d %H:%M:%S")
        date_string = self.get_default_date_model().strftime("%Y-%m-%d")
        date_string = self.start_date.strftime("%B-%y")
        h_date_string = self.start_date.strftime("%B %Y").upper()

        report_name = 'WPS_' + date.strftime("%y%m%d%H%M%S")
        filename = '%s %s' % (report_name, date_string)

        fp = BytesIO()
        workbook = xlsxwriter.Workbook(fp)
        wbf = {}
        wbf['content'] = workbook.add_format()
        wbf['header1'] = workbook.add_format(
            {'bold': 1, 'align': 'center', 'font_size': '13'})
        wbf['header1'].set_align('vcenter')
        wbf['header2'] = workbook.add_format(
            {'bold': 1, 'align': 'center', 'bg_color': '#C4D79B'})
        wbf['header2'].set_top()
        wbf['header2'].set_bottom()
        wbf['header2'].set_left()
        wbf['header2'].set_right()
        wbf['content_float'] = workbook.add_format({'align': 'right', 'num_format': '#,##0.00'})

        wbf['content_border'] = workbook.add_format()
        wbf['content_border'].set_top()
        wbf['content_border'].set_bottom()
        wbf['content_border'].set_left()
        wbf['content_border'].set_right()
        wbf['content_float_border'] = workbook.add_format({'align': 'right', 'num_format': '#,##0.00'})
        wbf['content_float_border'].set_top()
        wbf['content_float_border'].set_bottom()
        wbf['content_float_border'].set_left()
        wbf['content_float_border'].set_right()
        wbf['content_float_border_bg'] = workbook.add_format(
            {'align': 'right', 'num_format': '#,##0.00', 'bold': 1, 'bg_color': '#E1E1E1'})
        wbf['content_float_border_bg'].set_top()
        wbf['content_float_border_bg'].set_bottom()
        wbf['content_float_border_bg'].set_left()
        wbf['content_float_border_bg'].set_right()

        wbf['content_signature'] = workbook.add_format({'align': 'center', 'bold': 1})
        wbf['content_signature'].set_top(6)

        worksheet = workbook.add_worksheet(report_name)
        worksheet2 = workbook.add_worksheet("Summary")
        worksheet2.center_horizontally()
        worksheet2.set_margins(left=0, right=0, top=1.5, bottom=1.65)
        worksheet2.fit_to_pages(1, 0)
        worksheet2.set_footer('&C-------------------------\n&"Bold"(C.E.O)\n\n'+'&LPage &P of &N', {'margin': .5})
        worksheet2.set_row(0, 25)
        worksheet2.merge_range('A1:E1', 'EMPLOYEES SALARY '+h_date_string, wbf['header1'])

        col = 0
        column_width = 25
        worksheet.set_column(col, col, column_width)
        worksheet.write(0, col, 'Beneficiary Bank Name', wbf['header2'])
        worksheet2.set_column(col, col, 5)
        worksheet2.write(1, col, 'SL No', wbf['header2'])
        col = 1
        column_width = 16
        worksheet.set_column(col, col, column_width)
        worksheet.write(0, col, 'Bank Routing Code', wbf['header2'])
        worksheet2.set_column(col, col, 40)
        worksheet2.write(1, col, 'EMPLOYEES NAME', wbf['header2'])
        col = 2
        column_width = 15
        worksheet.set_column(col, col, column_width)
        worksheet.write(0, col, 'Transaction Type', wbf['header2'])
        worksheet2.set_column(col, col, 23.5)
        worksheet2.write(1, col, 'Beneficiary Bank Name', wbf['header2'])
        col = 3
        column_width = 15
        worksheet.set_column(col, col, column_width)
        worksheet.write(0, col, 'Transaction Code', wbf['header2'])
        worksheet2.set_column(col, col, 25)
        worksheet2.write(1, col, 'IBAN NUMBER', wbf['header2'])
        col = 4
        column_width = 40
        worksheet.set_column(col, col, column_width)
        worksheet.write(0, col, 'Beneficiary Name (Max.140 characters)', wbf['header2'])
        worksheet2.set_column(col, col, 12)
        worksheet2.write(1, col, 'SALARY AED', wbf['header2'])
        col = 5
        column_width = 25
        worksheet.set_column(col, col, column_width)
        worksheet.write(0, col, 'Beneficiary IBAN No.', wbf['header2'])
        col = 6
        column_width = 12
        worksheet.set_column(col, col, column_width)
        worksheet.write(0, col, 'Amount', wbf['header2'])
        col = 7
        column_width = 21
        worksheet.set_column(col, col, column_width)
        worksheet.write(0, col, 'Remittance Information', wbf['header2'])
        col = 8
        column_width = 14
        worksheet.set_column(col, col, column_width)
        worksheet.write(0, col, 'Transfer Month', wbf['header2'])

        count = 0
        sum = 0
        monthyear = self.end_date
        monthyear = str(monthyear).split('-')
        monthyear = str(monthyear[1]) + str(monthyear[0])
        wps = []
        for rec in slips:
            existing_lines = (
                line_id for line_id in wps if
                line_id['employee'] == rec.employee_id.full_name)
            main_line = next(existing_lines, False)
            amount = 0.00
            for line in rec.line_ids:
                if line.code == 'NET':
                    amount = -line.amount if rec.credit_note else line.amount

            if not main_line:
                main_line = {
                    'employee': rec.employee_id.full_name,
                    'bank': rec.employee_id.agent_id.name,
                    'routing_code': rec.employee_id.agent_id.routing_code,
                    'iban_no': rec.employee_id.iban_number,
                    'amount': amount
                }
                wps.append(main_line)
            else:
                main_line['amount'] += amount

        for rec in wps:
            if rec['amount'] != 0:
                count += 1
                col = 0
                worksheet2.write(count + 1, col, count, wbf['content_border'])
                if rec['bank']:
                    worksheet.write(count, col, rec['bank'], wbf['content_border'])
                    worksheet2.write(count + 1, 2, rec['bank'], wbf['content_border'])
                else:
                    worksheet2.write(count + 1, 2, '', wbf['content_border'])

                col += 1
                if rec['routing_code']:
                    worksheet.write(count, col, rec['routing_code'], wbf['content_border'])
                col += 1
                worksheet.write(count, col, "Salary", wbf['content_border'])
                col += 1
                worksheet.write(count, col, "SAL", wbf['content_border'])
                col += 1
                worksheet.write(count, col, rec['employee'], wbf['content_border'])
                worksheet2.write(count + 1, 1, rec['employee'], wbf['content_border'])
                col += 1
                if rec['iban_no']:
                    worksheet.write(count, col, rec['iban_no'], wbf['content_border'])
                    worksheet2.write(count + 1, 3, rec['iban_no'], wbf['content_border'])
                else:
                    worksheet2.write(count + 1, 3, '', wbf['content_border'])

                col += 1
                worksheet.write(count, col, rec['amount'], wbf['content_float_border'])
                worksheet2.write(count + 1, 4, rec['amount'], wbf['content_float_border'])
                sum += rec['amount']
                col += 1
                worksheet.write(count, col, "Salary Transfer", wbf['content_border'])
                col += 1
                worksheet.write(count, col, date_string, wbf['content_border'])
        count += 2
        worksheet2.merge_range('A%s:D%s' % (count + 1, count + 1), 'Total', wbf['content_float_border_bg'])
        worksheet2.write(count, 4, sum, wbf['content_float_border_bg'])

        workbook.close()
        out = base64.encodebytes(fp.getvalue())
        self.write({'datas': out, 'datas_fname': filename})
        fp.close()
        filename += '%2Exlsx'

        return {
            'type': 'ir.actions.act_url',
            'target': 'new',
            'url': 'web/content/?model=' + self._name + '&id=' + str(
                self.id) + '&field=datas&download=true&filename=' + filename,
        }
Exemple #17
0
class battle(models.Model):
    _name = 'game.battle'
    name = fields.Char(compute='_get_name')
    attack = fields.Many2many('res.partner',
                              relation='player_attacks',
                              column1='b_a',
                              column2='p_a',
                              domain="[('is_player','!=',True)]"
                              )  # Cal especificar el nom de la taula
    defend = fields.Many2many('res.partner',
                              relation='player_defends',
                              column1='b_d',
                              column2='p_d',
                              domain="[('is_player','!=',True)]")
    characters_attack_available = fields.Many2many(
        'game.character', compute='_get_characters_available')
    characters_defend_available = fields.Many2many(
        'game.character', compute='_get_characters_available')
    characters_attack = fields.Many2many(
        'game.character',
        relation='characters_attack',
        domain="[('id', 'in', characters_attack_available)]")
    characters_defend = fields.Many2many(
        'game.character',
        relation='characters_defend',
        domain="[('id', 'in', characters_defend_available)]")
    state = fields.Selection([('1', 'Creation'), ('2', 'Character Selection'),
                              ('3', 'Waiting'), ('4', 'Finished')],
                             compute='_get_state')
    time_remaining = fields.Char(compute='_get_state')

    def _get_date(self):
        date = datetime.now() + timedelta(hours=3)
        return fields.Datetime.to_string(date)

    date = fields.Datetime(
        default=_get_date)  # Serà calculada a partir de l'hora de creació
    finished = fields.Boolean()

    def _get_characters_available(self):
        for c in self:
            c.characters_attack_available = c.attack.mapped(
                'fortresses').mapped('characters').filtered(
                    lambda p: p.unemployed == True and p.health > 0)
            c.characters_defend_available = c.defend.mapped(
                'fortresses').mapped('characters').filtered(
                    lambda p: p.unemployed == True and p.health > 0)

    @api.onchange('attack', 'defend')
    def onchange_attack(self):
        for b in self:
            characters_available = b.attack.mapped('fortresses').mapped(
                'characters').filtered(
                    lambda p: p.unemployed == True and p.health > 0)
            characters_available_defense = b.defend.mapped(
                'fortresses').mapped('characters').filtered(
                    lambda p: p.unemployed == True and p.health > 0)

            b.characters_attack_available = characters_available
            b.characters_defend_available = characters_available_defense

            attackers = self.attack
            defenders = self.defend
            result = {}

            if len(attackers) > 0 and len(defenders) > 0:
                # Tots els atacants i defensors han d'estar en el mateix clan
                if len(attackers.mapped('clan')) > 1:
                    warning = {
                        'title': "Battle not valid",
                        'message': 'All attackers have to be the same clan'
                    }
                    result['warning'] = warning
                    return result

                if len(defenders.mapped('clan')) > 1:
                    warning = {
                        'title': "Battle not valid",
                        'message': 'All attackers have to be the same clan'
                    }
                    result['warning'] = warning
                    return result

                if defenders[0].clan == attackers[0].clan:
                    warning = {
                        'title': "Battle not valid",
                        'message': 'One clan cannot attack himself'
                    }
                    result['warning'] = warning
                    return result

                # Tots els caracters atacants ha de ser de jugadors atacants
                if len(
                        self.characters_defend
                ) > 0 and defenders.ids != self.characters_defend.mapped(
                        'player.id'):
                    warning = {
                        'title':
                        "Battle not valid",
                        'message':
                        'All the characters have to be from defender players'
                    }
                    result['warning'] = warning
                    return result

                if len(
                        self.characters_attack
                ) > 0 and attackers.ids != self.characters_attack.mapped(
                        'player.id'):
                    warning = {
                        'title':
                        "Battle not valid",
                        'message':
                        'All the characters have to be from attacker players'
                    }
                    result['warning'] = warning
                    return result

            # En cas de superar tots els tests
            result = {
                'domain': {
                    'characters_attack':
                    [('id', 'in', characters_available.ids)],
                    'characters_defend':
                    [('id', 'in', characters_available_defense.ids)],
                    'attack': [('id', 'not in', b.defend.ids),
                               ('is_player', '=', True)],
                    'defend': [('id', 'not in', b.attack.ids),
                               ('is_player', '=', True)],
                    #'attack': [('clan', 'in', b.attack.mapped('clan'))], 'defend': [('clan', 'in', b.defend.mapped('clan'))],
                },
            }
            if len(attackers) > 0:
                result['domain']['attack'] = [
                    ('id', 'not in', b.defend.ids),
                    ('clan', 'in', b.attack.mapped('clan').ids),
                    ('clan', 'not in', b.defend.mapped('clan').ids)
                ]
            if len(defenders) > 0:
                result['domain']['defend'] = [
                    ('id', 'not in', b.attack.ids),
                    ('clan', 'in', b.defend.mapped('clan').ids),
                    ('clan', 'not in', b.attack.mapped('clan').ids)
                ]

            return result

    @api.depends('attack', 'defend')
    def _get_name(self):
        for b in self:
            name = ''
            for i in b.attack:
                name = name + i.name + ', '
            name = name + ' VS '
            for i in b.defend:
                name = name + i.name + ', '
            b.name = name

    def _get_state(self):
        for b in self:
            b.state = '1'
            if len(b.attack) > 0 and len(b.defend) > 0:
                b.state = '2'
            if len(b.characters_attack) > 0 and len(b.characters_defend) > 0:
                b.state = '3'
            if b.finished == True:
                b.state = '4'
            start = datetime.now()
            end = fields.Datetime.from_string(b.date)
            relative = relativedelta(end, start)
            if end > start:
                b.time_remaining = str(relative.hours) + ":" + str(
                    relative.minutes)
            else:
                b.time_remaining = '00:00'

    def compute_battle(self):
        for b in self:
            at = b.characters_attack.ids  # Els atacants
            print(at)
            de = b.characters_defend.ids  # Els Defensors
            print(de)
            finished = False
            first = True  # La primera ronda es amb dispars, la resta es melee
            rounds = 1
            while (not finished):
                # L'ordre de la batalla és important,
                # ja que els primers són els primers en atacar defendre
                for i in zip(at, de):
                    c_at = self.env['game.character'].browse(
                        i[0])  # El caracter atacant
                    c_de = self.env['game.character'].browse(i[1])
                    b.calculate_attack(c_at, c_de, first)
                    b.calculate_attack(c_de, c_at, first)
                at = b.characters_attack.filtered(lambda c: c.health > 0).ids
                de = b.characters_defend.filtered(lambda c: c.health > 0).ids
                first = False
                if len(at) == 0 or len(de) == 0:
                    finished = True
                else:
                    rounds = rounds + 1

            # Ja ha acabat, ara a repartir experiencia i botin de guerra
            if len(at) == 0:
                wins = 'Defenders (' + str(len(de)) + ' survivors)'
                for d in self.env['game.character'].browse(de):
                    d.write({'war': d.war + 100})
                botin = b.characters_attack.mapped('stuff')
                players = b.defend.ids
                for s in botin:
                    player = random.choice(players)
                    s.write({'character': False, 'player': player})
            else:
                wins = 'Attackers (' + str(len(at)) + ' survivors)'
                for a in self.env['game.character'].browse(at):
                    a.write({
                        'war': a.war + 100
                    })  # Augmenta la experiència en guerra de l'atacant
                botin = b.characters_defend.mapped(
                    'stuff')  # Totes les armes passen al botin de guerra
                players = b.attack.ids
                for s in botin:
                    player = random.choice(players)
                    s.write({'character': False, 'player': player})
                # L'atacant ataca per obtindre recursos del defensor
                first_defender = b.defend[0]
                #print(first_defender)
                for r in first_defender.raws:
                    raw = r.raw.id
                    raws = b.attack[0].clan.raws.search([
                        ('raw', '=', raw), ('clan', '=', b.attack[0].clan.id)
                    ])[0]
                    #print(raws)
                    raws.write({
                        'quantity': r.quantity / 2
                    })  # El clan obté la meitat dels recursos del defensor

            # Anem a fer que alguns dels caiguts siguen ferits i no morts
            death = (b.characters_attack +
                     b.characters_defend).filtered(lambda c: c.health == 0)
            for d in death:
                if random.random() > 0.5:  # 50% de que no estiga mort
                    d.write({
                        'health': 1,
                        'war': d.war + 50
                    })  # Augmenta la seua experiencia en guerra

            self.env['game.log'].create({
                'title':
                'Battle ' + str(b.name),
                'description':
                wins + ' wins in ' + str(rounds) + ' rounds'
            })
            b.write({'finished': True})

    def calculate_attack(self, c_at, c_de, first):
        print('Ataca: ' + c_at.name + ' Defen: ' + c_de.name)
        # El nivell de 'war' indica quin dels dos caracters
        # Està més experimentat. Això li  dona un avantatge percentual
        ratio_at_de = c_at.war / (c_at.war + c_de.war)
        ratio_de_at = c_de.war / (c_at.war + c_de.war)
        print("Ratios: At. War: " + str(c_at.war) + " Def War: " +
              str(c_de.war) + " Ratio At: " + str(ratio_at_de) +
              " Ration Def: " + str(ratio_de_at))
        if first:
            # Calcular en base al shot
            at_best_shot = c_at.stuff.filtered(
                lambda s: s.type == '0')  # Si va equipat en armes de foc
            if at_best_shot:
                at_best_shot = at_best_shot.sorted(
                    key=lambda s: s.shoot,
                    reverse=True)[0].shoot  # La millor arma de foc
            else:
                at_best_shot = 1

        else:
            # Calcular en base al melee
            at_best_shot = c_at.stuff.filtered(
                lambda s: s.type == '1')  # Si va equipat en armes de melee
            if at_best_shot:
                at_best_shot = at_best_shot.sorted(
                    key=lambda s: s.melee,
                    reverse=True)[0].melee  # La millor arma de foc
            else:
                at_best_shot = 1

        de_best_armor = c_de.stuff.filtered(
            lambda s: s.type == '2')  # Si te armadura
        if de_best_armor:
            de_best_armor = de_best_armor.sorted(
                key=lambda s: s.armor,
                reverse=True)[0].armor  # La millor armadura
        else:
            de_best_armor = 1
        ratio_shot_armor = at_best_shot / (at_best_shot + de_best_armor
                                           )  # El ratio de arma/armadur
        print("Ratio Shot/armor: " + str(ratio_shot_armor))
        ratio_at_de = ratio_at_de * ratio_shot_armor
        print("Ratio final: " + str(ratio_at_de))
        if random.random(
        ) < ratio_at_de:  # quan més ratio mes probabilitat de fer mal
            damage_de = 100 * ratio_at_de  # Com que el ratio no pot ser > 1, el mal no pot ser > 100
            print('LI FA MAL')
        else:
            damage_de = 0
        print("Li fa este mal: " + str(damage_de))
        health_de = c_de.health - damage_de
        if health_de < 0:
            health_de = 0
        c_de.write({'health': health_de})
Exemple #18
0
class HotelReservationOrder(models.Model):

    @api.multi
    @api.depends('order_list')
    def _sub_total(self):
        '''
        amount_subtotal will display on change of order_list
        ----------------------------------------------------
        @param self: object pointer
        '''
        for sale in self:
            sale.amount_subtotal = sum(line.price_subtotal for line
                                       in sale.order_list)

    @api.multi
    @api.depends('amount_subtotal')
    def _total(self):
        '''
        amount_total will display on change of amount_subtotal
        -------------------------------------------------------
        @param self: object pointer
        '''
        for line in self:
            line.amount_total = line.amount_subtotal + (line.amount_subtotal *
                                                        line.tax) / 100.0

    @api.multi
    def reservation_generate_kot(self):
        """
        This method create new record for hotel restaurant order list.
        --------------------------------------------------------------
        @param self: The object pointer
        @return: new record set for hotel restaurant order list.
        """
        res = []
        order_tickets_obj = self.env['hotel.restaurant.kitchen.order.tickets']
        rest_order_list_obj = self.env['hotel.restaurant.order.list']
        for order in self:
            if len(order.order_list) == 0:
                raise except_orm(_('No Order Given'),
                                 _('Please Give an Order'))
            table_ids = [x.id for x in order.table_no]
            line_data = {
                'orderno': order.order_number,
                'resno': order.reservationno.reservation_id,
                'kot_date': order.date1,
                'w_name': order.waitername.name,
                'tableno': [(6, 0, table_ids)],
                }
            kot_data = order_tickets_obj.create(line_data)
            self.kitchen_id = kot_data.id
            for order_line in order.order_list:
                o_line = {
                    'kot_order_list': kot_data.id,
                    'name': order_line.name.id,
                    'item_qty': order_line.item_qty,
                    'item_rate': order_line.item_rate
                }
                rest_order_list_obj.create(o_line)
                res.append(order_line.id)
            self.rest_id = [(6, 0, res)]
            self.state = 'order'
        return res

    @api.multi
    def reservation_update_kot(self):
        """
        This method update record for hotel restaurant order list.
        ----------------------------------------------------------
        @param self: The object pointer
        @return: update record set for hotel restaurant order list.
        """
        order_tickets_obj = self.env['hotel.restaurant.kitchen.order.tickets']
        rest_order_list_obj = self.env['hotel.restaurant.order.list']
        for order in self:
            table_ids = [x.id for x in order.table_no]
            line_data = {
                'orderno': order.order_number,
                'resno': order.reservationno.reservation_id,
                'kot_date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                'w_name': order.waitername.name,
                'tableno': [(6, 0, table_ids)],
                }
            kot_obj = order_tickets_obj.browse(self.kitchen_id)
            kot_obj.write(line_data)
            for order_line in order.order_list:
                if order_line.id not in order.rest_id.ids:
                    kot_data1 = order_tickets_obj.create(line_data)
                    self.kitchen_id = kot_data1.id
                    o_line = {
                        'kot_order_list': kot_data1.id,
                        'name': order_line.name.id,
                        'item_qty': order_line.item_qty,
                        'item_rate': order_line.item_rate
                    }
                    self.rest_id = [(4, order_line.id)]
                    rest_order_list_obj.create(o_line)
        return True

    @api.multi
    def done_kot(self):
        """
        This method is used to change the state
        to done of the hotel reservation order
        ----------------------------------------
        @param self: object pointer
        """
        hotel_folio_obj = self.env['hotel.folio']
        hsl_obj = self.env['hotel.service.line']
        so_line_obj = self.env['sale.order.line']
        for order_obj in self:
                hotelfolio = order_obj.folio_id.order_id.id
                if order_obj.folio_id:
                    for order1 in order_obj.order_list:
                        values = {'order_id': hotelfolio,
                                  'name': order1.name.name,
                                  'product_id': order1.name.product_id.id,
                                  'product_uom_qty': order1.item_qty,
                                  'price_unit': order1.item_rate,
                                  'price_subtotal': order1.price_subtotal,
                                  }
                        sol_rec = so_line_obj.create(values)
                        hsl_obj.create({'folio_id': order_obj.folio_id.id,
                                        'service_line_id': sol_rec.id})
                        hf_rec = hotel_folio_obj.browse(order_obj.folio_id.id)
                        hf_rec.write({'hotel_reservation_order_ids':
                                      [(4, order_obj.id)]})
                if order_obj.reservationno:
                    order_obj.reservationno.write({'state': 'done'})
        self.state = 'done'
        return True

    _name = "hotel.reservation.order"
    _description = "Reservation Order"

    _rec_name = "order_number"

    order_number = fields.Char('Order No', size=64, readonly=True)
    reservationno = fields.Many2one('hotel.restaurant.reservation',
                                    'Reservation No')
    date1 = fields.Datetime('Date', required=True,
                            default=(lambda *a:
                                     time.strftime
                                     (DEFAULT_SERVER_DATETIME_FORMAT)))
    waitername = fields.Many2one('res.partner', 'Waiter Name')
    table_no = fields.Many2many('hotel.restaurant.tables', 'temp_table4',
                                'table_no', 'name', 'Table Number')
    order_list = fields.One2many('hotel.restaurant.order.list', 'o_l',
                                 'Order List')
    tax = fields.Float('Tax (%) ', size=64)
    amount_subtotal = fields.Float(compute='_sub_total', method=True,
                                   string='Subtotal')
    amount_total = fields.Float(compute='_total', method=True,
                                string='Total')
    kitchen_id = fields.Integer('Kitchen id')
    rest_id = fields.Many2many('hotel.restaurant.order.list', 'reserv_id',
                               'kitchen_id', 'res_kit_ids', "Rest")
    state = fields.Selection([('draft', 'Draft'), ('order', 'Order Created'),
                              ('done', 'Done')], 'State', index=True,
                             required=True, readonly=True,
                             default=lambda * a: 'draft')
    folio_id = fields.Many2one('hotel.folio', string='Folio No')
    is_folio = fields.Boolean('Is a Hotel Guest??', help='is customer reside'
                              'in hotel or not')

    @api.model
    def create(self, vals):
        """
        Overrides orm create method.
        @param self: The object pointer
        @param vals: dictionary of fields value.
        """
        if not vals:
            vals = {}
        if self._context is None:
            self._context = {}
        seq_obj = self.env['ir.sequence']
        res_oder = seq_obj.next_by_code('hotel.reservation.order') or 'New'
        vals['order_number'] = res_oder
        return super(HotelReservationOrder, self).create(vals)
class AeatCheckSiiResult(models.Model):
    _name = 'aeat.check.sii.result'

    RESULTS = [('ConDatos', 'Con datos'),
               ('SinDatos', 'Sin datos')]

    RECONCILE = [('1', 'No contrastable'),
                 ('2', 'En proceso de contraste'),
                 ('3', 'No contrastada'),
                 ('4', 'Parcialmente contrastada'),
                 ('5', 'Contrastada')]

    check_date = fields.Datetime(string='Date check')
    result_query = fields.Selection(RESULTS, string='Type')
    vat = fields.Char(string='NIF')
    other_id = fields.Char(string='Other identifier')
    invoice_number = fields.Char(string='Invoice number')
    invoice_date = fields.Date(string='Invoice date')
    invoice_type = fields.Char(string='Invoice type')
    refund_type = fields.Selection(
        selection=[('S', 'By substitution'), ('I', 'By differences')],
        string="Refund Type")
    registration_key = fields.Many2one(
        comodel_name='aeat.sii.mapping.registration.keys',
        string="Registration key")
    amount_total = fields.Float(
        string='Amount total', digits=dp.get_precision('Account'))
    description = fields.Text(string='Description')
    name = fields.Char(string='Company Name')
    vat_partner = fields.Char(string='NIF')
    other_id_partner = fields.Char(string='Other identifier')
    vat_presenter = fields.Char(string='NIF')
    timestamp_presentation = fields.Datetime(string='Timestamp Presentation')
    csv = fields.Char(string='CSV')
    reconcile_state = fields.Selection(RECONCILE, string='Reconcile State')
    reconcile_timestamp = fields.Datetime(string='Reconcile Timestamp')
    timestamp_last = fields.Datetime(string='Timestamp Last Update ')
    sent_state = fields.Char(string='State')
    error_code = fields.Char(string='Error code')
    description_error = fields.Text(string='Description Error')
    reconcile_description = fields.Text(string='Reconcile description')
    registry_error_description = fields.Char(
        string='Registry Error Description')
    invoice_id = fields.Many2one(comodel_name='account.move',
                                 string='Invoice')

    def _get_data(self, model_id, res, model):
        data = {}
        key = ''
        key_type = ''
        if model == 'account.move':
            if model_id.type in ('out_invoice', 'out_refund'):
                data = res['RegistroRespuestaConsultaLRFacturasEmitidas'][0]
                key = 'DatosFacturaEmitida'
                key_type = 'sale'
            if model_id.type in ('in_invoice', 'in_refund'):
                data = res['RegistroRespuestaConsultaLRFacturasRecibidas'][0]
                key = 'DatosFacturaRecibida'
                key_type = 'purchase'
        return data, key, key_type

    def _prepare_vals(self, model_id, res, fault, model):
        vals = {
            'check_date': fields.Datetime.now()
        }
        if model == 'account.move':
            vals['invoice_id'] = model_id.id
        if fault:
            vals['registry_error_description'] = fault
        else:
            vals['result_query'] = res['ResultadoConsulta']
            data, key, key_type = self._get_data(model_id, res, model)
        if 'result_query' in vals and vals['result_query'] == 'ConDatos':
            key_obj = self.env['aeat.sii.mapping.registration.keys']
            vals['vat'] = data['IDFactura']['IDEmisorFactura']['NIF']
            if model == 'account.move':
                if model_id.type in ('in_invoice', 'in_refund'):
                    vals['other_id'] = data[
                        'IDFactura']['IDEmisorFactura']['IDOtro']
            vals['invoice_number'] = data['IDFactura']['NumSerieFacturaEmisor']
            date = datetime.datetime.strptime(
                data['IDFactura']['FechaExpedicionFacturaEmisor'],
                '%d-%m-%Y')
            new_date = datetime.datetime.strftime(
                date, '%Y-%m-%d')
            vals['invoice_date'] = new_date
            vals['invoice_type'] = data[key]['TipoFactura']
            vals['refund_type'] = data[key]['TipoRectificativa']
            registration_key = key_obj.search(
                [('code', '=', data[key][
                    'ClaveRegimenEspecialOTrascendencia']),
                 ('type', '=', key_type)]
            )
            vals['registration_key'] = registration_key.id
            vals['amount_total'] = data[key]['ImporteTotal']
            vals['description'] = data[key]['DescripcionOperacion']
            if 'Contraparte' in data[key] and data[key]['Contraparte']:
                vals['name'] = data[key]['Contraparte']['NombreRazon']
                vals['vat_partner'] = data[key]['Contraparte']['NIF']
                vals['other_id_partner'] = data[key]['Contraparte']['IDOtro']
            vals['vat_presenter'] = data['DatosPresentacion']['NIFPresentador']
            date = datetime.datetime.strptime(
                data['DatosPresentacion']['TimestampPresentacion'],
                '%d-%m-%Y %H:%M:%S')
            new_date = datetime.datetime.strftime(
                date, '%Y-%m-%d %H:%M:%S')
            vals['timestamp_presentation'] = new_date
            vals['csv'] = data['DatosPresentacion']['CSV']
            vals['reconcile_state'] = data['EstadoFactura']['EstadoCuadre']
            model_id.sii_reconcile_state = vals['reconcile_state']
            if 'TimestampEstadoCuadre' in data['EstadoFactura']:
                if data['EstadoFactura']['TimestampEstadoCuadre']:
                    date = datetime.datetime.strptime(
                        data['EstadoFactura']['TimestampEstadoCuadre'],
                        '%d-%m-%Y %H:%M:%S')
                    new_date = datetime.datetime.strftime(
                        date, '%Y-%m-%d %H:%M:%S')
                    vals['reconcile_timestamp'] = new_date
            date = datetime.datetime.strptime(
                data['EstadoFactura']['TimestampUltimaModificacion'],
                '%d-%m-%Y %H:%M:%S')
            new_date = datetime.datetime.strftime(
                date, '%Y-%m-%d %H:%M:%S')
            vals['timestamp_last'] = new_date
            vals['sent_state'] = data['EstadoFactura']['EstadoRegistro']
            vals['error_code'] = data['EstadoFactura']['CodigoErrorRegistro']
            vals['description_error'] = data[
                'EstadoFactura']['DescripcionErrorRegistro']
            vals['reconcile_description'] = data['DatosDescuadreContraparte']
        return vals

    def create_result(self, model_id, res, fault, model):
        vals = self._prepare_vals(model_id, res, fault, model)
        self.create(vals)
Exemple #20
0
class HotelRestaurantReservation(models.Model):

    @api.multi
    def create_order(self):
        """
        This method is for create a new order for hotel restaurant
        reservation .when table is booked and create order button is
        clicked then this method is called and order is created.you
        can see this created order in "Orders"
        ------------------------------------------------------------
        @param self: The object pointer
        @return: new record set for hotel restaurant reservation.
        """
        proxy = self.env['hotel.reservation.order']
        for record in self:
            table_ids = [tableno.id for tableno in record.tableno]
            values = {
                'reservationno': record.id,
                'date1': record.start_date,
                'folio_id': record.folio_id.id,
                'table_no': [(6, 0, table_ids)],
                'is_folio': record.is_folio,
            }
            proxy.create(values)
        self.state = 'order'
        return True

    @api.onchange('cname')
    def onchange_partner_id(self):
        '''
        When Customer name is changed respective adress will display
        in Adress field
        @param self: object pointer
        '''
        if not self.cname:
            self.partner_address_id = False
        else:
            addr = self.cname.address_get(['default'])
            self.partner_address_id = addr['default']

    @api.onchange('folio_id')
    def get_folio_id(self):
        '''
        When you change folio_id, based on that it will update
        the cname and room_number as well
        ---------------------------------------------------------
        @param self: object pointer
        '''
        for rec in self:
            self.cname = False
            self.room_no = False
            if rec.folio_id:
                self.cname = rec.folio_id.partner_id.id
                if rec.folio_id.room_lines:
                    self.room_no = rec.folio_id.room_lines[0].product_id.id

    @api.multi
    def action_set_to_draft(self):
        """
        This method is used to change the state
        to draft of the hotel restaurant reservation
        --------------------------------------------
        @param self: object pointer
        """
        self.state = 'draft'
        return True

    @api.multi
    def table_reserved(self):
        """
        when CONFIRM BUTTON is clicked this method is called for
        table reservation
        @param self: The object pointer
        @return: change a state depending on the condition
        """
        for reservation in self:
            self._cr.execute("select count(*) from "
                             "hotel_restaurant_reservation as hrr "
                             "inner join reservation_table as rt on \
                             rt.reservation_table_id = hrr.id "
                             "where (start_date,end_date)overlaps\
                             ( timestamp %s , timestamp %s ) "
                             "and hrr.id<> %s and state != 'done'"
                             "and rt.name in (select rt.name from \
                             hotel_restaurant_reservation as hrr "
                             "inner join reservation_table as rt on \
                             rt.reservation_table_id = hrr.id "
                             "where hrr.id= %s) ",
                             (reservation.start_date, reservation.end_date,
                              reservation.id, reservation.id))
            res = self._cr.fetchone()
            roomcount = res and res[0] or 0.0
            if len(reservation.tableno.ids) == 0:
                raise except_orm(_('Warning'),
                                 _('Please Select Tables For Reservation'))
            if roomcount:
                raise except_orm(_('Warning'), _('You tried to confirm \
                reservation with table those already reserved in this \
                reservation period'))
            else:
                self.state = 'confirm'
            return True

    @api.multi
    def table_cancel(self):
        """
        This method is used to change the state
        to cancel of the hotel restaurant reservation
        --------------------------------------------
        @param self: object pointer
        """
        self.state = 'cancel'
        return True

    @api.multi
    def table_done(self):
        """
        This method is used to change the state
        to done of the hotel restaurant reservation
        --------------------------------------------
        @param self: object pointer
        """
        self.state = 'done'
        return True

    _name = "hotel.restaurant.reservation"
    _description = "Includes Hotel Restaurant Reservation"
    _rec_name = "reservation_id"

    reservation_id = fields.Char('Reservation No', size=64, readonly=True,
                                 index=True)
    room_no = fields.Many2one('product.product', string='Room No', size=64,
                              index=True)
    folio_id = fields.Many2one('hotel.folio', string='Folio No')
    start_date = fields.Datetime('Start Time', required=True,
                                 default=(lambda *a:
                                          time.strftime
                                          (DEFAULT_SERVER_DATETIME_FORMAT)))
    end_date = fields.Datetime('End Time', required=True)
    cname = fields.Many2one('res.partner', string='Customer Name', size=64,
                            required=True, index=True)
    partner_address_id = fields.Many2one('res.partner', string='Address')
    tableno = fields.Many2many('hotel.restaurant.tables',
                               relation='reservation_table',
                               index=True,
                               column1='reservation_table_id',
                               column2='name', string='Table Number',
                               help="Table reservation detail. ")
    state = fields.Selection([('draft', 'Draft'), ('confirm', 'Confirmed'),
                              ('done', 'Done'), ('cancel', 'Cancelled'),
                              ('order', 'Order Created')], 'state',
                             index=True, required=True, readonly=True,
                             default=lambda * a: 'draft')
    is_folio = fields.Boolean('Is a Hotel Guest??')

    @api.model
    def create(self, vals):
        """
        Overrides orm create method.
        @param self: The object pointer
        @param vals: dictionary of fields value.
        """
        if not vals:
            vals = {}
        if self._context is None:
            self._context = {}
        seq_obj = self.env['ir.sequence']
        resrve = seq_obj.next_by_code('hotel.restaurant.reservation') or 'New'
        vals['reservation_id'] = resrve
        return super(HotelRestaurantReservation, self).create(vals)

    @api.constrains('start_date', 'end_date')
    def check_start_dates(self):
        '''
        This method is used to validate the start_date and end_date.
        -------------------------------------------------------------
        @param self: object pointer
        @return: raise a warning depending on the validation
        '''
        if self.start_date >= self.end_date:
            raise ValidationError(_('Start Date Should be less \
            than the End Date!'))
class SaasServerClient(models.Model):
    _name = 'saas_server.client'
    _inherit = ['mail.thread', 'saas_base.client']

    name = fields.Char('Database name', readonly=True, required=True)
    client_id = fields.Char('Database UUID', readonly=True, index=True)
    expiration_datetime = fields.Datetime(readonly=True)
    state = fields.Selection([('template', 'Template'), ('draft', 'New'),
                              ('open', 'In Progress'),
                              ('cancelled', 'Cancelled'),
                              ('pending', 'Pending'), ('deleted', 'Deleted')],
                             'State',
                             default='draft',
                             track_visibility='onchange')
    host = fields.Char('Host')

    _sql_constraints = [
        ('client_id_uniq', 'unique (client_id)',
         'client_id should be unique!'),
    ]

    @api.multi
    def create_database(self, template_db=None, demo=False, lang='en_US'):
        self.ensure_one()
        new_db = self.name
        res = {}
        if template_db:
            db._drop_conn(self.env.cr, template_db)
            db.exp_duplicate_database(template_db, new_db)
        else:
            password = random_password()
            res.update({'superuser_password': password})
            db.exp_create_database(new_db, demo, lang, user_password=password)
        self.state = 'open'
        return res

    @api.multi
    def registry(self, new=False, **kwargs):
        self.ensure_one()
        m = odoo.modules.registry.Registry
        return m.new(self.name, **kwargs)

    @api.multi
    def install_addons(self, addons, is_template_db):
        self.ensure_one()
        addons = set(addons)
        addons.add('mail_delete_sent_by_footer')  # debug
        if is_template_db:
            addons.add('auth_oauth')
            addons.add('saas_client')
        else:
            addons.add('saas_client')
        if not addons:
            return
        with self.registry().cursor() as cr:
            env = api.Environment(cr, SUPERUSER_ID, self._context)
            self._install_addons(env, addons)

    @api.multi
    def disable_mail_servers(self):
        '''
        disables mailserver on db to stop it from sending and receiving mails
        '''
        # let's disable incoming mail servers
        self.ensure_one()
        incoming_mail_servers = self.env['fetchmail.server'].search([])
        if len(incoming_mail_servers):
            incoming_mail_servers.write({'active': False})

        # let's disable outgoing mailservers too
        outgoing_mail_servers = self.env['ir.mail_server'].search([])
        if len(outgoing_mail_servers):
            outgoing_mail_servers.write({'active': False})

    @api.multi
    def _install_addons(self, client_env, addons):
        for addon in client_env['ir.module.module'].search([('name', 'in',
                                                             list(addons))]):
            addon.button_install()

    @api.multi
    def update_registry(self):
        self.ensure_one()
        self.registry(new=True, update_module=True)

    @api.multi
    def prepare_database(self, **kwargs):
        self.ensure_one()
        with self.registry().cursor() as cr:
            env = api.Environment(cr, SUPERUSER_ID, self._context)
            self._prepare_database(env, **kwargs)

    @api.model
    def _config_parameters_to_copy(self):
        return [
            'saas_client.ab_location', 'saas_client.ab_register',
            'saas_client.saas_dashboard'
        ]

    @api.multi
    def _prepare_database(self,
                          client_env,
                          owner_user=None,
                          is_template_db=False,
                          addons=None,
                          access_token=None,
                          tz=None,
                          server_requests_scheme='http'):
        self.ensure_one()
        if not addons:
            addons = []
        client_id = self.client_id

        # update saas_server.client state
        if is_template_db:
            self.state = 'template'

        # set tz
        if tz:
            client_env['res.users'].search([]).write({'tz': tz})
            client_env['ir.default'].set('res.partner', 'tz', tz)

        # update database.uuid
        client_env['ir.config_parameter'].sudo().set_param(
            'database.uuid', client_id)

        # copy configs
        for key in self._config_parameters_to_copy():
            value = self.env['ir.config_parameter'].sudo().get_param(
                key, default='')
            client_env['ir.config_parameter'].sudo().set_param(key, value)

        # set web.base.url config
        client_env['ir.config_parameter'].sudo().set_param(
            'web.base.url', '%s://%s' % (server_requests_scheme, self.host))

        # saas_client must be already installed
        oauth_provider = client_env.ref('saas_client.saas_oauth_provider')
        if is_template_db:
            # copy auth provider from saas_server
            saas_oauth_provider = self.env.ref(
                'saas_server.saas_oauth_provider')

            oauth_provider_data = {'enabled': False, 'client_id': client_id}
            for attr in [
                    'name', 'auth_endpoint', 'scope', 'validation_endpoint',
                    'data_endpoint', 'css_class', 'body', 'enabled',
                    'local_host', 'local_port'
            ]:
                oauth_provider_data[attr] = getattr(saas_oauth_provider, attr)
            oauth_provider = client_env.ref('saas_client.saas_oauth_provider')
            oauth_provider.write(oauth_provider_data)
        else:
            oauth_provider.client_id = client_id

        # prepare users
        OWNER_TEMPLATE_LOGIN = '******'
        user = None
        if is_template_db:
            client_env['res.users'].create({
                'login': OWNER_TEMPLATE_LOGIN,
                'name': 'NAME',
                'email': '*****@*****.**',
            })

            client_env['res.users'].browse(SUPERUSER_ID).write({
                'oauth_provider_id':
                oauth_provider.id,
                'oauth_uid':
                SUPERUSER_ID,
                'oauth_access_token':
                access_token
            })
        else:
            domain = [('login', '=', OWNER_TEMPLATE_LOGIN)]
            res = client_env['res.users'].search(domain)
            if res:
                user = res[0]
                client_env['ir.config_parameter'].sudo().set_param(
                    'res.users.owner', user.id)

            portal_owner_uid = owner_user.pop('user_id')
            res = client_env['res.users'].search([('oauth_uid', '=',
                                                   portal_owner_uid)])
            if res:
                # user already exists (e.g. administrator)
                user = res[0]
            if not user:
                user = client_env['res.users'].browse(SUPERUSER_ID)

            vals = owner_user
            vals.update({
                'oauth_provider_id':
                oauth_provider.id,
                'oauth_uid':
                portal_owner_uid,
                'oauth_access_token':
                access_token,
                'country_id':
                owner_user.get('country_id')
                and self.env['res.country'].browse(owner_user['country_id'])
                and self.env['res.country'].browse(
                    owner_user['country_id']).id,
            })

            user.write(vals)

    @api.model
    def update_all(self):
        self.sudo().search([]).update()

    @api.multi
    def update_one(self):
        for server in self:
            server.sudo().update()

    @api.multi
    def update(self):
        for record in self:
            try:
                registry = record.registry()
                with registry.cursor() as client_cr:
                    client_env = api.Environment(client_cr, SUPERUSER_ID,
                                                 record._context)
                    data = record._get_data(client_env, record.client_id)
                    record.write(data)
            except psycopg2.OperationalError:
                if record.state != 'draft':
                    record.state = 'deleted'
                return

    @api.multi
    def _get_data(self, client_env, check_client_id):
        self.ensure_one()
        client_id = client_env['ir.config_parameter'].sudo().get_param(
            'database.uuid')
        if check_client_id != client_id:
            return {'state': 'deleted'}
        users = client_env['res.users'].search([('share', '=', False),
                                                ('id', '!=', SUPERUSER_ID)])
        param_obj = client_env['ir.config_parameter']
        max_users = param_obj.sudo().get_param('saas_client.max_users',
                                               '0').strip()
        suspended = param_obj.sudo().get_param('saas_client.suspended',
                                               '0').strip()
        total_storage_limit = param_obj.sudo().get_param(
            'saas_client.total_storage_limit', '0').strip()
        users_len = len(users)
        data_dir = odoo.tools.config['data_dir']

        file_storage = get_size('%s/filestore/%s' % (data_dir, self.name))
        file_storage = int(file_storage / (1024 * 1024))

        client_env.cr.execute("select pg_database_size('%s')" % self.name)
        db_storage = client_env.cr.fetchone()[0]
        db_storage = int(db_storage / (1024 * 1024))

        data = {
            'client_id': client_id,
            'users_len': users_len,
            'max_users': max_users,
            'file_storage': file_storage,
            'db_storage': db_storage,
            'total_storage_limit': total_storage_limit,
        }
        if suspended == '0' and self.state in ('pending', 'deleted'):
            data.update({'state': 'open'})
        if suspended == '1' and self.state in ('open', 'deleted'):
            data.update({'state': 'pending'})
        return data

    @api.multi
    def upgrade_database(self, **kwargs):
        for record in self.filtered(lambda record: record.state != "deleted"):
            with record.registry().cursor() as cr:
                env = api.Environment(cr, SUPERUSER_ID, record._context)
                return record._upgrade_database(env, **kwargs)

    @api.multi
    def _upgrade_database(self, client_env, data):
        self.ensure_one()
        # "data" comes from saas_portal/models/wizard.py::upgrade_database
        post = data
        module = client_env['ir.module.module']
        print(('_upgrade_database', data))
        res = {}

        # 0. Update module list
        update_list = post.get('update_addons_list', False)
        if update_list:
            module.update_list()

        # 1. Update addons
        update_addons = post.get('update_addons', [])
        if update_addons:
            module.search([('name', 'in', update_addons)
                           ]).button_immediate_upgrade()

        # 2. Install addons
        install_addons = post.get('install_addons', [])
        if install_addons:
            module.search([('name', 'in', install_addons)
                           ]).button_immediate_install()

        # 3. Uninstall addons
        uninstall_addons = post.get('uninstall_addons', [])
        if uninstall_addons:
            module.search([('name', 'in', uninstall_addons)
                           ]).button_immediate_uninstall()

        # 4. Run fixes
        fixes = post.get('fixes', [])
        for model, method in fixes:
            getattr(self.env[model], method)()

        # 5. update parameters
        params = post.get('params', [])
        for obj in params:
            if obj['key'] == 'saas_client.expiration_datetime':
                self.expiration_datetime = obj['value']
            if obj['key'] == 'saas_client.trial' and obj['value'] == 'False':
                self.trial = False
            # groups = []
            # if obj.get('hidden'):
            #     groups = ['saas_client.group_saas_support']
            client_env['ir.config_parameter'].sudo().set_param(
                obj['key'], obj['value'] or ' ')

        # 6. Access rights
        access_owner_add = post.get('access_owner_add', [])
        owner_id = client_env['ir.config_parameter'].sudo().get_param(
            'res.users.owner', 0)
        owner_id = int(owner_id)
        if not owner_id:
            res['owner_id'] = "Owner's user is not found"
        if access_owner_add and owner_id:
            res['access_owner_add'] = []
            for g_ref in access_owner_add:
                g = client_env.ref(g_ref, raise_if_not_found=False)
                if not g:
                    res['access_owner_add'].append('group not found: %s' %
                                                   g_ref)
                    continue
                g.write({'users': [(4, owner_id, 0)]})
        access_remove = post.get('access_remove', [])
        if access_remove:
            res['access_remove'] = []
            for g_ref in access_remove:
                g = client_env.ref(g_ref, raise_if_not_found=False)
                if not g:
                    res['access_remove'].append('group not found: %s' % g_ref)
                    continue
                users = []
                for u in g.users:
                    if u.id != SUPERUSER_ID:
                        users.append((3, u.id, 0))
                g.write({'users': users})

        # 7. Configure outgoing mail
        data = post.get('configure_outgoing_mail', [])
        for mail_conf in data:
            ir_mail_server = client_env['ir.mail_server']
            ir_mail_server.create({
                'name': 'mailgun',
                'smtp_host': 'smtp.mailgun.org',
                'smtp_user': mail_conf['smtp_login'],
                'smtp_pass': mail_conf['smtp_password']
            })

        # 8.Limit number of records
        model_obj = client_env['ir.model']
        base_limit_records_number_obj = client_env['base.limit.records_number']
        data = post.get('limit_nuber_of_records', [])
        for limit_line in data:
            model = model_obj.search([('model', '=', limit_line['model'])])
            if model:
                limit_record = base_limit_records_number_obj.search([
                    ('model_id', '=', model.id)
                ])
                if limit_record:
                    limit_record.update({
                        'domain':
                        limit_line['domain'],
                        'max_records':
                        limit_line['max_records'],
                    })
                else:
                    base_limit_records_number_obj.create({
                        'name':
                        'limit_' + limit_line['model'],
                        'model_id':
                        model.id,
                        'domain':
                        limit_line['domain'],
                        'max_records':
                        limit_line['max_records'],
                    })
            else:
                res['limit'] = "there is no model named %s" % limit_line[
                    'model']

        return res

    @api.model
    def _cron_delete_expired_databases(self):
        now = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)

        res = self.search([('state', 'not in', ['deleted', 'template']),
                           ('expiration_datetime', '<=', now),
                           ('trial', '=', True)])
        _logger.info('delete_expired_databases %s', res)
        res.delete_database()

    @api.multi
    def delete_database(self):
        for record in self:
            db.exp_drop(self.name)
        self.write({'state': 'deleted'})

    @api.multi
    def rename_database(self, new_dbname):
        for record in self:
            db.exp_rename(self.name, new_dbname)
        self.name = new_dbname

    @api.model
    def _transport_backup(self, dump_db, filename=None):
        '''
        backup transport agents should override this
        '''
        raise exceptions.Warning(
            _('''Transport agent has not been configured. You need either
              install one of saas_server_backup_* or remove
              saas_portal_backup'''))

    @api.multi
    def backup_database(self):
        res = []
        for database_obj in self:
            data = {}
            data['name'] = database_obj.name

            filename = "%(db_name)s_%(timestamp)s.zip" % {
                'db_name': database_obj.name,
                'timestamp': datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%SZ")
            }

            def dump_db(stream):
                return db.dump_db(database_obj.name, stream)

            try:
                database_obj._transport_backup(dump_db, filename=filename)
                data['status'] = 'success'
            except Exception as e:
                _logger.exception(
                    'An error happened during database %s backup' %
                    (database_obj.name))
                data['status'] = 'fail'
                data['message'] = str(e)

            res.append(data)

        return res

    @api.model
    def restart_server(self):
        odoo.service.server.restart()
        return True
Exemple #22
0
class PosOrderLineCanceled(models.Model):
    _name = "pos.order.line.canceled"
    _description = "Order Line Canceled"
    _rec_name = "product_id"

    def _order_cancel_line_fields(self, line):
        if line and 'tax_ids' not in line[2]:
            product = self.env['product.product'].browse(line[2]['product_id'])
            line[2]['tax_ids'] = [(6, 0, [x.id for x in product.taxes_id])]
        return line

    product_id = fields.Many2one('product.product', string='Product', domain=[('sale_ok', '=', True)], required=True, change_default=True, readonly=True)
    discount = fields.Float(string='Discount (%)', digits=0, default=0.0, readonly=True)
    price_unit = fields.Float(string='Unit Price', digits=0, readonly=True)
    user_id = fields.Many2one(comodel_name='res.users', string='Salesman', help="Person who removed order line", default=lambda self: self.env.uid, readonly=True)
    qty = fields.Float('Cancelled Quantity', default=1, readonly=True)
    current_qty = fields.Float('Remainder', default=0, readonly=True)
    reason = fields.Text(string="Reasons as Text", help="The Reason of Line Canceled", readonly=True)
    order_id = fields.Many2one('pos.order', string='Order Ref', ondelete='cascade', readonly=True)
    pack_lot_ids = fields.One2many('pos.pack.operation.lot', 'pos_order_line_id', string='Lot/serial Number', readonly=True)
    tax_ids = fields.Many2many('account.tax', string='Taxes', readonly=True)
    canceled_date = fields.Datetime(string='Cancelation Time', readonly=True, default=fields.Datetime.now)
    price_subtotal = fields.Float(compute='_compute_amount_line_all', digits=0, string='Subtotal w/o Tax', store=True)
    price_subtotal_incl = fields.Float(compute='_compute_amount_line_all', digits=0, string='Subtotal', store=True)
    cancelled_reason_ids = fields.Many2many('pos.cancelled_reason', 'reason_cancelled_lines_rel',
                                            'canceled_line_id', 'cancelled_reason_id', string='Predefined Reasons')

    # the absolute_discount field is needed for compatibility
    # with <https://www.odoo.com/apps/modules/10.0/pos_orderline_absolute_discount/> module
    absolute_discount = fields.Float(string='Discount (abs)', digits=0, default=0.0, readonly=True)

    @api.depends('price_unit', 'tax_ids', 'qty', 'discount', 'product_id')
    def _compute_amount_line_all(self):
        for line in self:
            currency = line.order_id.pricelist_id.currency_id
            taxes = line.tax_ids.filtered(lambda tax: tax.company_id.id == line.order_id.company_id.id)
            fiscal_position_id = line.order_id.fiscal_position_id
            if fiscal_position_id:
                taxes = fiscal_position_id.map_tax(taxes, line.product_id, line.order_id.partner_id)
            price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
            line.price_subtotal = line.price_subtotal_incl = price * line.qty
            if taxes:
                taxes = taxes.compute_all(price, currency, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)
                line.price_subtotal = taxes['total_excluded']
                line.price_subtotal_incl = taxes['total_included']

            line.price_subtotal = currency.round(line.price_subtotal)
            line.price_subtotal_incl = currency.round(line.price_subtotal_incl)

    @api.model
    def create(self, values):
        if values.get('canceled_date'):
            canceled_date = datetime.strptime(values.get('canceled_date'), "%d/%m/%Y %H:%M:%S")
            tz = pytz.timezone(self.env.user.tz) if self.env.user.tz else pytz.utc
            canceled_date = tz.localize(canceled_date)
            canceled_date = canceled_date.astimezone(pytz.utc)
            canceled_date = fields.Datetime.to_string(canceled_date)
            values['canceled_date'] = canceled_date
        if values.get('cancelled_reason_ids') and len(values.get('cancelled_reason_ids')) > 0:
            values['cancelled_reason_ids'] = [(4, id) for id in values.get('cancelled_reason_ids')]
        return super(PosOrderLineCanceled, self).create(values)
Exemple #23
0
class BlogPost(models.Model):
    _name = "blog.post"
    _description = "Blog Post"
    _inherit = ['mail.thread', 'website.seo.metadata', 'website.published.mixin']
    _order = 'id DESC'
    _mail_post_access = 'read'

    @api.multi
    def _compute_website_url(self):
        super(BlogPost, self)._compute_website_url()
        for blog_post in self:
            blog_post.website_url = "/blog/%s/post/%s" % (slug(blog_post.blog_id), slug(blog_post))

    @api.multi
    def _compute_ranking(self):
        res = {}
        for blog_post in self:
            age = datetime.now() - fields.Datetime.from_string(blog_post.post_date)
            res[blog_post.id] = blog_post.visits * (0.5 + random.random()) / max(3, age.days)
        return res

    def _default_content(self):
        return '''
            <section class="s_text_block">
                <div class="container">
                    <div class="row">
                        <div class="col-md-12 mb16 mt16">
                            <p class="o_default_snippet_text">''' + _("Start writing here...") + '''</p>
                        </div>
                    </div>
                </div>
            </section>
        '''

    name = fields.Char('Title', required=True, translate=True, default='')
    subtitle = fields.Char('Sub Title', translate=True)
    author_id = fields.Many2one('res.partner', 'Author', default=lambda self: self.env.user.partner_id)
    active = fields.Boolean('Active', default=True)
    cover_properties = fields.Text(
        'Cover Properties',
        default='{"background-image": "none", "background-color": "oe_black", "opacity": "0.2", "resize_class": ""}')
    blog_id = fields.Many2one('blog.blog', 'Blog', required=True, ondelete='cascade')
    tag_ids = fields.Many2many('blog.tag', string='Tags')
    content = fields.Html('Content', default=_default_content, translate=html_translate, sanitize_attributes=False)
    teaser = fields.Text('Teaser', compute='_compute_teaser', inverse='_set_teaser')
    teaser_manual = fields.Text(string='Teaser Content')

    website_message_ids = fields.One2many(
        'mail.message', 'res_id',
        domain=lambda self: [
            '&', '&', ('model', '=', self._name), ('message_type', '=', 'comment'), ('path', '=', False)
        ],
        string='Website Messages',
        help="Website communication history",
    )
    # creation / update stuff
    create_date = fields.Datetime('Created on', index=True, readonly=True)
    published_date = fields.Datetime('Published Date')
    post_date = fields.Datetime('Published date', compute='_compute_post_date', inverse='_set_post_date', store=True)
    create_uid = fields.Many2one('res.users', 'Created by', index=True, readonly=True)
    write_date = fields.Datetime('Last Modified on', index=True, readonly=True)
    write_uid = fields.Many2one('res.users', 'Last Contributor', index=True, readonly=True)
    author_avatar = fields.Binary(related='author_id.image_small', string="Avatar")
    visits = fields.Integer('No of Views')
    ranking = fields.Float(compute='_compute_ranking', string='Ranking')

    @api.multi
    @api.depends('content', 'teaser_manual')
    def _compute_teaser(self):
        for blog_post in self:
            if blog_post.teaser_manual:
                blog_post.teaser = blog_post.teaser_manual
            else:
                content = html2plaintext(blog_post.content).replace('\n', ' ')
                blog_post.teaser = ' '.join(filter(None, content.split(' '))[:50]) + '...'

    @api.multi
    def _set_teaser(self):
        for blog_post in self:
            blog_post.teaser_manual = blog_post.teaser

    @api.multi
    @api.depends('create_date', 'published_date')
    def _compute_post_date(self):
        for blog_post in self:
            if blog_post.published_date:
                blog_post.post_date = blog_post.published_date
            else:
                blog_post.post_date = blog_post.create_date

    @api.multi
    def _set_post_date(self):
        for blog_post in self:
            blog_post.published_date = blog_post.post_date

    def _check_for_publication(self, vals):
        if vals.get('website_published'):
            for post in self:
                post.blog_id.message_post_with_view(
                    'website_blog.blog_post_template_new_post',
                    subject=post.name,
                    values={'post': post},
                    subtype_id=self.env['ir.model.data'].sudo().xmlid_to_res_id('website_blog.mt_blog_blog_published'))
            return True
        return False

    @api.model
    def create(self, vals):
        post_id = super(BlogPost, self.with_context(mail_create_nolog=True)).create(vals)
        post_id._check_for_publication(vals)
        return post_id

    @api.multi
    def write(self, vals):
        self.ensure_one()
        if 'website_published' in vals and 'published_date' not in vals:
            if self.published_date <= fields.Datetime.now():
                vals['published_date'] = vals['website_published'] and fields.Datetime.now()
        result = super(BlogPost, self).write(vals)
        self._check_for_publication(vals)
        return result

    @api.multi
    def get_access_action(self):
        """ Instead of the classic form view, redirect to the post on website
        directly if user is an employee or if the post is published. """
        self.ensure_one()
        if self.env.user.share and not self.sudo().website_published:
            return super(BlogPost, self).get_access_action()
        return {
            'type': 'ir.actions.act_url',
            'url': '/blog/%s/post/%s' % (self.blog_id.id, self.id),
            'target': 'self',
            'res_id': self.id,
        }

    @api.multi
    def _notification_recipients(self, message, groups):
        groups = super(BlogPost, self)._notification_recipients(message, groups)

        for group_name, group_method, group_data in groups:
            group_data['has_button_access'] = True

        return groups

    @api.multi
    def message_get_message_notify_values(self, message, message_values):
        """ Override to avoid keeping all notified recipients of a comment.
        We avoid tracking needaction on post comments. Only emails should be
        sufficient. """
        if message.message_type == 'comment':
            return {
                'needaction_partner_ids': [],
            }
        return {}
class Don(models.Model):
    _name = 'crm.alima.don'
    _description = 'Dons'

    codeMedia = fields.Many2one('crm.alima.code.media',
                                string='code media',
                                ondelete='restrict')
    adhesion = fields.Selection(OUIOUNON, string="Adhesion")
    date = fields.Date(string="Date du don", required=True)
    forme_don = fields.Selection(FORME_DON_RECU,
                                 index=True,
                                 string='forme du don')
    nature_don = fields.Selection(NATURE_DON, string="Nature du don")
    liberalite = fields.Selection(LIBERALITE, string='Liberalite')
    moyen_paiment = fields.Selection(MOYEN_PAIEMENT,
                                     string="Moyen de paiement")
    mode_versement = fields.Selection(MODE_VERSEMENT,
                                      string="Mode de versement")
    commentaire = fields.Text(string="Commentaire")
    date_recep = fields.Date(string='Date de récéption du chèque')
    date_signature = fields.Date(string='Date de signature du chèque')
    date_remise = fields.Date(string='Date de remise du chèque')
    date_encais = fields.Date(string='Date d\'encaissement du chèque')
    montantEur = fields.Float(string="Montant du don en Euro")
    montantLettre = fields.Char(string="Montant en toutes lettres en euro",
                                compute='convNombre2lettres',
                                store=True)
    devis_origine = fields.Selection(DEVISE, string='Devis d\'origine')
    montant_dans_devise = fields.Float(
        string='montant dans la devise d\'origine', compute='tauxChange')
    taux_change = fields.Float(string='taux de change à la date du don',
                               default=1)
    nom_banque = fields.Char(string='Non banque')
    numCheque = fields.Char(string='Numero cheque')
    remise_globale = fields.Boolean(string="remise globale")
    montant_remise_globale = fields.Float(string="montant remise globale")
    plateforme_paiement = fields.Selection(PLATEFORME,
                                           string='plateforme de paiement')
    parametreRF = fields.Selection(RF, string='RF')
    NumRecuFiscal = fields.Char(string="numero reçu fiscal")
    dateEdition = fields.Date(string='date édition RF')
    dateEnvoi = fields.Date(string='Date envoi RF')
    prelevement_en_cours = fields.Boolean(string='Prelevement en cours')
    commentaire_autre = fields.Text(string="Commentaire")
    donateur = fields.Many2one('crm.alima.donateur',
                               string='donateur',
                               required=True)
    montantEurSave = fields.Float(string='Montant du don en Euro sauvegarder')
    valide = fields.Boolean(dafault=False, string='Validation creation')
    id_intersa = fields.Char(string='ID Intersa')
    url_intersa = fields.Char(compute='_get_url_intersa', string="Url Intersa")
    datetime_import = fields.Datetime(readonly=True, string="Date Import")

    @api.multi
    def copy(self, default):
        self.ensure_one()
        self.donateur.nombreDons += 1
        return super(Don, self).copy(default)

    @api.model
    @api.returns('self', lambda value: value.id)
    def create(self, vals):
        res = super(Don, self).create(vals)
        res.montantEurSave = res.montantEur
        rec = res.donateur
        if not rec.datePremierDon:
            rec.datePremierDon = res.date
            rec.montantPremierDon = res.montantEur
            rec.codemediaPremierDon = res.codeMedia
            rec.dateDernierDon = res.date
            rec.montantDernierDon = res.montantEur
            rec.codemediaDernierDon = res.codeMedia
            rec.cumulDonTotal = res.montantEur
            rec.nombreDons = 1
            rec.don_moy = rec.cumulDonTotal
            rec.idpremierdon = res.id
            rec.iddernierdon = res.id
        else:
            if datetime.strptime(res.date, '%Y-%m-%d') > datetime.strptime(
                    rec.dateDernierDon, '%Y-%m-%d'):
                rec.dateDernierDon = res.date
                rec.montantDernierDon = res.montantEur
                rec.codemediaDernierDon = res.codeMedia
                rec.cumulDonTotal = rec.cumulDonTotal + res.montantEur
                rec.nombreDons = rec.nombreDons + 1
                rec.don_moy = rec.cumulDonTotal / rec.nombreDons
                rec.iddernierdon = res.id
            if datetime.strptime(res.date, '%Y-%m-%d') < datetime.strptime(
                    rec.datePremierDon, '%Y-%m-%d'):
                rec.datePremierDon = res.date
                rec.montantPremierDon = res.montantEur
                rec.codemediaPremierDon = res.codeMedia
                rec.cumulDonTotal = rec.cumulDonTotal + res.montantEur
                rec.nombreDons = rec.nombreDons + 1
                rec.don_moy = rec.cumulDonTotal / rec.nombreDons
                rec.idpremierdon = res.id
        if not rec.datePremierDonHPA and res.mode_versement != 'avec prelevement':
            rec.datePremierDonHPA = res.date
            rec.montantPremierDonHPA = res.montantEur
            rec.codemediaPremierDonHPA = res.codeMedia
            rec.dateDernierDonHPA = res.date
            rec.montantDernierDonHPA = res.montantEur
            rec.codemediaDernierDonHPA = res.codeMedia
            rec.cumulDonHPA = res.montantEur
            rec.nombreDonsHPA = 1
            rec.don_moyHPA = rec.cumulDonHPA
            rec.idpremierdonHPA = res.id
            rec.iddernierdonsHPA = res.id
        elif res.mode_versement != 'avec prelevement':
            if datetime.strptime(res.date, '%Y-%m-%d') > datetime.strptime(
                    rec.dateDernierDonHPA, '%Y-%m-%d'):
                rec.dateDernierDonHPA = res.date
                rec.montantDernierDonHPA = res.montantEur
                rec.codemediaDernierDonHPA = res.codeMedia
                rec.cumulDonHPA = rec.cumulDonHPA + res.montantEur
                rec.nombreDonsHPA = rec.nombreDonsHPA + 1
                rec.don_moyHPA = rec.cumulDonHPA / rec.nombreDonsHPA
                rec.iddernierdonsHPA = res.id
            if datetime.strptime(res.date, '%Y-%m-%d') < datetime.strptime(
                    rec.datePremierDonHPA, '%Y-%m-%d'):
                rec.datePremierDonHPA = res.date
                rec.montantPremierDonHPA = res.montantEur
                rec.codemediaPremierDonHPA = res.codeMedia
                rec.cumulDonHPA = rec.cumulDonHPA + res.montantEur
                rec.nombreDonsHPA = rec.nombreDonsHPA + 1
                rec.don_moyHPA = rec.cumulDonHPA / rec.nombreDonsHPA
                rec.idpremierdonHPA = res.id
        if rec.nombreDons == 0:
            rec.freq_communication = 'Prospect'
        elif rec.nombreDons == 1:
            rec.freq_communication = 'Nouveau'
        if rec.nombreDons > 1:
            rec.freq_communication = 'Consolide'
        if rec.cumulDonTotal < 1000:
            rec.montant = 'Donor'
        elif rec.cumulDonTotal >= 1000 and rec.cumulDonTotal <= 5000:
            rec.montant = 'Middle donor'
        elif rec.cumulDonTotal > 5000 and rec.cumulDonTotal <= 50000:
            rec.montant = 'Global Leader'
        elif rec.cumulDonTotal > 50000:
            rec.montant = 'Investisseur Fondateur'
        return res

    @api.multi
    def unlink(self):
        my_array = array('i', [])
        for dons in self:
            test = bool()
            for idd in my_array:
                if idd == dons.donateur.id:
                    test = bool(1)
            if not test:
                my_array.append(dons.donateur.id)
        rep = super(Don, self).unlink()
        for iddonateur in my_array:
            rec = self.env['crm.alima.donateur'].search([('id', '=',
                                                          iddonateur)])
            rec.datePremierDon = ""
            rec.montantPremierDon = 0
            rec.codemediaPremierDon = ""
            rec.dateDernierDon = ""
            rec.montantDernierDon = 0
            rec.codemediaDernierDon = ""
            rec.cumulDonTotal = 0
            rec.nombreDons = 0
            rec.don_moy = 0
            rec.idpremierdon = 0
            rec.iddernierdon = 0
            rec.datePremierDonHPA = ""
            rec.montantPremierDonHPA = 0
            rec.codemediaPremierDonHPA = ""
            rec.dateDernierDonHPA = ""
            rec.montantDernierDonHPA = 0
            rec.codemediaDernierDonHPA = 0
            rec.cumulDonHPA = 0
            rec.nombreDonsHPA = 0
            rec.don_moyHPA = 0
            rec.idpremierdonHPA = 0
            rec.iddernierdonsHPA = 0
            for res in rec.dons:
                rec = res.donateur
                if not rec.datePremierDon:
                    rec.datePremierDon = res.date
                    rec.montantPremierDon = res.montantEur
                    rec.codemediaPremierDon = res.codeMedia
                    rec.dateDernierDon = res.date
                    rec.montantDernierDon = res.montantEur
                    rec.codemediaDernierDon = res.codeMedia
                    rec.cumulDonTotal = res.montantEur
                    rec.nombreDons = 1
                    rec.don_moy = rec.cumulDonTotal
                    rec.idpremierdon = res.id
                    rec.iddernierdon = res.id
                else:
                    rec.dateDernierDon = res.date
                    rec.montantDernierDon = res.montantEur
                    rec.codemediaDernierDon = res.codeMedia
                    rec.cumulDonTotal = rec.cumulDonTotal + res.montantEur
                    rec.nombreDons = rec.nombreDons + 1
                    rec.don_moy = rec.cumulDonTotal / rec.nombreDons
                    rec.iddernierdon = res.id
                if not rec.datePremierDonHPA and res.mode_versement != 'avec prelevement':
                    rec.datePremierDonHPA = res.date
                    rec.montantPremierDonHPA = res.montantEur
                    rec.codemediaPremierDonHPA = res.codeMedia
                    rec.dateDernierDonHPA = res.date
                    rec.montantDernierDonHPA = res.montantEur
                    rec.codemediaDernierDonHPA = res.codeMedia
                    rec.cumulDonHPA = res.montantEur
                    rec.nombreDonsHPA = 1
                    rec.don_moyHPA = rec.cumulDonHPA
                    rec.idpremierdonHPA = res.id
                    rec.iddernierdonsHPA = res.id
                elif res.mode_versement != 'avec prelevement':
                    rec.dateDernierDonHPA = res.date
                    rec.montantDernierDonHPA = res.montantEur
                    rec.codemediaDernierDonHPA = res.codeMedia
                    rec.cumulDonHPA = rec.cumulDonHPA + res.montantEur
                    rec.nombreDonsHPA = rec.nombreDonsHPA + 1
                    rec.don_moyHPA = rec.cumulDonHPA / rec.nombreDonsHPA
                    rec.iddernierdonsHPA = res.id
            if rec.nombreDons == 0:
                rec.freq_communication = 'Prospect'
            elif rec.nombreDons == 1:
                rec.freq_communication = 'Nouveau'
            if rec.nombreDons > 1:
                rec.freq_communication = 'Consolide'
            if rec.cumulDonTotal < 1000:
                rec.montant = 'Donor'
            elif rec.cumulDonTotal >= 1000 and rec.cumulDonTotal <= 5000:
                rec.montant = 'Middle donor'
            elif rec.cumulDonTotal > 5000 and rec.cumulDonTotal <= 50000:
                rec.montant = 'Global Leader'
            elif rec.cumulDonTotal > 50000:
                rec.montant = 'Investisseur Fondateur'
        return rep

    @api.multi
    def write(self, vals):
        montantEurSave = self.montantEur
        rep = super(Don, self).write(vals)
        res = self
        rec = res.donateur
        if str(rec.idpremierdon) == str(res.id):
            rec.datePremierDon = res.date
            rec.montantPremierDon = res.montantEur
            rec.codemediaPremierDon = res.codeMedia

        if str(rec.iddernierdon) == str(res.id):
            rec.dateDernierDon = res.date
            rec.montantDernierDon = res.montantEur
            rec.codemediaDernierDon = res.codeMedia

        if str(rec.idpremierdon) == str(res.id) or str(
                rec.iddernierdon) == str(res.id):
            rec.cumulDonTotal = rec.cumulDonTotal - montantEurSave + res.montantEur
            rec.don_moy = rec.cumulDonTotal / rec.nombreDons

        if str(rec.idpremierdonHPA) == str(res.id):
            rec.datePremierDonHPA = res.date
            rec.montantPremierDonHPA = res.montantEur
            rec.codemediaPremierDonHPA = res.codeMedia

        if str(rec.iddernierdonsHPA) == str(res.id):
            rec.dateDernierDonHPA = res.date
            rec.montantDernierDonHPA = res.montantEur
            rec.codemediaDernierDonHPA = res.codeMedia

        if str(rec.idpremierdonHPA) == str(res.id) or str(
                rec.iddernierdonsHPA) == str(res.id):
            rec.cumulDonHPA = rec.cumulDonHPA - montantEurSave + res.montantEur
            rec.don_moyHPA = rec.cumulDonHPA / rec.nombreDonsHPA

        if rec.cumulDonTotal < 1000:
            rec.montant = 'Donor'
        elif rec.cumulDonTotal >= 1000 and rec.cumulDonTotal <= 5000:
            rec.montant = 'Middle donor'
        elif rec.cumulDonTotal > 5000 and rec.cumulDonTotal <= 50000:
            rec.montant = 'Global Leader'
        elif rec.cumulDonTotal > 50000:
            rec.montant = 'Investisseur Fondateur'

        return rep

    @api.depends('montantEur')
    def convNombre2lettres(self):
        for rec in self:
            nombre = rec.montantEur
            rec.montantLettre = rec.convNombre(int(nombre))

    @api.depends('taux_change', 'montantEur')
    def tauxChange(self):
        for rec in self:
            rec.montant_dans_devise = rec.taux_change * rec.montantEur

    @api.depends('id_intersa')
    def _get_url_intersa(self):
        for rec in self:
            if rec.id_intersa:
                rec.url_intersa = 'https://www.intersa.fr/imagesAlima/INT_' + rec.id_intersa

    def convNombre(self, nombre):
        s = ''
        reste = nombre
        i = 1000000000
        while i > 0:
            y = reste / i
            if y != 0:
                centaine = y / 100
                dizaine = (y - centaine * 100) / 10
                unite = y - centaine * 100 - dizaine * 10
                if centaine == 1:
                    s += "CENT "
                elif centaine != 0:
                    s += schu[centaine] + "CENT "
                    if dizaine == 0 and unite == 0: s = s[:-1] + "S "
                if dizaine not in [0, 1]: s += schd[dizaine]
                if unite == 0:
                    if dizaine in [1, 7, 9]: s += "DIX "
                    elif dizaine == 8: s = s[:-1] + "S "
                elif unite == 1:
                    if dizaine in [1, 9]: s += "ONZE "
                    elif dizaine == 7: s += "ET ONZE "
                    elif dizaine in [2, 3, 4, 5, 6]: s += "ET UN "
                    elif dizaine in [0, 8]: s += "UN "
                elif unite in [2, 3, 4, 5, 6, 7, 8, 9]:
                    if dizaine in [1, 7, 9]: s += schud[unite]
                    else: s += schu[unite]
                if i == 1000000000:
                    if y > 1: s += "MILLIARDS "
                    else: s += "MILLIARD "
                if i == 1000000:
                    if y > 1: s += "MILLIONS "
                    else: s += "MILLIONS "
                if i == 1000:
                    s += "MILLE "
                if i == 1000 and s == 'UN MILLE ':
                    s = "MILLE "
            #end if y!=0
            reste -= y * i
            dix = False
            i /= 1000
        #end while
        if len(s) == 0: s += "ZERO "
        return s

    @api.multi
    def reset_reconsiliation_don(self):
        all_don = self.env['crm.alima.don'].search([])
        for don in all_don:
            don.write({'NumRecuFiscal': False})

    @api.multi
    def correction_historique_don(self):
        id_donateur_a_traiter = [
            5202,
            247,
            8508,
            3958,
            1069,
            3632,
            5559,
            2155,
            3736,
            6975,
            4731,
            4074,
            1467,
            4097,
            3222,
            2253,
            1394,
            4220,
            6855,
            3669,
            273,
            5213,
            5420,
            4926,
            6139,
            413,
            701,
            378,
            2252,
            4687,
            465,
            4553,
            2594,
            503,
            3838,
            3969,
            1336,
            4085,
            594,
            5029,
            5422,
        ]
        donateur_a_traiter = self.env['crm.alima.donateur'].browse(
            id_donateur_a_traiter)
        for donateur in donateur_a_traiter:
            for don in donateur.dons:
                if datetime.strptime(don.date, '%Y-%m-%d') > datetime.strptime(
                        donateur.dateDernierDon, '%Y-%m-%d'):
                    donateur.dateDernierDon = don.date
                    donateur.montantDernierDon = don.montantEur
                    donateur.codemediaDernierDon = don.codeMedia
                    donateur.iddernierdon = don.id
                if datetime.strptime(don.date, '%Y-%m-%d') < datetime.strptime(
                        donateur.datePremierDon, '%Y-%m-%d'):
                    donateur.datePremierDon = don.date
                    donateur.montantPremierDon = don.montantEur
                    donateur.codemediaPremierDon = don.codeMedia
                    donateur.idpremierdon = don.id
                if don.mode_versement != 'avec prelevement':
                    if datetime.strptime(
                            don.date, '%Y-%m-%d') > datetime.strptime(
                                donateur.dateDernierDonHPA, '%Y-%m-%d'):
                        donateur.dateDernierDonHPA = don.date
                        donateur.montantDernierDonHPA = don.montantEur
                        donateur.codemediaDernierDonHPA = don.codeMedia
                        donateur.iddernierdonsHPA = don.id
                    if datetime.strptime(
                            don.date, '%Y-%m-%d') < datetime.strptime(
                                donateur.datePremierDonHPA, '%Y-%m-%d'):
                        donateur.datePremierDonHPA = don.date
                        donateur.montantPremierDonHPA = don.montantEur
                        donateur.codemediaPremierDonHPA = don.codeMedia
                        donateur.idpremierdonHPA = don.id
Exemple #25
0
class reparacion(models.Model):
    _name = 'upotussam.reparacion'
    descripcion = fields.Char('Descripcion', size=64, required=True)
    fechaInicio = fields.Datetime('Fecha de Inicio',
                                  required=True,
                                  autodate=True)
    fechaFin = fields.Datetime('Fecha de Fin', required=True, autodate=True)
    mensajeCancelacion = fields.Char('Mensaje de Cancelacion',
                                     size=64,
                                     required=False)

    autobus_id = fields.Many2one('upotussam.autobus', 'Autobus', required=True)
    piezas_ids = fields.Many2many('upotussam.piezas', string='Piezas')
    mecanico_ids = fields.Many2many('upotussam.mecanico',
                                    string='Mecanicos involucrados')

    state = fields.Selection([
        ('espera', 'En espera'),
        ('enviado', 'Autobus enviado'),
        ('reparacion', 'En reparacion'),
        ('finalizado', 'Finalizado'),
        ('cancelado', 'Cancelado'),
    ],
                             'Estado',
                             default='espera')

    #botones

    @api.one
    def btn_submit_to_enviado(self):
        self.write({'state': 'enviado'})

    @api.one
    def btn_submit_to_reparacion(self):
        if not self.mecanico_ids:
            raise models.ValidationError(
                'Se tiene que asignar un mecanico minimo')
        else:
            self.write({'state': 'reparacion'})

    @api.one
    def btn_submit_to_finalizado(self):
        self.write({'state': 'finalizado'})

    @api.one
    def btn_submit_to_cancelado(self):
        self.write({'state': 'cancelado'})

    #onchanges

    @api.onchange('piezas_ids')
    def onchange_piezas(self):
        if self.state != 'enviado':
            raise models.ValidationError(
                'Solo se pueden asignar las piezas cuando se haya enviado el autobus a reparar'
            )

    @api.onchange('mecanico_ids')
    def onchange_mecanico(self):
        if self.state != 'enviado':
            raise models.ValidationError(
                'Solo se pueden asignar los mecanicos cuando se haya enviado el autobus a reparar'
            )

    @api.onchange('descripcion', 'fechaInicio', 'fechaFin', 'garantia',
                  'autobus_id', 'piezas_ids', 'mecanico_ids')
    def onechange_cancelar(self):
        if self.state == 'cancelado':
            raise models.ValidationError(
                'Una vez cancelado no se puede modificar')

    @api.onchange('mensajeCancelacion')
    def onchange_mecanico(self):
        if self.state != 'cancelado':
            raise models.ValidationError(
                'Solo se puede añadir un mensaje si la reparacion se ha cancelado'
            )

    @api.multi
    @api.onchange('mecanico_ids')
    def onchange_mecanicos_taller(self):
        taller = 0
        for mecanico in self.mecanico_ids:
            if not taller:
                taller = mecanico.taller_id
            else:
                if mecanico.taller_id != taller:
                    raise models.ValidationError(
                        'Tiene que ser el mismo taller')
class VendorDelayReport(models.Model):
    _name = "vendor.delay.report"
    _description = "Vendor Delay Report"
    _auto = False

    partner_id = fields.Many2one('res.partner', 'Vendor', readonly=True)
    product_id = fields.Many2one('product.product', 'Product', readonly=True)
    category_id = fields.Many2one('product.category',
                                  'Product Category',
                                  readonly=True)
    date = fields.Datetime('Effective Date', readonly=True)
    qty_total = fields.Float('Total Quantity', readonly=True)
    qty_on_time = fields.Float('On-Time Quantity', readonly=True)
    on_time_rate = fields.Float('On-Time Delivery Rate', readonly=True)

    def init(self):
        tools.drop_view_if_exists(self.env.cr, 'vendor_delay_report')
        self.env.cr.execute("""
CREATE OR replace VIEW vendor_delay_report AS(
SELECT m.id                     AS id,
       m.date                   AS date,
       m.purchase_line_id       AS purchase_line_id,
       m.product_id             AS product_id,
       Min(pc.id)               AS category_id,
       Min(po.partner_id)       AS partner_id,
       Sum(pol.product_uom_qty) AS qty_total,
       Sum(CASE
             WHEN pol.date_planned >= m.date THEN ml.qty_done
             ELSE 0
           END)                 AS qty_on_time
FROM   stock_move m
       JOIN stock_move_line ml
         ON m.id = ml.move_id
       JOIN purchase_order_line pol
         ON pol.id = m.purchase_line_id
       JOIN purchase_order po
         ON po.id = pol.order_id
       JOIN product_product p
         ON p.id = m.product_id
       JOIN product_template pt
         ON pt.id = p.product_tmpl_id
       JOIN product_category pc
         ON pc.id = pt.categ_id
WHERE  m.state = 'done'
GROUP  BY m.id
)""")

    @api.model
    def read_group(self,
                   domain,
                   fields,
                   groupby,
                   offset=0,
                   limit=None,
                   orderby=False,
                   lazy=True):
        if 'on_time_rate' not in fields:
            res = super().read_group(domain,
                                     fields,
                                     groupby,
                                     offset=offset,
                                     limit=limit,
                                     orderby=orderby,
                                     lazy=lazy)
            return res

        fields.remove('on_time_rate')
        if 'qty_total' not in fields:
            fields.append('qty_total')
        if 'qty_on_time' not in fields:
            fields.append('qty_on_time')
        res = super().read_group(domain,
                                 fields,
                                 groupby,
                                 offset=offset,
                                 limit=limit,
                                 orderby=orderby,
                                 lazy=lazy)
        for group in res:
            if group['qty_total'] == 0:
                on_time_rate = 100
            else:
                on_time_rate = group['qty_on_time'] / group['qty_total'] * 100
            group.update({'on_time_rate': on_time_rate})

        return res
Exemple #27
0
class Customer(models.Model):
    _name = 'customer'

    name = fields.Char(string=u'公司名称')
    address = fields.Char(string=u'地址')
    website = fields.Char(string=u'网址')
    develop_time = fields.Datetime(string=u'开发时间', default=fields.Datetime.now)
    last_contact_time = fields.Datetime(string=u'上次联系时间',
                                        default=fields.Datetime.now)
    note = fields.Text(string=u'备注')
    active = fields.Boolean(string=u'可用', default=True)
    so_qty = fields.Float(string=u'销售订单数', store=False, compute='_get_so_qty')
    del_days = fields.Integer(string=u'间隔天数',
                              related='grade_id.del_time',
                              store=False)
    # del_days_final = fields.Integer(string=u'最终的间隔天数', store=False)
    origin_id = fields.Many2one('customer.origin', string=u'来源')
    grade_id = fields.Many2one('customer.grade', string=u'客户等级')
    category_id = fields.Many2one('customer.category', string=u'客户类型')
    develop_person = fields.Many2one('res.users',
                                     string=u'业务员',
                                     default=lambda self: self.env.user.id)
    country_id = fields.Many2one('country', string=u'国家')
    customer_state = fields.Many2one('customer.state', string=u'状态')
    sale_orders = fields.One2many('sale.order', 'customer_id', string=u'销售订单')
    so_remind_ids = fields.One2many('sale.order.remind',
                                    'customer_id',
                                    string=u'交货日期提醒')
    # product_ids = fields.Many2many('product','customer_product_rel','customer_id','product_id',string=u'意向产品')
    contact_ids = fields.Many2many('customer.contact.person',
                                   'customers_contacts_rel',
                                   'customer_id',
                                   'contact_id',
                                   string=u'联系人')

    _sql_constraints = [
        ('CompanyName_unique', 'unique (name)', u'系统中已存在该公司!'),
        ('website_unique', 'unique (website)', u'系统中已存在该公司网址!'),
    ]

    #获取销售订单数
    @api.one
    def _get_so_qty(self):
        for record in self:
            record.so_qty = len(record.sale_orders)

    #跳转到销售订单tree视图
    @api.multi
    def view_sale_order(self):
        return {
            'name': _(u'销售订单'),
            'type': 'ir.actions.act_window',
            'res_model': 'sale.order',
            'view_type': 'form',
            'view_mode': 'tree,form',
            'domain': [('customer_id', '=', self.id)],
        }

    #customer contacter One2many的关系
    @api.model
    def create(self, vals):
        contact_ids = vals.get('contact_ids')
        if contact_ids and len(contact_ids) >= 1:
            for item in contact_ids:
                if len(item) == 3:
                    c_t_ps = self.env['customer.contact.person'].browse(
                        item[2])
                    for c_t_p in c_t_ps:
                        if c_t_p.customer_ids:
                            info = u"【%s】已经是【%s】的联系人!" % (
                                c_t_p.name, c_t_p.customer_ids.name)
                            raise ValidationError(_(info))
        return super(Customer, self).create(vals)

    # customer contacter One2many的关系
    @api.multi
    def write(self, vals):
        contact_ids = vals.get('contact_ids')
        # print self.id,self.ids
        if contact_ids and len(contact_ids) >= 1:
            for item in contact_ids:
                if len(item) == 3:
                    c_t_ps = self.env['customer.contact.person'].browse(
                        item[2])
                    for c_t_p in c_t_ps:
                        # print c_t_p.customer_ids.id
                        if c_t_p.customer_ids and c_t_p.customer_ids.id != self.id:
                            info = u"【%s】已经是【%s】的联系人!" % (
                                c_t_p.name, c_t_p.customer_ids.name)
                            raise ValidationError(_(info))
        #update last_contact_time
        # last_contact_time = vals.get('last_contact_time')
        # if last_contact_time:
        #     records = self.env['contact.customer'].search([('state','=','to_do'),('customer_id','=',self.id)])
        #     if records:
        #         if len(records) >= 2:
        #             info = u'有多条联系该客户的记录'
        #             raise ValidationError(_(info))
        #         elif len(records) == 1:
        #             last_contact_time = datetime.datetime.strptime(last_contact_time, '%Y-%m-%d %H:%M:%S')
        #             if last_contact_time + datetime.timedelta(days=self.del_days) > datetime.datetime.now():
        #                 records[0].state = 'done'

        return super(Customer, self).write(vals)

    #查看客户跟踪记录
    def view_customer_follow_record(self):
        # print self.id
        return {
            'name': _(u'客户跟踪记录'),
            'type': 'ir.actions.act_window',
            'res_model': 'customer.follow.record',
            'view_type': 'form',
            'view_mode': 'tree,form',
            'domain': [('customer_id', '=', self.id)],
        }
Exemple #28
0
class crm_claim(models.Model):
    _name = "crm.claim"
    _description = "Claim"
    _order = "priority,date desc"
    _inherit = ['mail.thread']

    def _get_default_stage_id(self):
        """ Gives default stage_id """
        team_id = self.env['crm.team'].sudo()._get_default_team_id()
        return self._stage_find(team_id=team_id.id, domain=[('sequence', '=', '1')])

    @api.model
    def _default_user(self):
        return self.env.context.get('user_id', self.env.user.id)

    id = fields.Integer('ID', readonly=True)
    name = fields.Char('Claim Subject', required=True)
    active = fields.Boolean('Active',default=lambda *a: 1)
    action_next = fields.Char('Next Action')
    date_action_next = fields.Datetime('Next Action Date')
    description = fields.Text('Description')
    resolution = fields.Text('Resolution')
    create_date = fields.Datetime('Creation Date' , readonly=True)
    write_date = fields.Datetime('Update Date' , readonly=True)
    date_deadline = fields.Datetime('Deadline')
    date_closed = fields.Datetime('Closed', readonly=True)
    date = fields.Datetime('Claim Date', select=True,default=lambda self: self._context.get('date', fields.Datetime.now()))
    categ_id = fields.Many2one('crm.claim.category', 'Category')
    priority = fields.Selection([('0','Low'), ('1','Normal'), ('2','High')], 'Priority',default='1')
    type_action = fields.Selection([('correction','Corrective Action'),('prevention','Preventive Action')], 'Action Type')
    user_id = fields.Many2one('res.users', 'Responsible', track_visibility='always', default=_default_user)
    user_fault = fields.Char('Trouble Responsible')
    team_id = fields.Many2one('crm.team', 'Sales Team', oldname='section_id',\
                        select=True, help="Responsible sales team."\
                                " Define Responsible user and Email account for"\
                                " mail gateway.")#,default=lambda self: self.env['crm.team']._get_default_team_id()
    company_id = fields.Many2one('res.company', 'Company',default=lambda self: self.env['res.company']._company_default_get('crm.case'))
    partner_id = fields.Many2one('res.partner', 'Partner')
    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")
    email_from = fields.Char('Email', size=128, help="Destination email for email gateway.")
    partner_phone = fields.Char('Phone')
    stage_id = fields.Many2one ('crm.claim.stage', 'Stage', track_visibility='onchange',
                domain="['|', ('team_ids', '=', team_id), ('case_default', '=', True)]")    #,default=lambda self:self.env['crm.claim']._get_default_stage_id()
    cause = fields.Text('Root Cause')

    @api.onchange('partner_id')
    def onchange_partner_id(self, email=False):
        if not self.partner_id:
            return {'value': {'email_from': False, 'partner_phone': False}}
        address = self.pool.get('res.partner').browse(self.partner_id)
        return {'value': {'email_from': address.email, 'partner_phone': address.phone}}

    @api.model
    def create(self, vals):
        context = dict(self._context or {})
        if vals.get('team_id') and not self._context.get('default_team_id'):
            context['default_team_id'] = vals.get('team_id')

        # context: no_log, because subtype already handle this
        return super(crm_claim, self).create(vals)

    def message_new(self,msg, custom_values=None):
        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),
        }
        if msg.get('priority'):
            defaults['priority'] = msg.get('priority')
        defaults.update(custom_values)
        return super(crm_claim, self).message_new(msg, custom_values=defaults)
Exemple #29
0
class TierReview(models.Model):
    _name = "tier.review"
    _description = "Tier Review"

    name = fields.Char(related="definition_id.name", readonly=True)
    status = fields.Selection(
        selection=[
            ("pending", "Pending"),
            ("rejected", "Rejected"),
            ("approved", "Approved"),
        ],
        default="pending",
    )
    model = fields.Char(string="Related Document Model", index=True)
    res_id = fields.Integer(string="Related Document ID", index=True)
    definition_id = fields.Many2one(comodel_name="tier.definition")
    company_id = fields.Many2one(
        related="definition_id.company_id",
        store=True,
    )
    review_type = fields.Selection(related="definition_id.review_type",
                                   readonly=True)
    reviewer_id = fields.Many2one(related="definition_id.reviewer_id",
                                  readonly=True)
    reviewer_group_id = fields.Many2one(
        related="definition_id.reviewer_group_id", readonly=True)
    reviewer_ids = fields.Many2many(
        string="Reviewers",
        comodel_name="res.users",
        compute="_compute_reviewer_ids",
        store=True,
    )
    sequence = fields.Integer(string="Tier")
    todo_by = fields.Char(compute="_compute_todo_by", store=True)
    done_by = fields.Many2one(comodel_name="res.users")
    requested_by = fields.Many2one(comodel_name="res.users")
    reviewed_date = fields.Datetime(string="Validation Date")
    has_comment = fields.Boolean(related="definition_id.has_comment",
                                 readonly=True)
    comment = fields.Char(string="Comments")
    can_review = fields.Boolean(
        compute="_compute_can_review",
        store=True,
        help="""Can review will be marked if the review is pending and the
        approve sequence has been achieved""",
    )
    approve_sequence = fields.Boolean(related="definition_id.approve_sequence",
                                      readonly=True)

    @api.depends("definition_id.approve_sequence")
    def _compute_can_review(self):
        for record in self:
            record.can_review = record._can_review_value()

    def _can_review_value(self):
        if self.status != "pending":
            return False
        if not self.approve_sequence:
            return True
        resource = self.env[self.model].browse(self.res_id)
        reviews = resource.review_ids.filtered(lambda r: r.status == "pending")
        if not reviews:
            return True
        sequence = min(reviews.mapped("sequence"))
        return self.sequence == sequence

    @api.model
    def _get_reviewer_fields(self):
        return ["reviewer_id", "reviewer_group_id", "reviewer_group_id.users"]

    @api.depends(lambda self: self._get_reviewer_fields())
    def _compute_reviewer_ids(self):
        for rec in self:
            rec.reviewer_ids = rec._get_reviewers()

    @api.depends("reviewer_ids")
    def _compute_todo_by(self):
        """ Show by group or by abbrev list of names """
        num_show = 3  # Max number of users to display
        for rec in self:
            todo_by = False
            if rec.reviewer_group_id:
                todo_by = _("Group %s") % rec.reviewer_group_id.name
            else:
                todo_by = ", ".join(
                    rec.reviewer_ids[:num_show].mapped("display_name"))
                num_users = len(rec.reviewer_ids)
                if num_users > num_show:
                    todo_by = "{} (and {} more)".format(
                        todo_by, num_users - num_show)
            rec.todo_by = todo_by

    def _get_reviewers(self):
        return self.reviewer_id + self.reviewer_group_id.users
Exemple #30
0
class PurchaseOrderLine(models.Model):
    _name = 'purchase.order.line'
    _description = 'Purchase Order Line'
    _order = 'order_id, sequence, id'

    name = fields.Text(string='Description', required=True)
    sequence = fields.Integer(string='Sequence', default=10)
    product_qty = fields.Float(string='Quantity',
                               digits='Product Unit of Measure',
                               required=True)
    product_uom_qty = fields.Float(string='Total Quantity',
                                   compute='_compute_product_uom_qty',
                                   store=True)
    date_planned = fields.Datetime(string='Scheduled Date', index=True)
    taxes_id = fields.Many2many(
        'account.tax',
        string='Taxes',
        domain=['|', ('active', '=', False), ('active', '=', True)])
    product_uom = fields.Many2one(
        'uom.uom',
        string='Unit of Measure',
        domain="[('category_id', '=', product_uom_category_id)]")
    product_uom_category_id = fields.Many2one(
        related='product_id.uom_id.category_id')
    product_id = fields.Many2one('product.product',
                                 string='Product',
                                 domain=[('purchase_ok', '=', True)],
                                 change_default=True)
    product_type = fields.Selection(related='product_id.type', readonly=True)
    price_unit = fields.Float(string='Unit Price',
                              required=True,
                              digits='Product Price')

    price_subtotal = fields.Monetary(compute='_compute_amount',
                                     string='Subtotal',
                                     store=True)
    price_total = fields.Monetary(compute='_compute_amount',
                                  string='Total',
                                  store=True)
    price_tax = fields.Float(compute='_compute_amount',
                             string='Tax',
                             store=True)

    order_id = fields.Many2one('purchase.order',
                               string='Order Reference',
                               index=True,
                               required=True,
                               ondelete='cascade')
    account_analytic_id = fields.Many2one('account.analytic.account',
                                          string='Analytic Account')
    analytic_tag_ids = fields.Many2many('account.analytic.tag',
                                        string='Analytic Tags')
    company_id = fields.Many2one('res.company',
                                 related='order_id.company_id',
                                 string='Company',
                                 store=True,
                                 readonly=True)
    state = fields.Selection(related='order_id.state',
                             store=True,
                             readonly=False)

    invoice_lines = fields.One2many('account.move.line',
                                    'purchase_line_id',
                                    string="Bill Lines",
                                    readonly=True,
                                    copy=False)

    # Replace by invoiced Qty
    qty_invoiced = fields.Float(compute='_compute_qty_invoiced',
                                string="Billed Qty",
                                digits='Product Unit of Measure',
                                store=True)

    qty_received_method = fields.Selection(
        [('manual', 'Manual')],
        string="Received Qty Method",
        compute='_compute_qty_received_method',
        store=True,
        help=
        "According to product configuration, the recieved quantity can be automatically computed by mechanism :\n"
        "  - Manual: the quantity is set manually on the line\n"
        "  - Stock Moves: the quantity comes from confirmed pickings\n")
    qty_received = fields.Float("Received Qty",
                                compute='_compute_qty_received',
                                inverse='_inverse_qty_received',
                                compute_sudo=True,
                                store=True,
                                digits='Product Unit of Measure')
    qty_received_manual = fields.Float("Manual Received Qty",
                                       digits='Product Unit of Measure',
                                       copy=False)

    partner_id = fields.Many2one('res.partner',
                                 related='order_id.partner_id',
                                 string='Partner',
                                 readonly=True,
                                 store=True)
    currency_id = fields.Many2one(related='order_id.currency_id',
                                  store=True,
                                  string='Currency',
                                  readonly=True)
    date_order = fields.Datetime(related='order_id.date_order',
                                 string='Order Date',
                                 readonly=True)

    display_type = fields.Selection([('line_section', "Section"),
                                     ('line_note', "Note")],
                                    default=False,
                                    help="Technical field for UX purpose.")

    _sql_constraints = [
        ('accountable_required_fields',
         "CHECK(display_type IS NOT NULL OR (product_id IS NOT NULL AND product_uom IS NOT NULL AND date_planned IS NOT NULL))",
         "Missing required fields on accountable purchase order line."),
        ('non_accountable_null_fields',
         "CHECK(display_type IS NULL OR (product_id IS NULL AND price_unit = 0 AND product_uom_qty = 0 AND product_uom IS NULL AND date_planned is NULL))",
         "Forbidden values on non-accountable purchase order line"),
    ]

    @api.depends('product_qty', 'price_unit', 'taxes_id')
    def _compute_amount(self):
        for line in self:
            vals = line._prepare_compute_all_values()
            taxes = line.taxes_id.compute_all(vals['price_unit'],
                                              vals['currency_id'],
                                              vals['product_qty'],
                                              vals['product'], vals['partner'])
            line.update({
                'price_tax':
                sum(t.get('amount', 0.0) for t in taxes.get('taxes', [])),
                'price_total':
                taxes['total_included'],
                'price_subtotal':
                taxes['total_excluded'],
            })

    def _prepare_compute_all_values(self):
        # Hook method to returns the different argument values for the
        # compute_all method, due to the fact that discounts mechanism
        # is not implemented yet on the purchase orders.
        # This method should disappear as soon as this feature is
        # also introduced like in the sales module.
        self.ensure_one()
        return {
            'price_unit': self.price_unit,
            'currency_id': self.order_id.currency_id,
            'product_qty': self.product_qty,
            'product': self.product_id,
            'partner': self.order_id.partner_id,
        }

    def _compute_tax_id(self):
        for line in self:
            fpos = line.order_id.fiscal_position_id or line.order_id.partner_id.with_company(
                line.company_id).property_account_position_id
            # If company_id is set, always filter taxes by the company
            taxes = line.product_id.supplier_taxes_id.filtered(
                lambda r: not line.company_id or r.company_id == line.
                company_id)
            line.taxes_id = fpos.map_tax(
                taxes, line.product_id,
                line.order_id.partner_id) if fpos else taxes

    @api.depends('invoice_lines.move_id.state', 'invoice_lines.quantity')
    def _compute_qty_invoiced(self):
        for line in self:
            qty = 0.0
            for inv_line in line.invoice_lines:
                if inv_line.move_id.state not in ['cancel']:
                    if inv_line.move_id.type == 'in_invoice':
                        qty += inv_line.product_uom_id._compute_quantity(
                            inv_line.quantity, line.product_uom)
                    elif inv_line.move_id.type == 'in_refund':
                        qty -= inv_line.product_uom_id._compute_quantity(
                            inv_line.quantity, line.product_uom)
            line.qty_invoiced = qty

    @api.depends('product_id')
    def _compute_qty_received_method(self):
        for line in self:
            if line.product_id and line.product_id.type in [
                    'consu', 'service'
            ]:
                line.qty_received_method = 'manual'
            else:
                line.qty_received_method = False

    @api.depends('qty_received_method', 'qty_received_manual')
    def _compute_qty_received(self):
        for line in self:
            if line.qty_received_method == 'manual':
                line.qty_received = line.qty_received_manual or 0.0
            else:
                line.qty_received = 0.0

    @api.onchange('qty_received')
    def _inverse_qty_received(self):
        """ When writing on qty_received, if the value should be modify manually (`qty_received_method` = 'manual' only),
            then we put the value in `qty_received_manual`. Otherwise, `qty_received_manual` should be False since the
            received qty is automatically compute by other mecanisms.
        """
        for line in self:
            if line.qty_received_method == 'manual':
                line.qty_received_manual = line.qty_received
            else:
                line.qty_received_manual = 0.0

    @api.model
    def create(self, values):
        if values.get('display_type',
                      self.default_get(['display_type'])['display_type']):
            values.update(product_id=False,
                          price_unit=0,
                          product_uom_qty=0,
                          product_uom=False,
                          date_planned=False)

        order_id = values.get('order_id')
        if 'date_planned' not in values:
            order = self.env['purchase.order'].browse(order_id)
            if order.date_planned:
                values['date_planned'] = order.date_planned
        line = super(PurchaseOrderLine, self).create(values)
        if line.order_id.state == 'purchase':
            msg = _("Extra line with %s ") % (line.product_id.display_name, )
            line.order_id.message_post(body=msg)
        return line

    def write(self, values):
        if 'display_type' in values and self.filtered(
                lambda line: line.display_type != values.get('display_type')):
            raise UserError(
                "You cannot change the type of a purchase order line. Instead you should delete the current line and create a new line of the proper type."
            )

        if 'product_qty' in values:
            for line in self:
                if line.order_id.state == 'purchase':
                    line.order_id.message_post_with_view(
                        'purchase.track_po_line_template',
                        values={
                            'line': line,
                            'product_qty': values['product_qty']
                        },
                        subtype_id=self.env.ref('mail.mt_note').id)
        return super(PurchaseOrderLine, self).write(values)

    def unlink(self):
        for line in self:
            if line.order_id.state in ['purchase', 'done']:
                raise UserError(
                    _('Cannot delete a purchase order line which is in state \'%s\'.'
                      ) % (line.state, ))
        return super(PurchaseOrderLine, self).unlink()

    @api.model
    def _get_date_planned(self, seller, po=False):
        """Return the datetime value to use as Schedule Date (``date_planned``) for
           PO Lines that correspond to the given product.seller_ids,
           when ordered at `date_order_str`.

           :param Model seller: used to fetch the delivery delay (if no seller
                                is provided, the delay is 0)
           :param Model po: purchase.order, necessary only if the PO line is
                            not yet attached to a PO.
           :rtype: datetime
           :return: desired Schedule Date for the PO line
        """
        date_order = po.date_order if po else self.order_id.date_order
        if date_order:
            return date_order + relativedelta(
                days=seller.delay if seller else 0)
        else:
            return datetime.today() + relativedelta(
                days=seller.delay if seller else 0)

    @api.onchange('product_id')
    def onchange_product_id(self):
        if not self.product_id:
            return

        # Reset date, price and quantity since _onchange_quantity will provide default values
        self.date_planned = datetime.today().strftime(
            DEFAULT_SERVER_DATETIME_FORMAT)
        self.price_unit = self.product_qty = 0.0

        self._product_id_change()

        self._suggest_quantity()
        self._onchange_quantity()

    def _product_id_change(self):
        if not self.product_id:
            return

        self.product_uom = self.product_id.uom_po_id or self.product_id.uom_id
        product_lang = self.product_id.with_context(
            lang=self.partner_id.lang,
            partner_id=self.partner_id.id,
            company_id=self.company_id.id,
        )
        self.name = self._get_product_purchase_description(product_lang)

        self._compute_tax_id()

    @api.onchange('product_id')
    def onchange_product_id_warning(self):
        if not self.product_id or not self.env.user.has_group(
                'purchase.group_warning_purchase'):
            return
        warning = {}
        title = False
        message = False

        product_info = self.product_id

        if product_info.purchase_line_warn != 'no-message':
            title = _("Warning for %s") % product_info.name
            message = product_info.purchase_line_warn_msg
            warning['title'] = title
            warning['message'] = message
            if product_info.purchase_line_warn == 'block':
                self.product_id = False
            return {'warning': warning}
        return {}

    @api.onchange('product_qty', 'product_uom')
    def _onchange_quantity(self):
        if not self.product_id:
            return
        params = {'order_id': self.order_id}
        seller = self.product_id._select_seller(
            partner_id=self.partner_id,
            quantity=self.product_qty,
            date=self.order_id.date_order and self.order_id.date_order.date(),
            uom_id=self.product_uom,
            params=params)

        if seller or not self.date_planned:
            self.date_planned = self._get_date_planned(seller).strftime(
                DEFAULT_SERVER_DATETIME_FORMAT)

        if not seller:
            if self.product_id.seller_ids.filtered(
                    lambda s: s.name.id == self.partner_id.id):
                self.price_unit = 0.0
            return

        price_unit = self.env['account.tax']._fix_tax_included_price_company(
            seller.price, self.product_id.supplier_taxes_id, self.taxes_id,
            self.company_id) if seller else 0.0
        if price_unit and seller and self.order_id.currency_id and seller.currency_id != self.order_id.currency_id:
            price_unit = seller.currency_id._convert(
                price_unit, self.order_id.currency_id,
                self.order_id.company_id, self.date_order
                or fields.Date.today())

        if seller and self.product_uom and seller.product_uom != self.product_uom:
            price_unit = seller.product_uom._compute_price(
                price_unit, self.product_uom)

        self.price_unit = price_unit

    @api.depends('product_uom', 'product_qty', 'product_id.uom_id')
    def _compute_product_uom_qty(self):
        for line in self:
            if line.product_id and line.product_id.uom_id != line.product_uom:
                line.product_uom_qty = line.product_uom._compute_quantity(
                    line.product_qty, line.product_id.uom_id)
            else:
                line.product_uom_qty = line.product_qty

    def _suggest_quantity(self):
        '''
        Suggest a minimal quantity based on the seller
        '''
        if not self.product_id:
            return
        seller_min_qty = self.product_id.seller_ids\
            .filtered(lambda r: r.name == self.order_id.partner_id and (not r.product_id or r.product_id == self.product_id))\
            .sorted(key=lambda r: r.min_qty)
        if seller_min_qty:
            self.product_qty = seller_min_qty[0].min_qty or 1.0
            self.product_uom = seller_min_qty[0].product_uom
        else:
            self.product_qty = 1.0

    def _get_product_purchase_description(self, product_lang):
        self.ensure_one()
        name = product_lang.display_name
        if product_lang.description_purchase:
            name += '\n' + product_lang.description_purchase

        return name

    def _prepare_account_move_line(self, move):
        self.ensure_one()
        if self.product_id.purchase_method == 'purchase':
            qty = self.product_qty - self.qty_invoiced
        else:
            qty = self.qty_received - self.qty_invoiced
        if float_compare(
                qty, 0.0, precision_rounding=self.product_uom.rounding) <= 0:
            qty = 0.0

        if self.currency_id == move.company_id.currency_id:
            currency = False
        else:
            currency = move.currency_id

        return {
            'name': '%s: %s' % (self.order_id.name, self.name),
            'move_id': move.id,
            'currency_id': currency and currency.id or False,
            'purchase_line_id': self.id,
            'date_maturity': move.invoice_date_due,
            'product_uom_id': self.product_uom.id,
            'product_id': self.product_id.id,
            'price_unit': self.price_unit,
            'quantity': qty,
            'partner_id': move.partner_id.id,
            'analytic_account_id': self.account_analytic_id.id,
            'analytic_tag_ids': [(6, 0, self.analytic_tag_ids.ids)],
            'tax_ids': [(6, 0, self.taxes_id.ids)],
            'display_type': self.display_type,
        }