class SetuInventoryAgeReport(models.TransientModel):
    _name = 'setu.inventory.age.report'
    _description = """
        Inventory age report is useful to determine how oldest your inventories are.
        It gives you detailed analysis products wise about oldest inventories at company level. 
    """

    stock_file_data = fields.Binary('Inventory Age Report File')
    company_ids = fields.Many2many("res.company", string="Companies")
    product_category_ids = fields.Many2many("product.category",
                                            string="Product Categories")
    product_ids = fields.Many2many("product.product", string="Products")

    @api.onchange('product_category_ids')
    def onchange_product_category_id(self):
        if self.product_category_ids:
            return {
                'domain': {
                    'product_ids':
                    [('categ_id', 'child_of', self.product_category_ids.ids)]
                }
            }

    def get_file_name(self):
        filename = "inventory_age_report.xlsx"
        return filename

    def create_excel_workbook(self, file_pointer):
        workbook = xlsxwriter.Workbook(file_pointer)
        return workbook

    def create_excel_worksheet(self, workbook, sheet_name):
        worksheet = workbook.add_worksheet(sheet_name)
        worksheet.set_default_row(22)
        # worksheet.set_border()
        return worksheet

    def set_column_width(self, workbook, worksheet):
        worksheet.set_column(0, 1, 25)
        worksheet.set_column(2, 8, 14)

    def set_format(self, workbook, wb_format):
        wb_new_format = workbook.add_format(wb_format)
        wb_new_format.set_border()
        return wb_new_format

    def set_report_title(self, workbook, worksheet):
        wb_format = self.set_format(workbook,
                                    setu_excel_formatter.FONT_TITLE_CENTER)
        worksheet.merge_range(0, 0, 1, 8, "Inventory Age Report", wb_format)
        wb_format_left = self.set_format(
            workbook, setu_excel_formatter.FONT_MEDIUM_BOLD_LEFT)
        wb_format_center = self.set_format(
            workbook, setu_excel_formatter.FONT_MEDIUM_BOLD_CENTER)

    def get_inventory_age_report_data(self):
        """
        :return:
        """
        category_ids = company_ids = {}
        if self.product_category_ids:
            categories = self.env['product.category'].search([
                ('id', 'child_of', self.product_category_ids.ids)
            ])
            category_ids = set(categories.ids) or {}
        products = self.product_ids and set(self.product_ids.ids) or {}

        if self.company_ids:
            companies = self.env['res.company'].search([
                ('id', 'child_of', self.company_ids.ids)
            ])
            company_ids = set(companies.ids) or {}
        else:
            company_ids = set(self.env.user.company_ids.ids) or {}

        # warehouses = self.warehouse_ids and set(self.warehouse_ids.ids) or {}

        # get_products_overstock_data(company_ids, product_ids, category_ids, warehouse_ids, start_date, end_date, advance_stock_days)
        query = """
                Select * from inventory_stock_age_report('%s','%s','%s')
            """ % (company_ids, products, category_ids)
        print(query)
        self._cr.execute(query)
        stock_data = self._cr.dictfetchall()
        return stock_data

    def prepare_data_to_write(self, stock_data={}):
        """

        :param stock_data:
        :return:
        """
        company_wise_data = {}
        for data in stock_data:
            key = (data.get('company_id'), data.get('company_name'))
            if not company_wise_data.get(key, False):
                company_wise_data[key] = {data.get('product_id'): data}
            else:
                company_wise_data.get(key).update(
                    {data.get('product_id'): data})
        return company_wise_data

    def download_report(self):
        file_name = self.get_file_name()
        file_pointer = BytesIO()
        stock_data = self.get_inventory_age_report_data()
        company_wise_analysis_data = self.prepare_data_to_write(
            stock_data=stock_data)
        if not company_wise_analysis_data:
            return False
        workbook = self.create_excel_workbook(file_pointer)
        for stock_data_key, stock_data_value in company_wise_analysis_data.items(
        ):
            sheet_name = stock_data_key[1]
            wb_worksheet = self.create_excel_worksheet(workbook, sheet_name)
            row_no = 3
            self.write_report_data_header(workbook, wb_worksheet, row_no)
            for age_data_key, age_data_value in stock_data_value.items():
                row_no = row_no + 1
                self.write_data_to_worksheet(workbook,
                                             wb_worksheet,
                                             age_data_value,
                                             row=row_no)

        # workbook.save(file_name)
        workbook.close()
        file_pointer.seek(0)
        file_data = base64.encodestring(file_pointer.read())
        self.write({'stock_file_data': file_data})
        file_pointer.close()

        return {
            'name':
            'Inventory Age Report',
            'type':
            'ir.actions.act_url',
            'url':
            '/web/binary/download_document?model=setu.inventory.age.report&field=stock_file_data&id=%s&filename=%s'
            % (self.id, file_name),
            'target':
            'self',
        }

    def download_report_in_listview(self):
        stock_data = self.get_inventory_age_report_data()
        print(stock_data)
        for fsn_data_value in stock_data:
            fsn_data_value['wizard_id'] = self.id
            self.create_data(fsn_data_value)
        graph_view_id = self.env.ref(
            'setu_advance_inventory_reports.setu_inventory_age_bi_report_graph'
        ).id
        tree_view_id = self.env.ref(
            'setu_advance_inventory_reports.setu_inventory_age_bi_report_tree'
        ).id
        is_graph_first = self.env.context.get('graph_report', False)
        report_display_views = []
        viewmode = ''
        if is_graph_first:
            report_display_views.append((graph_view_id, 'graph'))
            report_display_views.append((tree_view_id, 'tree'))
            viewmode = "graph,tree"
        else:
            report_display_views.append((tree_view_id, 'tree'))
            report_display_views.append((graph_view_id, 'graph'))
            viewmode = "tree,graph"
        return {
            'name': _('Inventory Age Analysis'),
            'domain': [('wizard_id', '=', self.id)],
            'res_model': 'setu.inventory.age.bi.report',
            'view_mode': viewmode,
            'type': 'ir.actions.act_window',
            'views': report_display_views,
        }

    def create_data(self, data):
        del data['company_name']
        del data['product_name']
        del data['category_name']
        return self.env['setu.inventory.age.bi.report'].create(data)

    def write_report_data_header(self, workbook, worksheet, row):
        self.set_report_title(workbook, worksheet)
        self.set_column_width(workbook, worksheet)
        wb_format = self.set_format(
            workbook, setu_excel_formatter.FONT_MEDIUM_BOLD_CENTER)
        wb_format.set_text_wrap()
        odd_normal_right_format = self.set_format(
            workbook, setu_excel_formatter.ODD_FONT_MEDIUM_BOLD_RIGHT)
        even_normal_right_format = self.set_format(
            workbook, setu_excel_formatter.EVEN_FONT_MEDIUM_BOLD_RIGHT)
        normal_left_format = self.set_format(
            workbook, setu_excel_formatter.FONT_MEDIUM_BOLD_LEFT)

        worksheet.write(row, 0, 'Product Name', normal_left_format)
        worksheet.write(row, 1, 'Category', normal_left_format)
        worksheet.write(row, 2, 'Current Stock', odd_normal_right_format)
        worksheet.write(row, 3, 'Stock Value', even_normal_right_format)
        worksheet.write(row, 4, 'Stock Qty (%)', odd_normal_right_format)
        worksheet.write(row, 5, 'Stock Value (%)', even_normal_right_format)
        worksheet.write(row, 6, "Oldest Stock Age", odd_normal_right_format)
        worksheet.write(row, 7, "Oldest Qty", even_normal_right_format)
        worksheet.write(row, 8, "Oldest Stock Value", odd_normal_right_format)

        return worksheet

    def write_data_to_worksheet(self, workbook, worksheet, data, row):
        # Start from the first cell. Rows and
        # columns are zero indexed.
        odd_normal_right_format = self.set_format(
            workbook, setu_excel_formatter.ODD_FONT_MEDIUM_NORMAL_RIGHT)
        even_normal_right_format = self.set_format(
            workbook, setu_excel_formatter.EVEN_FONT_MEDIUM_NORMAL_RIGHT)
        odoo_normal_center_format = self.set_format(
            workbook, setu_excel_formatter.ODD_FONT_MEDIUM_NORMAL_CENTER)
        # odd_normal_left_format = self.set_format(workbook, setu_excel_formatter.ODD_FONT_MEDIUM_NORMAL_LEFT)
        normal_left_format = self.set_format(
            workbook, setu_excel_formatter.FONT_MEDIUM_NORMAL_LEFT)

        worksheet.write(row, 0, data.get('product_name', ''),
                        normal_left_format)
        worksheet.write(row, 1, data.get('category_name', ''),
                        normal_left_format)
        worksheet.write(row, 2, data.get('current_stock', ''),
                        odd_normal_right_format)
        worksheet.write(row, 3, data.get('current_stock_value', ''),
                        even_normal_right_format)
        worksheet.write(row, 4, data.get('stock_qty_ratio', ''),
                        odd_normal_right_format)
        worksheet.write(row, 5, data.get('stock_value_ratio', ''),
                        even_normal_right_format)
        worksheet.write(row, 6, data.get('days_old', ''),
                        odd_normal_right_format)
        worksheet.write(row, 7, data.get('oldest_stock_qty', ''),
                        even_normal_right_format)
        worksheet.write(row, 8, data.get('oldest_stock_value', ''),
                        odd_normal_right_format)
        return worksheet
class ImportItemWizard(models.TransientModel):
    _name = "import.item.wizard"
    _description = "Import Items"

    file_data = fields.Binary(string='Import File')
    filename = fields.Char(string='File Name')

    @api.multi
    def action_import(self):
        try:
            if not self.file_data:
                raise Warning("Silahkan upload file.")
            wb = open_workbook(
                file_contents=base64.decodestring(self.file_data))
            values = []

            for s in wb.sheets():
                for row in range(s.nrows):
                    col_value = []
                    for col in range(s.ncols):
                        value = (s.cell(row, col).value)
                        col_value.append(value)
                    values.append(col_value)
            item_obj = self.env['master.item']
            item_lines_obj = self.env['master.item.lines']
            component_obj = self.env['master.component']
            row = 1
            for data in values:
                # posisi : name |  start_working_date | component_name | working_day | percentage
                if row != 1:
                    item_name = data[0]
                    if not item_name:
                        raise UserError(_("Kolom name ada yang kosong"))
                    start_working_date = data[1]
                    if not start_working_date:
                        raise UserError(
                            _("Kolom start_working_date ada yang kosong"))
                    component_name = data[2]
                    if not component_name:
                        raise UserError(
                            _("Kolom component_name ada yang kosong"))
                    working_day = data[3]
                    if not working_day:
                        raise UserError(_("Kolom working_day ada yang kosong"))
                    percentage = data[4]
                    if not percentage:
                        raise UserError(_("Kolom percentage ada yang kosong"))
                    start_working_date = xlrd.xldate_as_tuple(
                        start_working_date, wb.datemode)
                    start_working_date = datetime(*start_working_date)
                    start_working_date = start_working_date.strftime(
                        "%Y-%m-%d")
                    #start_working_date = self.make_date_valid(start_working_date)
                    # cek di master komponen
                    component_exist = component_obj.search(
                        [('name', '=', component_name)], limit=1)
                    if component_exist:
                        component_id = component_exist.id
                    else:
                        component = component_obj.create({
                            'name':
                            component_name,
                            'day':
                            int(working_day)
                        })
                        component_id = component.id

                    # cek di master item
                    item_exist = item_obj.search([('name', '=', item_name)],
                                                 limit=1)
                    if not item_exist:
                        item = item_obj.create({
                            'name':
                            item_name,
                            'date':
                            start_working_date,
                            'line_ids': [(0, 0, {
                                'component_id': component_id,
                                'percent': float(percentage)
                            })]
                        })
                    else:
                        item = item_lines_obj.create({
                            'item_id':
                            item_exist.id,
                            'component_id':
                            component_id,
                            'percent':
                            float(percentage)
                        })

                row += 1
        except Exception as e:
            raise Warning(e)
        views = [
            (self.env.ref('toffin_test.item_master_tree_view').id, 'tree'),
            (self.env.ref('toffin_test.master_item_form_view').id, 'form')
        ]
        return {
            'name': _('Master Items'),
            'domain': [],
            'view_type': 'form',
            'res_model': 'master.item',
            'context': {},
            'view_id': False,
            'views': views,
            'view_mode': 'tree,form',
            'type': 'ir.actions.act_window',
        }

    def make_date_valid(self, date_string):
        date_valid = '%s-%s-%s' % (
            date_string[-4:], month_range[date_string[3:6]], date_string[0:2])
        return date_valid
Example #3
0
class PaymentOrder(models.Model):
    _name = 'payment.order'
    _order = 'id desc'

    @api.depends('line_ids')
    def _compute_amount_total(self):
        for item in self:
            amount_total = 0
            for line in item.line_ids:
                amount_total += line.amount_total
            item.amount_total = amount_total

    name = fields.Char(max_length=30,
                       string="Nome",
                       required=True,
                       default='/')
    company_id = fields.Many2one(
        'res.company',
        string='Company',
        required=True,
        ondelete='restrict',
        default=lambda self: self.env['res.company']._company_default_get(
            'account.l10n_br.payment.mode'))
    type = fields.Selection([('receivable', 'Recebível'),
                             ('payable', 'Pagável')],
                            string="Tipo de Ordem",
                            default='receivable')
    user_id = fields.Many2one('res.users',
                              string=u'Responsável',
                              required=True)
    payment_mode_id = fields.Many2one('l10n_br.payment.mode',
                                      string='Modo de Pagamento',
                                      required=True)
    journal_id = fields.Many2one('account.journal', string="Diário")
    src_bank_account_id = fields.Many2one('res.partner.bank',
                                          string="Conta Bancária")
    state = fields.Selection([('draft', 'Rascunho'), ('open', 'Aberto'),
                              ('attention', 'Necessita Atenção'),
                              ('done', 'Finalizado')],
                             string="Situação",
                             compute="_compute_state",
                             store=True)
    line_ids = fields.One2many('payment.order.line',
                               'payment_order_id',
                               required=True,
                               string=u'Linhas de Cobrança')
    currency_id = fields.Many2one('res.currency', string='Moeda')
    amount_total = fields.Float(string="Total",
                                compute='_compute_amount_total')
    cnab_file = fields.Binary('CNAB File', readonly=True)
    file_number = fields.Integer(u'Número sequencial do arquivo', readonly=1)
    data_emissao_cnab = fields.Datetime('Data de Emissão do CNAB')

    def _get_next_code(self):
        sequence_id = self.env['ir.sequence'].sudo().search([
            ('code', '=', 'l10n_br_.payment.cnab.sequential'),
            ('company_id', '=', self.company_id.id)
        ])
        if not sequence_id:
            sequence_id = self.env['ir.sequence'].sudo().create({
                'name':
                'Sequencia para numeração CNAB',
                'code':
                'l10n_br_.payment.cnab.sequential',
                'company_id':
                self.company_id.id,
                'suffix':
                '.REM',
                'padding':
                8,
            })
        return sequence_id.next_by_id()

    @api.multi
    @api.depends('line_ids.state')
    def _compute_state(self):
        for item in self:
            lines = item.line_ids.filtered(lambda x: x.state != 'cancelled')
            if all(line.state in ('draft', 'approved') for line in lines):
                if len(item.line_ids - lines) > 0:
                    item.state = 'done'
                else:
                    item.state = 'draft'
            elif all(line.state in ('paid', 'rejected') for line in lines):
                item.state = 'done'
            elif any(line.state == 'rejected' for line in lines):
                item.state = 'attention'
            else:
                item.state = 'open'

    @api.multi
    def unlink(self):
        for item in self:
            item.line_ids.unlink()
        return super(PaymentOrder, self).unlink()
class import_purchase_order(models.TransientModel):
    _name = 'import.purchase.order'

    import_data = fields.Binary(string="File Import")
    check_import = fields.Boolean('Allow import',default=False)

    @api.multi
    def check_import_file(self):
        for record in self:
            data = base64.b64decode(record.import_data)
            wb = open_workbook(file_contents=data)
            sheet = wb.sheet_by_index(0)

            data_list = {
                'product_not_find': [],
                'price_not_sync': [],
                'customer_not_find' : [],
            }

            for row_no in range(sheet.nrows):
                if row_no >= 1:
                    line = (
                    map(lambda row: isinstance(row.value, unicode) and row.value.encode('utf-8') or str(row.value),
                        sheet.row(row_no)))

                    product_id = self.env['product.product'].search([('default_code', '=', line[5])], limit=1)
                    if not product_id:
                        data_list['product_not_find'].append({
                            'line': row_no + 1,
                            'default_code': line[5],
                        })

                    partner_id = self.env['res.partner'].search([('name', '=', line[4]), ('supplier', '=', True)],
                                                                limit=1)
                    if not partner_id:
                        data_list['customer_not_find'].append({
                            'line': row_no + 1,
                            'customer_name': line[4],
                        })

            return {
                'name': 'Import Warning Order',
                'type': 'ir.actions.act_window',
                'res_model': 'import.warning.order',
                'view_type': 'form',
                'view_mode': 'form',
                'target': 'new',
                'context': {
                    'data': data_list,
                    'import_data' : record.import_data,
                    'name_action' : "import.purchase.order",
                    'name_model'  : "import.purchase.order",
                }
            }

    @api.model
    def default_get(self, fields):
        res = super(import_purchase_order, self).default_get(fields)
        if 'import_data' in self._context:
            res['import_data'] = self._context.get('import_data')
        return res

    @api.multi
    def import_xls(self):
        for record in self:
            if record.import_data:
                data = base64.b64decode(record.import_data)
                wb = open_workbook(file_contents=data)
                sheet = wb.sheet_by_index(0)

                so_chung_tu = False
                sale_order = False
                count_order = 0
                count_order_line = 0
                list_not_import = []


                for row_no in range(sheet.nrows):
                    if row_no >= 1:
                        line = (map(lambda row: isinstance(row.value, unicode) and row.value.encode('utf-8') or str(row.value),sheet.row(row_no)))
                        partner_id = self.env['res.partner'].search([('name', '=', line[4]), ('supplier', '=', True)],
                                                                    limit=1)
                        if not partner_id:
                            if record.check_import:
                                self.env['res.partner'].create({
                                    'name' : line[4],
                                    'customer' : False,
                                    'supplier': True,
                                })
                            else:
                                raise UserError(_('Please check file first'))

                        product_id = self.env['product.product'].search([('default_code', '=', line[5])], limit=1)
                        if not product_id:
                            if record.check_import:
                                continue
                            else:
                                raise UserError(_('Please check file first'))
                        if int(float(line[11])) != 0:
                            continue
                    if row_no == 1:
                        line = (map(lambda row: isinstance(row.value, unicode) and row.value.encode('utf-8') or str(row.value),
                            sheet.row(row_no)))
                        if line[0]:
                            so_chung_tu = line[1]
                            date_order = datetime.utcfromtimestamp((float(line[0]) - 25569) * 86400.0)
                            date_planned = datetime.utcfromtimestamp((float(line[0]) - 25569) * 86400.0)
                            invoice_date_real = False
                            if line[2]:
                                invoice_date_real = datetime.utcfromtimestamp((float(line[2]) - 25569) * 86400.0) or False
                            invoice_number_real = line[3] or False
                            partner_id = self.env['res.partner'].search([('name','=',line[4]),('supplier','=',True)],limit=1)
                            product_id = self.env['product.product'].search([('default_code','=',line[5])],limit=1)
                            product_uom_quantity = int(float(line[6]))
                            price_discount = float(line[10])
                            discount = float(line[9]) * 100
                            sale_order = self.env['purchase.order'].create({
                                'partner_id' : partner_id.id,
                                'notes' : so_chung_tu,
                                'date_order' : date_order,
                                'date_planned': date_planned,
                                'invoice_date_real' : invoice_date_real,
                                'invoice_number_real' : invoice_number_real,
                            })
                            count_order += 1
                            line_data = {
                                'product_id': product_id.id,
                                'product_uom': product_id.uom_id.id,
                                'discount': discount,
                            }
                            count_order_line += 1
                            purchase_order_line = sale_order.order_line.new(line_data)
                            purchase_order_line.onchange_product_id()
                            purchase_order_line.price_discount = price_discount
                            purchase_order_line.price_unit = price_discount*100/(100-discount)
                            purchase_order_line.product_qty = product_uom_quantity
                            tax = []
                            if line[8]:
                                number_tax = int(float(line[8])*100)
                                tax = self.env['account.tax'].search([('amount', '=', number_tax),('type_tax_use','=','purchase')], limit=1).ids
                                if not tax:
                                    tax = self.env['account.tax'].search([('amount', '=', 0),('type_tax_use','=','purchase')], limit=1).ids
                            if tax:
                                purchase_order_line.taxes_id = [(6, 0, tax)]
                                if int(float(line[8]) * 100) in [0, 5, 10]:
                                    purchase_order_line.tax_sub = int(float(line[8]) * 100)
                                else:
                                    purchase_order_line.tax_sub = 0
                            sale_order.order_line += purchase_order_line
                    if row_no > 1 :
                        line = (map(lambda row: isinstance(row.value, unicode) and row.value.encode('utf-8') or str(row.value),sheet.row(row_no)))
                        if line[0]:
                            if line[1] == so_chung_tu:
                                product_id = self.env['product.product'].search([('default_code', '=', line[5])],limit=1)
                                product_uom_quantity = int(float(line[6]))
                                price_discount = float(line[10])
                                discount = float(line[9]) * 100
                                line_data = {
                                    'product_id': product_id.id,
                                    'product_uom': product_id.uom_id.id,
                                    'discount': discount,
                                }
                                count_order_line += 1
                                purchase_order_line = sale_order.order_line.new(line_data)
                                purchase_order_line.onchange_product_id()
                                purchase_order_line.price_discount = price_discount
                                purchase_order_line.price_unit = price_discount * 100 / (100 - discount)
                                purchase_order_line.product_qty = product_uom_quantity
                                tax = []
                                if line[8]:
                                    number_tax = int(float(line[8])*100)
                                    tax = self.env['account.tax'].search([('amount', '=', number_tax),('type_tax_use','=','purchase')], limit=1).ids
                                    if not tax:
                                        tax = self.env['account.tax'].search([('amount', '=', 0),('type_tax_use','=','purchase')], limit=1).ids
                                if tax:
                                    purchase_order_line.taxes_id = [(6, 0, tax)]
                                    if int(float(line[8])*100) in [0,5,10]:
                                        purchase_order_line.tax_sub = int(float(line[8])*100)
                                    else:
                                        purchase_order_line.tax_sub = 0
                                sale_order.order_line += purchase_order_line
                            if line[1] != so_chung_tu:
                                so_chung_tu = line[1]
                                date_order = datetime.utcfromtimestamp((float(line[0]) - 25569) * 86400.0)
                                date_planned = datetime.utcfromtimestamp((float(line[0]) - 25569) * 86400.0)
                                invoice_date_real = False
                                if line[2]:
                                    invoice_date_real = datetime.utcfromtimestamp((float(line[2]) - 25569) * 86400.0) or False
                                invoice_number_real = line[3] or False
                                partner_id = self.env['res.partner'].search([('name', '=', line[4]),('supplier','=',True)],limit=1)
                                product_id = self.env['product.product'].search([('default_code', '=', line[5])],limit=1)
                                product_uom_quantity = int(float(line[6]))
                                price_discount = float(line[10])
                                discount = float(line[9]) * 100
                                sale_order = self.env['purchase.order'].create({
                                    'partner_id': partner_id.id,
                                    'notes': so_chung_tu,
                                    'date_order' : date_order,
                                    'date_planned' : date_planned,
                                    'invoice_date_real': invoice_date_real,
                                    'invoice_number_real': invoice_number_real,
                                })
                                count_order += 1
                                line_data = {
                                    'product_id': product_id.id,
                                    'product_uom': product_id.uom_id.id,
                                    'discount': discount,
                                }
                                count_order_line += 1
                                purchase_order_line = sale_order.order_line.new(line_data)
                                purchase_order_line.onchange_product_id()
                                purchase_order_line.price_discount = price_discount
                                purchase_order_line.price_unit = price_discount * 100 / (100 - discount)
                                purchase_order_line.product_qty = product_uom_quantity
                                tax = []
                                if line[8]:
                                    number_tax = int(float(line[8])*100)
                                    tax = self.env['account.tax'].search([('amount', '=', number_tax),('type_tax_use','=','purchase')], limit=1).ids
                                    if not tax:
                                        tax = self.env['account.tax'].search([('amount', '=', 0),('type_tax_use','=','purchase')], limit=1).ids
                                if tax:
                                    purchase_order_line.taxes_id = [(6, 0, tax)]
                                    if int(float(line[8])*100) in [0,5,10]:
                                        purchase_order_line.tax_sub = int(float(line[8])*100)
                                    else:
                                        purchase_order_line.tax_sub = 0
                                sale_order.order_line += purchase_order_line

                print count_order
                print count_order_line
                print list_not_import

    # @api.multi
    # def import_xls(self):
    #     for record in self:
    #         if record.import_data:
    #             data = base64.b64decode(record.import_data)
    #             wb = open_workbook(file_contents=data)
    #             sheet = wb.sheet_by_index(0)
    #             list_customer = []

                # for row_no in range(sheet.nrows):
    #
    #                 # TODO import customer
    #                 if row_no >= 1:
    #                     line = (map(lambda row: isinstance(row.value, unicode) and row.value.encode('utf-8') or str(
    #                             row.value), sheet.row(row_no)))
    #                     partner_id = self.env['res.partner'].search([('name','=',line[1])])
    #                     if partner_id:
    #                         partner_id.write({
    #                             'street'                  : line[2],
    #                             'feosco_business_license' : line[4],
    #                             'phone'                   : line[5],
    #                             'ref'                     : line[0],
    #                             'supplier': True,
    #                         })
    #                         print "----------------------------write" + str(row_no+1)
    #                     else:
    #                         partner_id.create({
    #                             'street'                    : line[2],
    #                             'feosco_business_license'   : line[4],
    #                             'phone'                     : line[5],
    #                             'ref'                       : line[0],
    #                             'name'                      : line[1],
    #                             'customer'                  : False,
    #                             'supplier': True,
    #                         })
    #                         print "**************************create" + str(row_no + 1)



                    # TODO update name product
                    # if row_no >= 1:
                    #     line = (map(lambda row: isinstance(row.value, unicode) and row.value.encode('utf-8') or str(
                    #         row.value), sheet.row(row_no)))
                    #     product_id = self.env['product.template'].search([('default_code','=',line[0])])
                    #     group_id = False
                    #     if line[11]:
                    #         group_id = self.env['product.group'].search([('name','=',line[11])])
                    #         if not group_id:
                    #             group_id = self.env['product.group'].create({
                    #                 'name' : line[11]
                    #             })
                    #     if not product_id:
                    #         raise UserError("not found" + str(row_no+1))
                    #     else:
                    #         product_id.name = line[2]
                    #         product_id.group_id = group_id.id
                    #         print row_no+1
    # @api.multi
    # def import_xls(self):
    #     for record in self:
    #         if record.import_data:
    #             data = base64.b64decode(record.import_data)
    #             wb = open_workbook(file_contents=data)
    #             sheet = wb.sheet_by_index(0)
    #             list_customer = []
    #         for row_no in range(sheet.nrows):
    #
    #             # TODO import group product sale
    #             if row_no == 0:
    #                 line = (map(lambda row: isinstance(row.value, unicode) and row.value.encode('utf-8') or str(row.value), sheet.row(row_no)))
    #                 for no in range(1, len(line)):
    #                     list_customer.append(line[no])
    #             if row_no >= 1:
    #                 line = (map(lambda row: isinstance(row.value, unicode) and row.value.encode('utf-8') or str(row.value),
    #                     sheet.row(row_no)))
    #                 group_id = self.env['product.group.sale'].search([('name','=',line[0])])
    #                 if group_id.group_line_ids:
    #                     group_id.group_line_ids = None
    #                 if not group_id:
    #                     group_id = self.env['product.group.sale'].create({
    #                         'name' : line[0]
    #                     })
    #                 for no in range(0, len(line) - 1):
    #                     line_data = {
    #                         'partner_name' : list_customer[no],
    #                         'discount'     : float(line[no+1])*100
    #                     }
    #                     group_line = group_id.group_line_ids.new(line_data)
    #                     group_id.group_line_ids += group_line
Example #5
0
class PaymentAcquirer(models.Model):
    """ Acquirer Model. Each specific acquirer can extend the model by adding
    its own fields, using the acquirer_name as a prefix for the new fields.
    Using the required_if_provider='<name>' attribute on fields it is possible
    to have required fields that depend on a specific acquirer.

    Each acquirer has a link to an ir.ui.view record that is a template of
    a button used to display the payment form. See examples in ``payment_ogone``
    and ``payment_paypal`` modules.

    Methods that should be added in an acquirer-specific implementation:

     - ``<name>_form_generate_values(self, reference, amount, currency,
       partner_id=False, partner_values=None, tx_custom_values=None)``:
       method that generates the values used to render the form button template.
     - ``<name>_get_form_action_url(self):``: method that returns the url of
       the button form. It is used for example in ecommerce application if you
       want to post some data to the acquirer.
     - ``<name>_compute_fees(self, amount, currency_id, country_id)``: computes
       the fees of the acquirer, using generic fields defined on the acquirer
       model (see fields definition).

    Each acquirer should also define controllers to handle communication between
    OpenERP and the acquirer. It generally consists in return urls given to the
    button form and that the acquirer uses to send the customer back after the
    transaction, with transaction details given as a POST request.
    """
    _name = 'payment.acquirer'
    _description = 'Payment Acquirer'
    _order = 'website_published desc, sequence, name'

    name = fields.Char('Name', required=True, translate=True)
    description = fields.Html('Description')
    sequence = fields.Integer('Sequence',
                              default=10,
                              help="Determine the display order")
    provider = fields.Selection(selection=[('manual', 'Manual Configuration')],
                                string='Provider',
                                default='manual',
                                required=True)
    company_id = fields.Many2one(
        'res.company',
        'Company',
        default=lambda self: self.env.user.company_id.id,
        required=True)
    view_template_id = fields.Many2one('ir.ui.view',
                                       'Form Button Template',
                                       required=True)
    registration_view_template_id = fields.Many2one(
        'ir.ui.view',
        'S2S Form Template',
        domain=[('type', '=', 'qweb')],
        help="Template for method registration")
    environment = fields.Selection([('test', 'Test'), ('prod', 'Production')],
                                   string='Environment',
                                   default='test',
                                   oldname='env',
                                   required=True)
    website_published = fields.Boolean(
        'Visible in Portal / Website',
        copy=False,
        help="Make this payment acquirer available (Customer invoices, etc.)")
    # Formerly associated to `authorize` option from auto_confirm
    capture_manually = fields.Boolean(
        string="Capture Amount Manually",
        help="Capture the amount from Odoo, when the delivery is completed.")
    # Formerly associated to `generate_and_pay_invoice` option from auto_confirm
    journal_id = fields.Many2one(
        'account.journal',
        'Payment Journal',
        domain=[('type', 'in', ['bank', 'cash'])],
        default=lambda self: self.env['account.journal'].search(
            [('type', 'in', ['bank', 'cash'])], limit=1),
        help=
        """Payments will be registered into this journal. If you get paid straight on your bank account,
                select your bank account. If you get paid in batch for several transactions, create a specific
                payment journal for this payment acquirer to easily manage the bank reconciliation. You hold
                the amount in a temporary transfer account of your books (created automatically when you create
                the payment journal). Then when you get paid on your bank account by the payment acquirer, you
                reconcile the bank statement line with this temporary transfer account. Use reconciliation
                templates to do it in one-click.""")
    specific_countries = fields.Boolean(
        string="Specific Countries",
        help=
        "If you leave it empty, the payment acquirer will be available for all the countries."
    )
    country_ids = fields.Many2many(
        'res.country',
        'payment_country_rel',
        'payment_id',
        'country_id',
        'Countries',
        help=
        "This payment gateway is available for selected countries. If none is selected it is available for all countries."
    )

    pre_msg = fields.Html(
        'Help Message',
        translate=True,
        help='Message displayed to explain and help the payment process.')
    post_msg = fields.Html(
        'Thanks Message',
        translate=True,
        help='Message displayed after having done the payment process.')
    pending_msg = fields.Html(
        'Pending Message',
        translate=True,
        default=lambda s:
        _('<i>Pending,</i> Your online payment has been successfully processed. But your order is not validated yet.'
          ),
        help=
        'Message displayed, if order is in pending state after having done the payment process.'
    )
    done_msg = fields.Html(
        'Done Message',
        translate=True,
        default=lambda s:
        _('<i>Done,</i> Your online payment has been successfully processed. Thank you for your order.'
          ),
        help=
        'Message displayed, if order is done successfully after having done the payment process.'
    )
    cancel_msg = fields.Html(
        'Cancel Message',
        translate=True,
        default=lambda s: _('<i>Cancel,</i> Your payment has been cancelled.'),
        help='Message displayed, if order is cancel during the payment process.'
    )
    error_msg = fields.Html(
        'Error Message',
        translate=True,
        default=lambda s:
        _('<i>Error,</i> Please be aware that an error occurred during the transaction. The order has been confirmed but will not be paid. Do not hesitate to contact us if you have any questions on the status of your order.'
          ),
        help='Message displayed, if error is occur during the payment process.'
    )
    save_token = fields.Selection(
        [('none', 'Never'), ('ask', 'Let the customer decide'),
         ('always', 'Always')],
        string='Save Cards',
        default='none',
        help=
        "This option allows customers to save their credit card as a payment token and to reuse it for a later purchase. "
        "If you manage subscriptions (recurring invoicing), you need it to automatically charge the customer when you "
        "issue an invoice.")
    token_implemented = fields.Boolean('Saving Card Data supported',
                                       compute='_compute_feature_support',
                                       search='_search_is_tokenized')
    authorize_implemented = fields.Boolean('Authorize Mechanism Supported',
                                           compute='_compute_feature_support')
    fees_implemented = fields.Boolean('Fees Computation Supported',
                                      compute='_compute_feature_support')
    fees_active = fields.Boolean('Add Extra Fees')
    fees_dom_fixed = fields.Float('Fixed domestic fees')
    fees_dom_var = fields.Float('Variable domestic fees (in percents)')
    fees_int_fixed = fields.Float('Fixed international fees')
    fees_int_var = fields.Float('Variable international fees (in percents)')

    # TDE FIXME: remove that brol
    module_id = fields.Many2one('ir.module.module',
                                string='Corresponding Module')
    module_state = fields.Selection(selection=ir_module.STATES,
                                    string='Installation State',
                                    related='module_id.state')

    image = fields.Binary(
        "Image",
        attachment=True,
        help=
        "This field holds the image used for this provider, limited to 1024x1024px"
    )
    image_medium = fields.Binary(
        "Medium-sized image",
        attachment=True,
        help="Medium-sized image of this provider. 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 image",
        attachment=True,
        help="Small-sized image of this provider. It is automatically "
        "resized as a 64x64px image, with aspect ratio preserved. "
        "Use this field anywhere a small image is required.")

    payment_icon_ids = fields.Many2many('payment.icon',
                                        string='Supported Payment Icons')
    payment_flow = fields.Selection(
        selection=[('form', 'Redirection to the acquirer website'),
                   ('s2s', 'Payment from Odoo')],
        default='form',
        required=True,
        string='Payment Flow',
        help=
        """Note: Subscriptions does not take this field in account, it uses server to server by default."""
    )

    def _search_is_tokenized(self, operator, value):
        tokenized = self._get_feature_support()['tokenize']
        if (operator, value) in [('=', True), ('!=', False)]:
            return [('provider', 'in', tokenized)]
        return [('provider', 'not in', tokenized)]

    @api.multi
    def _compute_feature_support(self):
        feature_support = self._get_feature_support()
        for acquirer in self:
            acquirer.fees_implemented = acquirer.provider in feature_support[
                'fees']
            acquirer.authorize_implemented = acquirer.provider in feature_support[
                'authorize']
            acquirer.token_implemented = acquirer.provider in feature_support[
                'tokenize']

    @api.multi
    def _check_required_if_provider(self):
        """ If the field has 'required_if_provider="<provider>"' attribute, then it
        required if record.provider is <provider>. """
        for acquirer in self:
            if any(
                    getattr(f, 'required_if_provider', None) ==
                    acquirer.provider and not acquirer[k]
                    for k, f in self._fields.items()):
                return False
        return True

    _constraints = [
        (_check_required_if_provider, 'Required fields not filled', []),
    ]

    def _get_feature_support(self):
        """Get advanced feature support by provider.

        Each provider should add its technical in the corresponding
        key for the following features:
            * fees: support payment fees computations
            * authorize: support authorizing payment (separates
                         authorization and capture)
            * tokenize: support saving payment data in a payment.tokenize
                        object
        """
        return dict(authorize=[], tokenize=[], fees=[])

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

    @api.multi
    def write(self, vals):
        image_resize_images(vals)
        return super(PaymentAcquirer, self).write(vals)

    @api.multi
    def toggle_website_published(self):
        self.write({'website_published': not self.website_published})
        return True

    @api.multi
    def get_form_action_url(self):
        """ Returns the form action URL, for form-based acquirer implementations. """
        if hasattr(self, '%s_get_form_action_url' % self.provider):
            return getattr(self, '%s_get_form_action_url' % self.provider)()
        return False

    def _get_available_payment_input(self, partner=None, company=None):
        """ Generic (model) method that fetches available payment mechanisms
        to use in all portal / eshop pages that want to use the payment form.

        It contains

         * form_acquirers: record set of acquirers based on a local form that
                           sends customer to the acquirer website;
         * s2s_acquirers: reset set of acquirers that send customer data to
                          acquirer without redirecting to any other website;
         * pms: record set of stored credit card data (aka payment.token)
                connected to a given partner to allow customers to reuse them """
        if not company:
            company = self.env.user.company_id
        if not partner:
            partner = self.env.user.partner_id
        active_acquirers = self.sudo().search([
            ('website_published', '=', True), ('company_id', '=', company.id)
        ])
        form_acquirers = active_acquirers.filtered(
            lambda acq: acq.payment_flow == 'form' and acq.view_template_id)
        s2s_acquirers = active_acquirers.filtered(
            lambda acq: acq.payment_flow == 's2s' and acq.
            registration_view_template_id)
        return {
            'form_acquirers':
            form_acquirers,
            's2s_acquirers':
            s2s_acquirers,
            'pms':
            self.env['payment.token'].search([('partner_id', '=', partner.id),
                                              ('acquirer_id', 'in',
                                               s2s_acquirers.ids)]),
        }

    @api.multi
    def render(self,
               reference,
               amount,
               currency_id,
               partner_id=False,
               values=None):
        """ Renders the form template of the given acquirer as a qWeb template.
        :param string reference: the transaction reference
        :param float amount: the amount the buyer has to pay
        :param currency_id: currency id
        :param dict partner_id: optional partner_id to fill values
        :param dict values: a dictionary of values for the transction that is
        given to the acquirer-specific method generating the form values

        All templates will receive:

         - acquirer: the payment.acquirer browse record
         - user: the current user browse record
         - currency_id: id of the transaction currency
         - amount: amount of the transaction
         - reference: reference of the transaction
         - partner_*: partner-related values
         - partner: optional partner browse record
         - 'feedback_url': feedback URL, controler that manage answer of the acquirer (without base url) -> FIXME
         - 'return_url': URL for coming back after payment validation (wihout base url) -> FIXME
         - 'cancel_url': URL if the client cancels the payment -> FIXME
         - 'error_url': URL if there is an issue with the payment -> FIXME
         - context: Odoo context

        """
        if values is None:
            values = {}

        # reference and amount
        values.setdefault('reference', reference)
        amount = float_round(amount, 2)
        values.setdefault('amount', amount)

        # currency id
        currency_id = values.setdefault('currency_id', currency_id)
        if currency_id:
            currency = self.env['res.currency'].browse(currency_id)
        else:
            currency = self.env.user.company_id.currency_id
        values['currency'] = currency

        # Fill partner_* using values['partner_id'] or partner_id argument
        partner_id = values.get('partner_id', partner_id)
        billing_partner_id = values.get('billing_partner_id', partner_id)
        if partner_id:
            partner = self.env['res.partner'].browse(partner_id)
            if partner_id != billing_partner_id:
                billing_partner = self.env['res.partner'].browse(
                    billing_partner_id)
            else:
                billing_partner = partner
            values.update({
                'partner':
                partner,
                'partner_id':
                partner_id,
                'partner_name':
                partner.name,
                'partner_lang':
                partner.lang,
                'partner_email':
                partner.email,
                'partner_zip':
                partner.zip,
                'partner_city':
                partner.city,
                'partner_address':
                _partner_format_address(partner.street, partner.street2),
                'partner_country_id':
                partner.country_id.id,
                'partner_country':
                partner.country_id,
                'partner_phone':
                partner.phone,
                'partner_state':
                partner.state_id,
                'billing_partner':
                billing_partner,
                'billing_partner_id':
                billing_partner_id,
                'billing_partner_name':
                billing_partner.name,
                'billing_partner_lang':
                billing_partner.lang,
                'billing_partner_email':
                billing_partner.email,
                'billing_partner_zip':
                billing_partner.zip,
                'billing_partner_city':
                billing_partner.city,
                'billing_partner_address':
                _partner_format_address(billing_partner.street,
                                        billing_partner.street2),
                'billing_partner_country_id':
                billing_partner.country_id.id,
                'billing_partner_country':
                billing_partner.country_id,
                'billing_partner_phone':
                billing_partner.phone,
                'billing_partner_state':
                billing_partner.state_id,
            })
        if values.get('partner_name'):
            values.update({
                'partner_first_name':
                _partner_split_name(values.get('partner_name'))[0],
                'partner_last_name':
                _partner_split_name(values.get('partner_name'))[1],
            })
        if values.get('billing_partner_name'):
            values.update({
                'billing_partner_first_name':
                _partner_split_name(values.get('billing_partner_name'))[0],
                'billing_partner_last_name':
                _partner_split_name(values.get('billing_partner_name'))[1],
            })

        # Fix address, country fields
        if not values.get('partner_address'):
            values['address'] = _partner_format_address(
                values.get('partner_street', ''),
                values.get('partner_street2', ''))
        if not values.get('partner_country') and values.get(
                'partner_country_id'):
            values['country'] = self.env['res.country'].browse(
                values.get('partner_country_id'))
        if not values.get('billing_partner_address'):
            values['billing_address'] = _partner_format_address(
                values.get('billing_partner_street', ''),
                values.get('billing_partner_street2', ''))
        if not values.get('billing_partner_country') and values.get(
                'billing_partner_country_id'):
            values['billing_country'] = self.env['res.country'].browse(
                values.get('billing_partner_country_id'))

        # compute fees
        fees_method_name = '%s_compute_fees' % self.provider
        if hasattr(self, fees_method_name):
            fees = getattr(self,
                           fees_method_name)(values['amount'],
                                             values['currency_id'],
                                             values.get('partner_country_id'))
            values['fees'] = float_round(fees, 2)

        # call <name>_form_generate_values to update the tx dict with acqurier specific values
        cust_method_name = '%s_form_generate_values' % (self.provider)
        if hasattr(self, cust_method_name):
            method = getattr(self, cust_method_name)
            values = method(values)

        values.update({
            'tx_url':
            self._context.get('tx_url', self.get_form_action_url()),
            'submit_class':
            self._context.get('submit_class', 'btn btn-link'),
            'submit_txt':
            self._context.get('submit_txt'),
            'acquirer':
            self,
            'user':
            self.env.user,
            'context':
            self._context,
            'type':
            values.get('type') or 'form',
        })
        values.setdefault('return_url', False)

        _logger.info(
            'payment.acquirer.render: <%s> values rendered for form payment:\n%s',
            self.provider, pprint.pformat(values))
        return self.view_template_id.render(values, engine='ir.qweb')

    def get_s2s_form_xml_id(self):
        if self.registration_view_template_id:
            model_data = self.env['ir.model.data'].search([
                ('model', '=', 'ir.ui.view'),
                ('res_id', '=', self.registration_view_template_id.id)
            ])
            return ('%s.%s') % (model_data.module, model_data.name)
        return False

    @api.multi
    def s2s_process(self, data):
        cust_method_name = '%s_s2s_form_process' % (self.provider)
        if not self.s2s_validate(data):
            return False
        if hasattr(self, cust_method_name):
            # As this method may be called in JSON and overriden in various addons
            # let us raise interesting errors before having stranges crashes
            if not data.get('partner_id'):
                raise ValueError(
                    _('Missing partner reference when trying to create a new payment token'
                      ))
            method = getattr(self, cust_method_name)
            return method(data)
        return True

    @api.multi
    def s2s_validate(self, data):
        cust_method_name = '%s_s2s_form_validate' % (self.provider)
        if hasattr(self, cust_method_name):
            method = getattr(self, cust_method_name)
            return method(data)
        return True

    @api.multi
    def toggle_environment_value(self):
        prod = self.filtered(lambda acquirer: acquirer.environment == 'prod')
        prod.write({'environment': 'test'})
        (self - prod).write({'environment': 'prod'})

    @api.multi
    def button_immediate_install(self):
        # TDE FIXME: remove that brol
        if self.module_id and self.module_state != 'installed':
            self.module_id.button_immediate_install()
            return {
                'type': 'ir.actions.client',
                'tag': 'reload',
            }
class import_emp_img_wizard(models.TransientModel):
    _name="import.emp.img.wizard"

    import_type = fields.Selection([
        ('csv','CSV File'),
        ('excel','Excel File')
        ], default="csv", string="Import File Type", required=True)
    file = fields.Binary(string="File",required=True)
    emp_by = fields.Selection([
        ('name','Name'),
        ('db_id','ID'),
        ('id_no','Identification No')
        ], default="name", string = "Employee By", required = True) 
   
    
    @api.multi
    def show_success_msg(self,counter,skipped_line_no):
        
        #to close the current active wizard        
        action = self.env.ref('sh_all_in_one_import.sh_import_emp_img_action').read()[0]
        action = {'type': 'ir.actions.act_window_close'} 
        
        #open the new success message box    
        view = self.env.ref('sh_message.sh_message_wizard')
        view_id = view and view.id or False                                   
        context = dict(self._context or {})
        dic_msg = str(counter) + " Records imported successfully"
        if skipped_line_no:
            dic_msg = dic_msg + "\nNote:"
        for k,v in skipped_line_no.items():
            dic_msg = dic_msg + "\nRow No " + k + " " + v + " "
        context['message'] = dic_msg   
        return {
            'name': 'Success',
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'sh.message.wizard',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'context': context,
            }   

    
    @api.multi
    def import_emp_img_apply(self):

        hr_emp_obj = self.env['hr.employee']
        #perform import lead
        if self and self.file:
            #For CSV
            if self.import_type == 'csv':
                counter = 1
                skipped_line_no = {}
                try:
                    file = str(base64.decodestring(self.file).decode('utf-8'))
                    myreader = csv.reader(file.splitlines())
                    skip_header=True
                    
                    for row in myreader:
                        try:
                            if skip_header:
                                skip_header=False
                                counter = counter + 1
                                continue
                            
                            if row[0] not in (None,"") and row[1].strip() not in (None,""):
                                vals={}   
                                image_path = row[1].strip()
                                if "http://" in image_path or "https://" in image_path:
                                    try:
                                        r = requests.get(image_path)
                                        if r and r.content:
                                            image_base64 = base64.encodestring(r.content) 
                                            vals.update({'image': image_base64})
                                        else:
                                            skipped_line_no[str(counter)] = " - URL not correct or check your image size. "                                            
                                            counter = counter + 1                                                
                                            continue
                                    except Exception as e:
                                        skipped_line_no[str(counter)] = " - URL not correct or check your image size " + ustr(e)   
                                        counter = counter + 1 
                                        continue                                              
                                    
                                else:
                                    try:
                                        with open(image_path, 'rb') as image:
                                            image.seek(0)
                                            binary_data = image.read()
                                            image_base64 = codecs.encode(binary_data, 'base64')     
                                            if image_base64:
                                                vals.update({'image': image_base64})
                                            else:
                                                skipped_line_no[str(counter)] = " - Could not find the image or please make sure it is accessible to this app. "                                            
                                                counter = counter + 1                                                
                                                continue                                                                       
                                    except Exception as e:
                                        skipped_line_no[str(counter)] = " - Could not find the image or please make sure it is accessible to this app. " + ustr(e)   
                                        counter = counter + 1 
                                        continue  
                                

                                field_nm = 'name'
                                if self.emp_by == 'name':
                                    field_nm = 'name'
                                elif self.emp_by == 'db_id':
                                    field_nm = 'id'
                                elif self.emp_by == 'id_no':
                                    field_nm = 'identification_id'

                                                                 
                                
                                search_emp = hr_emp_obj.search([(field_nm,'=',row[0] )], limit = 1)                             
                                if search_emp:
                                    search_emp.write(vals)
                                else:
                                    skipped_line_no[str(counter)] = " - Employee not found. "   
                                    
                                counter = counter + 1
                            else:
                                skipped_line_no[str(counter)] = " - Employee or URL/Path field is empty. "   
                                counter = counter + 1 
                                continue                                                                     

                        except Exception as e:
                            skipped_line_no[str(counter)]=" - Value is not valid. " + ustr(e)   
                            counter = counter + 1 
                            continue          
                            
                except Exception as e:
                    raise UserError(_("Sorry, Your csv file does not match with our format " + ustr(e) ))
                
                if counter > 1:
                    completed_records = (counter - len(skipped_line_no)) - 2
                    res = self.show_success_msg(completed_records, skipped_line_no)
                    return res

            
            #For Excel
            if self.import_type == 'excel':
                counter = 1
                skipped_line_no = {}                  
                try:
                    wb = xlrd.open_workbook(file_contents=base64.decodestring(self.file))
                    sheet = wb.sheet_by_index(0)     
                    skip_header = True    
                    for row in range(sheet.nrows):
                        try:
                            if skip_header:
                                skip_header = False
                                counter = counter + 1
                                continue
                            
                            if sheet.cell(row,0).value not in (None,"") and sheet.cell(row,1).value.strip() not in (None,""):
                                vals={}   
                                image_path = sheet.cell(row,1).value.strip()
                                if "http://" in image_path or "https://" in image_path:
                                    try:
                                        r = requests.get(image_path)
                                        if r and r.content:
                                            image_base64 = base64.encodestring(r.content) 
                                            vals.update({'image': image_base64})
                                        else:
                                            skipped_line_no[str(counter)] = " - URL not correct or check your image size. "                                            
                                            counter = counter + 1                                                
                                            continue
                                    except Exception as e:
                                        skipped_line_no[str(counter)] = " - URL not correct or check your image size " + ustr(e)   
                                        counter = counter + 1 
                                        continue                                              
                                    
                                else:
                                    try:
                                        with open(image_path, 'rb') as image:
                                            image.seek(0)
                                            binary_data = image.read()
                                            image_base64 = codecs.encode(binary_data, 'base64')     
                                            if image_base64:
                                                vals.update({'image': image_base64})
                                            else:
                                                skipped_line_no[str(counter)] = " - Could not find the image or please make sure it is accessible to this app. "                                            
                                                counter = counter + 1                                                
                                                continue                                                                       
                                    except Exception as e:
                                        skipped_line_no[str(counter)] = " - Could not find the image or please make sure it is accessible to this app. " + ustr(e)   
                                        counter = counter + 1 
                                        continue  
                                

                                search_str = sheet.cell(row,0).value
                                field_nm = 'name'
                                if self.emp_by == 'name':
                                    field_nm = 'name'
                                elif self.emp_by == 'db_id':
                                    field_nm = 'id'
                                    str_id = str(sheet.cell(row,0).value)
                                    str_id = str_id.split('.', 1)[0]
                                    search_str = int(str_id)                                     
                                    
                                elif self.emp_by == 'id_no':
                                    field_nm = 'identification_id'

                           
                                
                                search_emp = hr_emp_obj.search([(field_nm,'=',search_str )], limit = 1)                             
                                if search_emp:
                                    search_emp.write(vals)
                                else:
                                    skipped_line_no[str(counter)] = " - Employee not found. "   
                                    
                                counter = counter + 1
                            else:
                                skipped_line_no[str(counter)] = " - Employee or URL/Path field is empty. "   
                                counter = counter + 1 
                                continue                                                                     

                        except Exception as e:
                            skipped_line_no[str(counter)]=" - Value is not valid. " + ustr(e)   
                            counter = counter + 1 
                            continue          
                            
                except Exception as e:
                    raise UserError(_("Sorry, Your excel file does not match with our format " + ustr(e) ))
                
                if counter > 1:
                    completed_records = (counter - len(skipped_line_no)) - 2
                    res = self.show_success_msg(completed_records, skipped_line_no)
                    return res
Example #7
0
class GhuExamination(models.Model):
    _name = 'ghu_custom_mba.examination'
    _rec_name = 'name'
    _description = "Examination"

    enrollment_id = fields.Many2one(
        string=u'Enrollment',
        comodel_name='ghu_custom_mba.course_enrollment',
        ondelete='cascade',
    )

    name = fields.Char('Name', compute='_compute_name')

    @api.depends('request_date')
    def _compute_name(self):
        for rec in self:
            rec.name = rec.request_date.strftime("%b %d %Y")

    type = fields.Char('Type', size=128, required=True)

    question_title = fields.Char('Question Title', required=True)

    question = fields.Html('Question', required=True)

    request_date = fields.Date(
        string='Request Date',
        default=fields.Date.context_today,
    )

    end_date = fields.Date(string='End Date', compute='_compute_enddate')

    @api.depends('request_date')
    def _compute_enddate(self):
        for rec in self:
            rec.end_date = rec.request_date + datetime.timedelta(days=21)

    @api.one
    def isActive(self):
        if fields.Date.context_today >= self.request_date and fields.Date.context_today <= self.end_date:
            return True
        return False

    submission = fields.Binary(string='Submission', attachment=True)
    submission_filename = fields.Char(string='Submission Filename')

    # Grading section

    grade = fields.Float(string='Grade', digits=(3, 1))

    # 44 to 50 Points = Excellent (1)
    # 38 to 43 Points = Good (2)
    # 32 to 37 Points = Satisfactory (3)
    # 26 to 31 Points = Pass (4)
    # below 25 = Fail (5)
    @api.one
    @api.constrains('grade')
    def _check_grade(self):
        if self.grade > 50.0 or self.grade < 0.0:
            raise ValidationError(
                "Grading score must be between 0 and 50 points.")

    result = fields.Html(string='Comment on grading', )
Example #8
0
class StudentCertificate(models.Model):
    _name = "student.certificate"

    student_id = fields.Many2one('student.student', 'Student')
    description = fields.Char('Description')
    certi = fields.Binary('Certificate', required=True)
Example #9
0
class import_attendance(models.TransientModel):
    _name = "import.attendance"

    file = fields.Binary('File')
    file_opt = fields.Selection([('csv', 'CSV'), ('excel', 'EXCEL')])

    def import_file(self):
        if self.file_opt == 'csv':
            try:
                keys = ['name', 'check_in', 'check_out']
                csv_data = base64.b64decode(self.file)
                data_file = io.StringIO(csv_data.decode("utf-8"))
                data_file.seek(0)
                file_reader = []
                csv_reader = csv.reader(data_file, delimiter=',')
                file_reader.extend(csv_reader)
            except Exception:
                raise exceptions.Warning(
                    _("Please select an CSV/XLS file or You have selected invalid file"
                      ))
            values = {}
            for i in range(len(file_reader)):
                field = list(map(str, file_reader[i]))
                values = dict(zip(keys, field))
                if values:
                    if i == 0:
                        continue
                    else:
                        res = self._create_timesheet(values)
        else:
            try:
                fp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
                fp.write(binascii.a2b_base64(self.file))
                fp.seek(0)
                values = {}
                workbook = xlrd.open_workbook(fp.name)
                sheet = workbook.sheet_by_index(0)
                for row_no in range(sheet.nrows):
                    if row_no <= 0:
                        fields = map(lambda row: row.value.encode('utf-8'),
                                     sheet.row(row_no))
                    else:
                        line = list(
                            map(
                                lambda row: isinstance(row.value, bytes) and
                                row.value.encode('utf-8') or str(row.value),
                                sheet.row(row_no)))
                        values.update({
                            'name': line[0],
                            'check_in': line[1],
                            'check_out': line[2],
                        })
                        res = self._create_timesheet(values)
            except Exception:
                raise exceptions.Warning(
                    _("Please select an CSV/XLS file or You have selected invalid file"
                      ))

        return res

    def _create_timesheet(self, val):
        emp_id = self._find_employee(val.get('name'))
        if not emp_id:
            raise Warning('Employee Not Found')
        if not val.get('check_in'):
            raise Warning('Please Provide Sign In Time')
        if not val.get('check_out'):
            self._cr.execute(
                "insert into hr_attendance (employee_id,check_in) values (%s,%s)",
                (emp_id.id, val.get('check_in')))
        else:
            self._cr.execute(
                "insert into hr_attendance (employee_id,check_in,check_out) values (%s,%s,%s)",
                (emp_id.id, val.get('check_in'), val.get('check_out')))
        return True

    def _find_employee(self, name):
        emp_id = self.env['hr.employee'].search([('name', '=', name)])
        if emp_id:
            return emp_id
        else:
            raise Warning(_("Employee '%s' Not Found!") % ustr(name))
class PurchaseRequestReportOut(models.Model):
    _name = 'purchase.request.recap.qcf.reports'
    _description = 'purchase request recap qcf report'

    purchase_request_data = fields.Char('Name', size=256)
    file_name = fields.Binary('PR Excel Report', readonly=True)
Example #11
0
class StudentStudent(models.Model):
    ''' Defining a student information '''
    _name = 'student.student'
    _table = "student_student"
    _description = 'Student Information'
    _inherits = {'res.users': 'user_id'}

    @api.multi
    @api.depends('date_of_birth')
    def _compute_student_age(self):
        '''Method to calculate student age'''
        current_dt = datetime.today()
        for rec in self:
            if rec.date_of_birth:
                start = datetime.strptime(rec.date_of_birth,
                                          DEFAULT_SERVER_DATE_FORMAT)
                age_calc = ((current_dt - start).days / 365)
                # Age should be greater than 0
                if age_calc > 0.0:
                    rec.age = age_calc

    @api.model
    def createsql(self, vals):
        for stu in vals:
            print "vals"
            print vals
            standard_ids = self.env['school.standard'].search([
                ('all_name', '=', stu.get('standard')),
                ('school_id', '=', stu.get('school_id'))
            ])
            standard = None
            if not standard_ids:

                # res = self.env['standard.standard'].search([('name','=','standard_name')])
                standard_new = self.env['school.standard'].create({
                    'all_name':
                    stu.get('standard'),
                    'school_id':
                    stu.get('school_id'),
                })
                standard = standard_new.id
            else:
                standard = standard_ids[0].id

            print "standard"
            print standard

            print {
                'name': "aa bb",
                'barcode': stu.get('barcode'),
                'login': stu.get('barcode'),
                'password': '******',
                'company_id': int(stu.get('company_id')),
            }

            print "user_new"

            user_new = self.env['res.users'].sudo().create({
                'name':
                "AA",
                'barcode':
                stu.get('barcode'),
                'login':
                stu.get('barcode'),
                'password':
                '******',
                'company_id':
                int(stu.get('company_id')),
            })

            print user_new

            insert_id = self._cr.execute(
                "insert into student_student (user_id,school_id,state,standard_id) VALUES(%s,%s,%s,%s) "
                % (user_new.id, stu.get('school_id'), 'done', standard))
            print "insert_id"
            print insert_id
            self._cr.execute(
                "insert into library_card (code,user,type,standard_id,student_id) VALUES(%s,%s,%s,%s) "
                % (stu.get('barcode'), 'student', 78, standard, insert_id))

            self._cr.commit()
            return 1

    @api.model
    def create(self, vals):
        _logger.info("create_StudentStudent_school_school_start")
        '''Method to create user when student is created'''
        if vals.get('pid', False):
            vals['login'] = uuid.uuid1()
            vals['password'] = vals['pid']
            vals['name'] = vals['name']
        # else:
        #     raise except_orm(_('Error!'),
        #                      _('''PID not valid
        #                          so record will not be saved.'''))
        if vals.get('cmp_id', False):
            h = {
                'company_ids': [(4, vals.get('cmp_id'))],
                'company_id': vals.get('cmp_id')
            }
            vals.update(h)
        _logger.info("create_StudentStudent_school_school_start_mmm")
        res = super(StudentStudent, self).create(vals)
        _logger.info("create_StudentStudent_school_school_end")
        # Assign group to student based on condition
        emp_grp = self.env.ref('base.group_user')
        #9/19 要30秒 故取消
        # if res.state == 'draft':
        #     admission_group = self.env.ref('school.group_is_admission')
        #     new_grp_list = [admission_group.id, emp_grp.id]
        #     res.user_id.write({'groups_id': [(6, 0, new_grp_list)]})
        # elif res.state == 'done':
        #     done_student = self.env.ref('school.group_school_student')
        #     group_list = [done_student.id, emp_grp.id]
        #     res.user_id.write({'groups_id': [(6, 0, group_list)]})
        _logger.info("create_StudentStudent_after")
        return res

    @api.model
    def _get_default_image(self, is_company, colorize=False):
        '''Method to get default Image'''
        # added in try-except because import statements are in try-except
        try:
            img_path = get_module_resource('base', 'static/src/img',
                                           'avatar.png')
            with open(img_path, 'rb') as f:
                image = f.read()
            image = image_colorize(image)
            return image_resize_image_big(image.encode('base64'))
        except:
            return False

    family_con_ids = fields.One2many('student.family.contact',
                                     'family_contact_id',
                                     'Family Contact Detail',
                                     states={'done': [('readonly', True)]})
    user_id = fields.Many2one('res.users',
                              'User ID',
                              ondelete="cascade",
                              required=True)
    student_name = fields.Char('学生姓名',
                               related='user_id.name',
                               store=True,
                               readonly=True)
    pid = fields.Char('系统编号',
                      required=True,
                      default=lambda obj: obj.env['ir.sequence'].next_by_code(
                          'student.student'),
                      help='Personal IDentification Number')
    reg_code = fields.Char('Registration Code',
                           help='Student Registration Code')
    student_code = fields.Char('学号')
    contact_phone1 = fields.Char('Phone no.', )
    contact_mobile1 = fields.Char('Mobile no', )
    roll_no = fields.Integer('Roll No.', readonly=True)
    photo = fields.Binary('Photo',
                          default=lambda self: self._get_default_image(
                              self._context.get('default_is_company', False)))
    year = fields.Many2one('academic.year',
                           'Academic Year',
                           states={'done': [('readonly', True)]})
    cast_id = fields.Many2one('student.cast', 'Religion')
    relation = fields.Many2one('student.relation.master', 'Relation')

    admission_date = fields.Date('Admission Date', default=date.today())
    middle = fields.Char('Middle Name', states={'done': [('readonly', True)]})
    last = fields.Char('Surname', states={'done': [('readonly', True)]})
    gender = fields.Selection([('male', 'Male'), ('female', 'Female')],
                              'Gender',
                              states={'done': [('readonly', True)]})
    date_of_birth = fields.Date('BirthDate',
                                states={'done': [('readonly', True)]})
    mother_tongue = fields.Many2one('mother.toungue', "Mother Tongue")
    age = fields.Integer(compute='_compute_student_age',
                         string='Age',
                         readonly=True)
    maritual_status = fields.Selection([('unmarried', 'Unmarried'),
                                        ('married', 'Married')],
                                       'Marital Status',
                                       states={'done': [('readonly', True)]})
    reference_ids = fields.One2many('student.reference',
                                    'reference_id',
                                    'References',
                                    states={'done': [('readonly', True)]})
    previous_school_ids = fields.One2many(
        'student.previous.school',
        'previous_school_id',
        'Previous School Detail',
        states={'done': [('readonly', True)]})
    doctor = fields.Char('Doctor Name', states={'done': [('readonly', True)]})
    designation = fields.Char('Designation')
    doctor_phone = fields.Char('Phone')
    blood_group = fields.Char('Blood Group')
    height = fields.Float('Height', help="Hieght in C.M")
    weight = fields.Float('Weight', help="Weight in K.G")
    eye = fields.Boolean('Eyes')
    ear = fields.Boolean('Ears')
    nose_throat = fields.Boolean('Nose & Throat')
    respiratory = fields.Boolean('Respiratory')
    cardiovascular = fields.Boolean('Cardiovascular')
    neurological = fields.Boolean('Neurological')
    muskoskeletal = fields.Boolean('Musculoskeletal')
    dermatological = fields.Boolean('Dermatological')
    blood_pressure = fields.Boolean('Blood Pressure')
    remark = fields.Text('Remark', states={'done': [('readonly', True)]})
    school_id = fields.Many2one('school.school',
                                '学校',
                                states={'done': [('readonly', True)]})
    state = fields.Selection([('draft', '草稿'), ('done', '在读'),
                              ('terminate', '中断'), ('alumni', '毕业')],
                             'State',
                             readonly=True,
                             default="draft")
    history_ids = fields.One2many('student.history', 'student_id', 'History')
    certificate_ids = fields.One2many('student.certificate', 'student_id',
                                      'Certificate')
    student_discipline_line = fields.One2many('student.descipline',
                                              'student_id', 'Descipline')
    address_ids = fields.One2many('res.partner', 'student_id', 'Contacts')
    document = fields.One2many('student.document', 'doc_id', 'Documents')
    description = fields.One2many('student.description', 'des_id',
                                  'Description')
    student_id = fields.Many2one('student.student', 'Name')
    contact_phone = fields.Char('Phone No',
                                related='student_id.phone',
                                readonly=True)
    contact_mobile = fields.Char('Mobile No',
                                 related='student_id.mobile',
                                 readonly=True)
    contact_email = fields.Char('Email',
                                related='student_id.email',
                                readonly=True)
    contact_website = fields.Char('WebSite',
                                  related='student_id.website',
                                  readonly=True)
    award_list = fields.One2many('student.award', 'award_list_id',
                                 'Award List')
    student_status = fields.Selection('Status',
                                      related='student_id.state',
                                      help="Shows Status Of Student",
                                      readonly=True)
    stu_name = fields.Char('First Name', related='user_id.name', readonly=True)
    Acadamic_year = fields.Char('Academic Year',
                                related='year.name',
                                help='Academic Year',
                                readonly=True)
    division_id = fields.Many2one('standard.division', 'Division')
    medium_id = fields.Many2one('standard.medium', 'Medium')
    cmp_id = fields.Many2one('res.company',
                             'Company Name',
                             related='school_id.company_id',
                             store=True)
    standard_id = fields.Many2one('school.standard', '班级')
    parent_id = fields.Many2many('res.partner',
                                 'student_parent_rel',
                                 'student_id',
                                 'parent_id',
                                 'Parent(s)',
                                 states={'done': [('readonly', True)]})
    terminate_reason = fields.Text('Reason')
    card_import_code = fields.Char('借书卡')

    @api.multi
    def set_to_draft(self):
        '''Method to change state to draft'''
        for rec in self:
            rec.state = 'draft'
        return True

    @api.multi
    def set_alumni(self):
        '''Method to change state to alumni'''
        for rec in self:
            rec.state = 'alumni'
        return True

    @api.multi
    def set_done(self):
        '''Method to change state to done'''
        for rec in self:
            rec.state = 'done'
        return True

    @api.multi
    def admission_draft(self):
        '''Set the state to draft'''
        for rec in self:
            rec.state = 'draft'
        return True

    @api.multi
    def set_terminate(self):
        for rec in self:
            rec.state = 'terminate'
        return True

    @api.multi
    def admission_done(self):
        '''Method to confirm admission'''
        school_standard_obj = self.env['school.standard']
        ir_sequence = self.env['ir.sequence']
        student_group = self.env.ref('school.group_school_student')
        emp_group = self.env.ref('base.group_user')
        for rec in self:

            domain = [('standard_id', '=', rec.standard_id.id)]
            # Checks the standard if not defined raise error
            print domain
            if not school_standard_obj.search(domain):
                raise except_orm(_('Warning'), _('''班级未指定'''))
            # Assign group to student
            rec.user_id.write(
                {'groups_id': [(6, 0, [emp_group.id, student_group.id])]})
            # Assign roll no to student
            number = 1
            for rec_std in rec.search(domain):
                rec_std.roll_no = number
                number += 1
            # Assign registration code to student
            reg_code = ir_sequence.next_by_code('student.registration')
            registation_code = (str(rec.school_id.state_id.name) + str('/') +
                                str(rec.school_id.city) + str('/') +
                                str(rec.school_id.name) + str('/') +
                                str(reg_code))
            stu_code = ir_sequence.next_by_code('student.code')
            student_code = (str(rec.school_id.code) + str('/') +
                            str(rec.year.code) + str('/') + str(stu_code))
            rec.write({
                'state': 'done',
                'admission_date': time.strftime('%Y-%m-%d'),
                'student_code': student_code,
                'reg_code': registation_code
            })
            card = self.env['library.card'].create({
                'student_id': rec.id,
                'user': '******',
                'gt_name': rec.name,
                'book_limit': 0
            })
            rec.user_id.write({'card_id': card.id})

        return True
Example #12
0
class create_hospital_invoice_wizard(models.TransientModel):
    _name = 'create.hospital.invoice.wizard'
    _description = 'Hospital Invoice Import'

    excel = fields.Binary(u'导入系统导出的excel文件', )
    excel2 = fields.Binary(u'导入系统导出的excel文件', )
    excel3 = fields.Binary(u'导入系统导出的excel文件', )
    type = fields.Selection([('hospitalization', u'住院'),
                             ('outpatient', u'门诊')],
                            u'单据类型',
                            default='hospitalization')

    @api.multi
    def create_hospital_invoice(self):
        """
        通过Excel文件导入信息到hospital.invoice
        """
        month = self.env['hospital.month'].browse(
            self.env.context.get('active_id'))
        if not month:
            return {}
        xls_data = xlrd.open_workbook(
            file_contents=base64.decodestring(self.excel))
        table = xls_data.sheets()[0]
        #取得行数
        ncows = table.nrows
        #取得第1行数据
        colnames = table.row_values(0)
        list = []
        newcows = 0
        for rownum in range(1, ncows):
            row = table.row_values(rownum)
            if row:
                app = {}
                for i in range(len(colnames)):
                    app[colnames[i]] = row[i]
                #过滤掉不需要的行,详见销货清单的会在清单中再次导入
                if app.get(u'病人姓名') or app.get(u'姓名'):
                    list.append(app)
                    newcows += 1
        #数据读入。
        for data in range(0, newcows):
            in_xls_data = list[data]
            invoice_ids = self.env['hospital.invoice'].create({
                'name':
                in_xls_data.get(u'病人姓名') or in_xls_data.get(u'姓名'),
                'invoice':
                in_xls_data.get(u'票据号'),
                'name_id':
                in_xls_data.get(u'身份证号'),
                'pay_type':
                in_xls_data.get(u'病人类别'),
                'amount':
                float(in_xls_data.get(u'结账金额') or 0.00),
                'difference_amount':
                float(in_xls_data.get(u'尾数处理') or 0.00),
                'type':
                self.type,
                'month_id':
                month.id or '',
            })
            if invoice_ids.type == 'outpatient' and in_xls_data.get(
                    u'结账金额') != in_xls_data.get(u'应收金额'):
                invoice_ids.write({
                    'difference_amount':
                    float(in_xls_data.get(u'应收金额') - in_xls_data.get(u'结账金额'))
                })
            if in_xls_data.get(u'冲预交'):
                self.env['hospital.pay.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'冲预交',
                    'amount':
                    float(in_xls_data.get(u'冲预交') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'个人自付'):
                self.env['hospital.pay.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'个人自付',
                    'amount':
                    float(in_xls_data.get(u'个人自付') or 0.00),
                    'type':
                    self.type,
                })

            if in_xls_data.get(u'应收金额'):
                self.env['hospital.pay.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'个人自付',
                    'amount':
                    float(in_xls_data.get(u'应收金额') or 0.00),
                    'type':
                    self.type,
                })

            if in_xls_data.get(u'材料费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'材料费',
                    'amount':
                    float(in_xls_data.get(u'材料费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'床位费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'床位费',
                    'amount':
                    float(in_xls_data.get(u'床位费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'护理费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'护理费',
                    'amount':
                    float(in_xls_data.get(u'护理费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'检查费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'检查费',
                    'amount':
                    float(in_xls_data.get(u'检查费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'检验费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'检验费',
                    'amount':
                    float(in_xls_data.get(u'检验费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'输氧费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'输氧费',
                    'amount':
                    float(in_xls_data.get(u'输氧费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'西药费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'西药费',
                    'amount':
                    float(in_xls_data.get(u'西药费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'诊查费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'诊查费',
                    'amount':
                    float(in_xls_data.get(u'诊查费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'治疗费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'治疗费',
                    'amount':
                    float(in_xls_data.get(u'治疗费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'中成药费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'中成药费',
                    'amount':
                    float(in_xls_data.get(u'中成药费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'中草药费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'中草药费',
                    'amount':
                    float(in_xls_data.get(u'中草药费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'其他费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'其他费',
                    'amount':
                    float(in_xls_data.get(u'其他费') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'手术费'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'手术费',
                    'amount':
                    float(in_xls_data.get(u'手术费') or 0.00),
                    'type':
                    self.type,
                })

            if in_xls_data.get(u'其他'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'其他',
                    'amount':
                    float(in_xls_data.get(u'其他') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'检验'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'检验',
                    'amount':
                    float(in_xls_data.get(u'检验') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'中成药'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'中成药',
                    'amount':
                    float(in_xls_data.get(u'中成药') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'西药'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'西药',
                    'amount':
                    float(in_xls_data.get(u'西药') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'检查'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'检查',
                    'amount':
                    float(in_xls_data.get(u'检查') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'卫生材料'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'卫生材料',
                    'amount':
                    float(in_xls_data.get(u'卫生材料') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'治疗'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'治疗',
                    'amount':
                    float(in_xls_data.get(u'治疗') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'中草药'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'中草药',
                    'amount':
                    float(in_xls_data.get(u'中草药') or 0.00),
                    'type':
                    self.type,
                })
            if in_xls_data.get(u'护理'):
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_ids.id,
                    'name':
                    u'护理',
                    'amount':
                    float(in_xls_data.get(u'护理') or 0.00),
                    'type':
                    self.type,
                })

    def excel_date(self, data):
        #将excel日期改为正常日期
        if type(data) in (int, float):
            year, month, day, hour, minute, second = xlrd.xldate_as_tuple(
                data, 0)
            py_date = datetime.datetime(year, month, day, hour, minute, second)
        else:
            py_date = data
        return py_date

    @api.multi
    def synchro_hospital_invoice(self):
        month = self.env['hospital.month'].browse(
            self.env.context.get('active_id'))
        if not month:
            return {}
        conn = self.createConnection()
        self.create_hospital_invoice2(conn, self.type, month)
        self.closeConnection(conn)

    @api.multi
    def createConnection(self):
        if config['amj_server'] and config['amj_server'] != 'None':
            amj_server = config['amj_server']
        else:
            raise Exception('医院服务器没有找到.')
        if config['amj_user'] and config['amj_user'] != 'None':
            amj_user = config['amj_user']
        else:
            raise Exception('医院用户没有找到.')
        if config['amj_password'] and config['amj_password'] != 'None':
            amj_password = config['amj_password']
        else:
            raise Exception('医院 用户密码没有找到.')
        if config['amj_database'] and config['amj_database'] != 'None':
            amj_database = config['amj_database']
        else:
            raise Exception('医院数据库没有找到.')
        conn = pymssql.connect(server=amj_server,
                               user=amj_user,
                               password=amj_password,
                               database=amj_database,
                               charset='utf8')
        return conn

    # 关闭数据库连接。
    @api.multi
    def closeConnection(self, conn):
        conn.close()

    # 创建发票数据
    @api.multi
    def create_hospital_invoice2(self, conn, type, period):
        cursor = conn.cursor()
        if type == 'outpatient':
            sql = "select vak01,vaa01,fab03,vak08,vak07,vaa07 from VAK1 WHERE vak06='2' and vak13>='%s' and vak13<'%s' ;"
        else:
            sql = "select vak01,vaa01,fab03,vak08,vak07,vaa07 from VAK1 WHERE vak06>'2' and vak13>='%s' and vak13<'%s' ;"
        star_date, end_date = self.env[
            'finance.period'].get_period_month_date_range(period.name)
        star_date = '%s 00:00:00' % (star_date)
        end_date = '%s 23:59:59' % (end_date)
        cursor.execute(sql % (star_date, end_date))
        invoice_ids = cursor.fetchall()
        for invoice in invoice_ids:
            internal_id, coustorm_id, invoice_name, amount, difference_amount, caseid = invoice
            name, name_id = self.search_coustorm(conn, coustorm_id)
            if self.search_pay_type(conn, caseid):
                pay_type = self.search_pay_type(conn, caseid)[0]
            else:
                pay_type = ''
            invoice_id = self.env['hospital.invoice'].create({
                'internal_id':
                internal_id,
                'name':
                name.encode('latin-1').decode('gbk'),
                'invoice':
                invoice_name,
                'name_id':
                name_id and name_id.encode('latin-1').decode('gbk') or '',
                'pay_type':
                pay_type.encode('latin-1').decode('gbk'),
                'amount':
                amount,
                'difference_amount':
                difference_amount,
                'type':
                self.type,
                'month_id':
                period.id or '',
            })
            if type == 'outpatient':
                self.crate_hospital_invoice_cost2(conn, invoice_id,
                                                  internal_id)
            else:
                self.crate_hospital_invoice_cost(conn, invoice_id, internal_id)

            self.crate_hospital_invoice_pay(conn, invoice_id, internal_id)
            self.crate_hospital_invoice_line(conn, invoice_id)

    @api.multi
    def crate_hospital_invoice_line(self, conn, invoice_id):
        # 合并正负发票
        invoice_line = []
        for line in invoice_id.cost_ids:
            if line.cost_type in invoice_line:
                old_amount = self.env['hospital.invoice.line'].search(
                    [('invoice_id', '=', invoice_id.id),
                     ('name', '=', line.cost_type)],
                    limit=1)
                amount = old_amount.amount + line.amount
                old_amount.write({'amount': amount})
            else:
                self.env['hospital.invoice.line'].create({
                    'invoice_id':
                    invoice_id.id,
                    'name':
                    line.cost_type,
                    'amount':
                    line.amount,
                    'type':
                    invoice_id.type,
                })
                invoice_line.append(line.cost_type)

    @api.multi
    def crate_hospital_invoice_cost(self, conn, invoice_id, internal_id):
        cursor = conn.cursor()
        sql = "select BBY01,VAJ36,VAJ35,VAJ25,VAJ46 from VAJ2 WHERE ACF01 = '2' AND vak01='%s';"
        cursor.execute(sql % internal_id)
        cost_ids = cursor.fetchall()
        for cost_id in cost_ids:
            m, amount, unit, number, cost_time = cost_id
            code, name, name2 = self.search_cost_nameall(conn, m)
            cost_type = self.search_cost_type(conn, code)[0]
            self.env['hospital.invoice.cost'].create({
                'invoice_id':
                invoice_id.id,
                'cost_type':
                cost_type.encode('latin-1').decode('gbk'),
                'name':
                name.encode('latin-1').decode('gbk'),
                'name2':
                name2 and name2.encode('latin-1').decode('gbk') or '',
                'number':
                number,
                'unit':
                unit.encode('latin-1').decode('gbk'),
                'amount':
                amount,
                'cost_time':
                cost_time,
                'type':
                self.type,
            })

    @api.multi
    def crate_hospital_invoice_cost2(self, conn, invoice_id, internal_id):
        cursor = conn.cursor()
        sql = "select BBY01,VAJ38,VAJ35,VAJ25,VAJ46 from VAJ1 WHERE VAK01='%s';"
        cursor.execute(sql % internal_id)
        cost_ids = cursor.fetchall()
        for cost_id in cost_ids:
            m, amount, unit, number, cost_time = cost_id
            code, name, name2 = self.search_cost_nameall(conn, m)
            cost_type = self.search_cost_type(conn, code)[0]
            self.env['hospital.invoice.cost'].create({
                'invoice_id':
                invoice_id.id,
                'cost_type':
                cost_type.encode('latin-1').decode('gbk'),
                'name':
                name.encode('latin-1').decode('gbk'),
                'name2':
                name2 and name2.encode('latin-1').decode('gbk') or '',
                'number':
                number,
                'unit':
                unit.encode('latin-1').decode('gbk'),
                'amount':
                amount,
                'cost_time':
                cost_time,
                'type':
                self.type,
            })

    @api.multi
    def crate_hospital_invoice_pay(self, conn, invoice_id, internal_id):
        cursor = conn.cursor()
        sql = "select VBL14,VBL13 from VBL1 WHERE vak01='%s';"
        cursor.execute(sql % internal_id)
        pay_ids = cursor.fetchall()
        for pay_id in pay_ids:
            name, amount = pay_id
            self.env['hospital.pay.line'].create({
                'invoice_id':
                invoice_id.id,
                'name':
                name.encode('latin-1').decode('gbk'),
                'amount':
                amount,
                'type':
                self.type,
            })
        return True

    # 查询病人信息数据
    @api.multi
    def search_coustorm(self, conn, name_id):
        cursor = conn.cursor()
        sql = "select VAA05,VAA15 from VAA1 WHERE VAA01='%s';"
        cursor.execute(sql % name_id)
        name_code = cursor.fetchone()
        return name_code

    # 查询病人信息数据
    @api.multi
    def search_pay_type(self, conn, name_id):
        cursor = conn.cursor()
        sql = "select BDP02 from VAE1 WHERE VAE01='%s';"
        cursor.execute(sql % name_id)
        name_code = cursor.fetchone()
        return name_code

    # 查询药品信息数据
    @api.multi
    def search_cost_nameall(self, conn, name_id):
        cursor = conn.cursor()
        sql = "select ABF01,BBY05,bby06 from BBY1 WHERE BBY01='%s';"
        cursor.execute(sql % name_id)
        name_code = cursor.fetchone()
        return name_code

    # 查询药品费别数据
    @api.multi
    def search_cost_type(self, conn, name_id):
        cursor = conn.cursor()
        sql = "select ABF02 from ABF1  WHERE ABF01='%s';"
        cursor.execute(sql % name_id)
        name_code = cursor.fetchone()
        return name_code
Example #13
0
class ResConfigSettings(models.TransientModel):

    _inherit = 'res.config.settings'

    theme_background_image = fields.Binary(
        related="company_id.background_image", readonly=False, required=True)

    theme_color_brand = fields.Char(string="Brand Color")

    theme_color_primary = fields.Char(string="Primary Color")

    @api.multi
    def set_values(self):
        res = super(ResConfigSettings, self).set_values()
        self._save_scss_values()
        return res

    @api.model
    def get_values(self):
        res = super(ResConfigSettings, self).get_values()
        res.update(self._get_scss_values())
        return res

    def _get_custom_scss_url(self, url, xmlid):
        return self._build_custom_scss_url(url.rsplit(".", 1), xmlid)

    def _build_custom_scss_url(self, url_parts, xmlid):
        return "%s.custom.%s.%s" % (url_parts[0], xmlid, url_parts[1])

    def _get_custom_attachment(self, url):
        return self.env["ir.attachment"].search([("url", '=', url)])

    def _get_scss_values(self):
        custom_url = self._get_custom_scss_url(SCSS_URL, XML_ID)
        custom_attachment = self._get_custom_attachment(custom_url)
        if custom_attachment.exists():
            content = str(base64.b64decode(custom_attachment.datas))
            brand = re.search(r'o-brand-odoo\:?\s(.*?);', content)
            primary = re.search(r'o-brand-primary\:?\s(.*?);', content)
            return {
                'theme_color_brand': brand and brand.group(1) or "#243742",
                'theme_color_primary': primary and primary.group(1)
                or "#5D8DA8",
            }
        else:
            return {
                'theme_color_brand': "#243742",
                'theme_color_primary': "#5D8DA8",
            }

    def _build_custom_scss_template(self):
        return TEMPLATE.format(self.theme_color_brand or "#243742",
                               self.theme_color_primary or "#5D8DA8")

    def _save_scss_values(self):
        custom_url = self._get_custom_scss_url(SCSS_URL, XML_ID)
        custom_attachment = self._get_custom_attachment(custom_url)
        custom_content = self._build_custom_scss_template()
        datas = base64.b64encode((custom_content).encode("utf-8"))
        if custom_attachment:
            custom_attachment.write({"datas": datas})
        else:
            self.env["ir.attachment"].create({
                'name':
                custom_url,
                'type':
                "binary",
                'mimetype':
                "text/scss",
                'datas':
                datas,
                'datas_fname':
                SCSS_URL.split("/")[-1],
                'url':
                custom_url,
            })
            view_to_xpath = self.env["ir.ui.view"].get_related_views(
                XML_ID,
                bundles=True).filtered(lambda v: v.arch.find(SCSS_URL) >= 0)
            self.env["ir.ui.view"].create({
                'name':
                custom_url,
                'key':
                'web_editor.scss_%s' % str(uuid.uuid4())[:6],
                'mode':
                "extension",
                'inherit_id':
                view_to_xpath.id,
                'arch':
                """
                    <data inherit_id="%(inherit_xml_id)s" name="%(name)s">
                        <xpath expr="//link[@href='%(url_to_replace)s']" position="attributes">
                            <attribute name="href">%(new_url)s</attribute>
                        </xpath>
                    </data>
                """ % {
                    'inherit_xml_id': view_to_xpath.xml_id,
                    'name': custom_url,
                    'url_to_replace': SCSS_URL,
                    'new_url': custom_url,
                }
            })
        self.env["ir.qweb"].clear_caches()
Example #14
0
class ConfigProduct(models.Model):
	_name="configurateur.config"

	total_price = fields.Float("Cout Total", default=0)
	variant_line_ids = fields.Many2many("configurateur_product.line")
	config_image = fields.Binary("Image", attachment=True)
Example #15
0
class BankReport(models.TransientModel):
    _name = 'bank.report'
    _description = 'Reporte para pago banco'

    stock_quant_id = fields.Char("LFC")
    data = fields.Binary("Archivo")
    data_name = fields.Char("nombre del archivo")
    secuencia = fields.Char("Aplicacion", default='A1')
    aplicacion = fields.Selection(string="tipo de pago",
                                  selection=[('I', 'Inmediata'),
                                             ('M', 'Medio dia'),
                                             ('N', 'Noche')])
    descripcion = fields.Char("Descripcion")
    journal = fields.Many2one('account.journal', string='Diario')
    tipo_pago = fields.Selection(string="tipo de pago",
                                 selection=[('104', 'Pago a Proveedores'),
                                            ('98', 'Pago de Nomina')])
    fecha_aplicacion = fields.Date('Fecha de Aplicacion')
    asientos = fields.Many2many('account.move',
                                string='Asientos',
                                required=True)
    exist_asientos = fields.Boolean(string='Asientos existentes',
                                    compute='get_data_asientos')

    @api.onchange('asientos')
    def get_data_asientos(self):
        if self.asientos:
            self.exist_asientos = True
        else:
            self.exist_asientos = False

    @api.onchange('journal', 'tipo_pago')
    def onchange_journal(self):
        for rec in self:
            return {
                'domain': {
                    'asientos':
                    [('name', 'like', 'CE'),
                     ('journal_id', '=', rec.journal.id), '|',
                     ('partner_id.category_id.id', '=', int(rec.tipo_pago)),
                     ('partner_id.category_id.parent_id', '=',
                      int(rec.tipo_pago))]
                }
            }

    def do_report(self):

        _logger.error("INICIA LA FUNCIÓN GENERAR EL REPORTE ")
        self.make_file()
        return {
            'type':
            'ir.actions.act_url',
            'url':
            '/web/binary/download_document?model=bank.report&field=data&id=%s&filename=%s'
            % (self.id, self.data_name),
            'target':
            'new',
            'nodestroy':
            False,
        }

    def make_file(self):
        _logger.error("INICIA LA FUNCIÓN CONSTRUIR EL ARCHIVO ")

        account = self.asientos

        if not account:
            raise Warning(
                _('!No hay resultados para los datos seleccionados¡'))

        buf = BytesIO()
        wb = xlsxwriter.Workbook(buf)
        ws = wb.add_worksheet('Report')

        # formatos
        title_head = wb.add_format({
            'bold': 1,
            'border': 1,
            'align': 'rigth',
            'fg_color': '#33CCCC',
            'valign': 'vcenter',
        })
        title_head.set_font_name('Arial')
        title_head.set_font_size(10)
        title_head.set_font_color('#ffffff')

        company = self.env['res.company'].search([])

        ws.write(0, 0, 'NIT PAGADOR', title_head)
        ws.write(0, 1, 'TIPO DE PAGO', title_head)
        ws.write(0, 2, 'APLICACIÓN', title_head)
        ws.write(0, 3, 'SECUENCIA DE ENVIO', title_head)
        ws.write(0, 4, 'NRO CUENTA A DEBITAR', title_head)
        ws.write(0, 5, 'TIPO DE CUENTA A DEBITAR', title_head)
        ws.write(0, 6, 'DESCRIPCIÓN DEL PAGO', title_head)

        ws.write(1, 0, '' if not company[0].vat else company[0].vat)
        ws.write(1, 1, self.tipo_pago)
        if self.tipo_pago:
            if self.tipo_pago == '104':
                ws.write(1, 1, '220')
            elif self.tipo_pago == '98':
                ws.write(1, 1, '225')
            else:
                ws.write(1, 1, '')
        else:
            ws.write(1, 1, '')
        ws.write(1, 2, self.aplicacion)
        ws.write(1, 3, self.secuencia)
        ws.write(1, 4, self.journal.bank_account_id.acc_number)
        if self.journal.bank_account_id.account_type:
            if self.journal.bank_account_id.account_type == '1':
                ws.write(1, 5, 'S')
            elif self.journal.bank_account_id.account_type == '2':
                ws.write(1, 5, 'D')
            else:
                ws.write(1, 5, '')
        else:
            ws.write(1, 5, '')
        ws.write(1, 6, self.descripcion)

        ws.write(2, 0, 'Tipo Documento Beneficiario', title_head)
        ws.write(2, 1, 'Nit Beneficiario', title_head)
        ws.write(2, 2, 'Nombre Beneficiario ', title_head)
        ws.write(2, 3, 'Tipo Transaccion', title_head)
        ws.write(2, 4, 'Código Banco', title_head)
        ws.write(2, 5, 'No Cuenta Beneficiario', title_head)
        ws.write(2, 6, 'Email', title_head)
        ws.write(2, 7, 'Documento Autorizado', title_head)
        ws.write(2, 8, 'Referencia', title_head)
        ws.write(2, 9, 'OficinaEntrega', title_head)
        ws.write(2, 10, 'ValorTransaccion', title_head)
        ws.write(2, 11, 'Fecha de aplicación', title_head)

        fila = 3
        for ac in account:
            vat = ac.partner_id.vat
            if ac.partner_id.l10n_co_document_type:
                if ac.partner_id.l10n_co_document_type == 'id_document':
                    ws.write(fila, 0, '1')
                    pos = (ac.partner_id.vat).find("-")
                    if pos != -1:
                        vat = ac.partner_id.vat[0:pos]
                    else:
                        vat = ac.partner_id.vat
                elif ac.partner_id.l10n_co_document_type == 'foreign_id_card':
                    ws.write(fila, 0, '2')
                elif ac.partner_id.l10n_co_document_type == 'rut':
                    ws.write(fila, 0, '3')
                    pos = (ac.partner_id.vat).find("-")
                    if pos != -1:
                        vat = ac.partner_id.vat[0:pos]
                    else:
                        vat = ac.partner_id.vat
                elif ac.partner_id.l10n_co_document_type == 'id_card':
                    ws.write(fila, 0, '4')
                elif ac.partner_id.l10n_co_document_type == 'passport':
                    ws.write(fila, 0, '5')
                else:
                    ws.write(fila, 0, '')
            else:
                ws.write(fila, 0, '')

            ws.write(fila, 1, '' if not vat else vat.replace(".", ""))
            ws.write(fila, 2, ac.partner_id.name)
            if ac.partner_id.bank_ids:
                if ac.partner_id.bank_ids[0].account_type == '1':
                    ws.write(fila, 3, '27')
                elif ac.partner_id.bank_ids[0].account_type == '2':
                    ws.write(fila, 3, '37')
                else:
                    ws.write(fila, 3, '')
            else:
                ws.write(fila, 3, '')
            ws.write(fila, 4, '') if not ac.partner_id.bank_ids else ws.write(
                fila, 4, ac.partner_id.bank_ids[0].bank_id.code_bank)
            ws.write(fila, 5, '') if not ac.partner_id.bank_ids else ws.write(
                fila, 5, ac.partner_id.bank_ids[0].acc_number)
            ws.write(fila, 6, '')
            ws.write(fila, 7, '')
            ws.write(fila, 8, '')
            ws.write(fila, 9, '')
            ws.write(fila, 10, "{:.2f}".format(ac.amount_total))
            ws.write(fila, 11,
                     str(self.fecha_aplicacion.isoformat()).replace("-", ""))
            fila += 1

        try:
            wb.close()
            out = base64.encodestring(buf.getvalue())
            buf.close()
            self.data = out
            self.data_name = 'Reporte pago bancos' + ".xls"
        except ValueError:
            raise Warning('No se pudo generar el archivo')
Example #16
0
class MrpWorkorder(models.Model):
    _name = 'mrp.workorder'
    _description = 'Work Order'
    _inherit = ['mail.thread', 'mail.activity.mixin', 'mrp.abstract.workorder']

    name = fields.Char(
        'Work Order', required=True,
        states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
    workcenter_id = fields.Many2one(
        'mrp.workcenter', 'Work Center', required=True,
        states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
    working_state = fields.Selection(
        'Workcenter Status', related='workcenter_id.working_state', readonly=False,
        help='Technical: used in views only')
    production_availability = fields.Selection(
        'Stock Availability', readonly=True,
        related='production_id.reservation_state', store=True,
        help='Technical: used in views and domains only.')
    production_state = fields.Selection(
        'Production State', readonly=True,
        related='production_id.state',
        help='Technical: used in views only.')
    qty_production = fields.Float('Original Production Quantity', readonly=True, related='production_id.product_qty')
    qty_remaining = fields.Float('Quantity To Be Produced', compute='_compute_qty_remaining', digits='Product Unit of Measure')
    qty_produced = fields.Float(
        'Quantity', default=0.0,
        readonly=True,
        digits='Product Unit of Measure',
        help="The number of products already handled by this work order")
    is_produced = fields.Boolean(string="Has Been Produced",
        compute='_compute_is_produced')
    state = fields.Selection([
        ('pending', 'Waiting for another WO'),
        ('ready', 'Ready'),
        ('progress', 'In Progress'),
        ('done', 'Finished'),
        ('cancel', 'Cancelled')], string='Status',
        default='pending')
    leave_id = fields.Many2one(
        'resource.calendar.leaves',
        help='Slot into workcenter calendar once planned')
    date_planned_start = fields.Datetime(
        'Scheduled Date Start',
        compute='_compute_dates_planned',
        inverse='_set_dates_planned',
        states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
        store=True)
    date_planned_finished = fields.Datetime(
        'Scheduled Date Finished',
        compute='_compute_dates_planned',
        inverse='_set_dates_planned',
        states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
        store=True)
    date_start = fields.Datetime(
        'Effective Start Date',
        states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
    date_finished = fields.Datetime(
        'Effective End Date',
        states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})

    duration_expected = fields.Float(
        'Expected Duration', digits=(16, 2),
        states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
        help="Expected duration (in minutes)")
    duration = fields.Float(
        'Real Duration', compute='_compute_duration',
        readonly=True, store=True)
    duration_unit = fields.Float(
        'Duration Per Unit', compute='_compute_duration',
        readonly=True, store=True)
    duration_percent = fields.Integer(
        'Duration Deviation (%)', compute='_compute_duration',
        group_operator="avg", readonly=True, store=True)

    operation_id = fields.Many2one(
        'mrp.routing.workcenter', 'Operation')  # Should be used differently as BoM can change in the meantime
    worksheet = fields.Binary(
        'Worksheet', related='operation_id.worksheet', readonly=True)
    worksheet_type = fields.Selection(
        'Worksheet Type', related='operation_id.worksheet_type', readonly=True)
    worksheet_google_slide = fields.Char(
        'Worksheet URL', related='operation_id.worksheet_google_slide', readonly=True)
    move_raw_ids = fields.One2many(
        'stock.move', 'workorder_id', 'Raw Moves',
        domain=[('raw_material_production_id', '!=', False), ('production_id', '=', False)])
    move_finished_ids = fields.One2many(
        'stock.move', 'workorder_id', 'Finished Moves',
        domain=[('raw_material_production_id', '=', False), ('production_id', '!=', False)])
    move_line_ids = fields.One2many(
        'stock.move.line', 'workorder_id', 'Moves to Track',
        help="Inventory moves for which you must scan a lot number at this work order")
    finished_lot_id = fields.Many2one(
        'stock.production.lot', 'Lot/Serial Number', domain="[('product_id', '=', product_id)]",
        states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
    time_ids = fields.One2many(
        'mrp.workcenter.productivity', 'workorder_id')
    is_user_working = fields.Boolean(
        'Is the Current User Working', compute='_compute_working_users',
        help="Technical field indicating whether the current user is working. ")
    working_user_ids = fields.One2many('res.users', string='Working user on this work order.', compute='_compute_working_users')
    last_working_user_id = fields.One2many('res.users', string='Last user that worked on this work order.', compute='_compute_working_users')

    next_work_order_id = fields.Many2one('mrp.workorder', "Next Work Order")
    scrap_ids = fields.One2many('stock.scrap', 'workorder_id')
    scrap_count = fields.Integer(compute='_compute_scrap_move_count', string='Scrap Move')
    production_date = fields.Datetime('Production Date', related='production_id.date_planned_start', store=True, readonly=False)
    color = fields.Integer('Color', compute='_compute_color')
    capacity = fields.Float(
        'Capacity', default=1.0,
        help="Number of pieces that can be produced in parallel.")
    raw_workorder_line_ids = fields.One2many('mrp.workorder.line',
        'raw_workorder_id', string='Components')
    finished_workorder_line_ids = fields.One2many('mrp.workorder.line',
        'finished_workorder_id', string='By-products')
    allowed_lots_domain = fields.One2many(comodel_name='stock.production.lot', compute="_compute_allowed_lots_domain")

    # Both `date_planned_start` and `date_planned_finished` are related fields on `leave_id`. Let's say
    # we slide a workorder on a gantt view, a single call to write is made with both
    # fields Changes. As the ORM doesn't batch the write on related fields and instead
    # makes multiple call, the constraint check_dates() is raised.
    # That's why the compute and set methods are needed. to ensure the dates are updated
    # in the same time.
    @api.depends('leave_id')
    def _compute_dates_planned(self):
        for workorder in self:
            workorder.date_planned_start = workorder.leave_id.date_from
            workorder.date_planned_finished = workorder.leave_id.date_to

    def _set_dates_planned(self):
        date_from = self[0].date_planned_start
        date_to = self[0].date_planned_finished
        self.mapped('leave_id').write({
            'date_from': date_from,
            'date_to': date_to,
        })

    @api.onchange('date_planned_start')
    def _onchange_date_planned_start(self):
        if self.duration_expected:
            time_delta = timedelta(minutes=self.duration_expected)
        else:
            time_delta = timedelta(hours=1)
        self.update({'date_planned_finished': self.date_planned_start + time_delta})

    @api.onchange('finished_lot_id')
    def _onchange_finished_lot_id(self):
        """When the user changes the lot being currently produced, suggest
        a quantity to produce consistent with the previous workorders. """
        previous_wo = self.env['mrp.workorder'].search([
            ('next_work_order_id', '=', self.id)
        ])
        if previous_wo:
            line = previous_wo.finished_workorder_line_ids.filtered(lambda line: line.product_id == self.product_id and line.lot_id == self.finished_lot_id)
            if line:
                self.qty_producing = line.qty_done

    @api.depends('production_id.workorder_ids.finished_workorder_line_ids',
    'production_id.workorder_ids.finished_workorder_line_ids.qty_done',
    'production_id.workorder_ids.finished_workorder_line_ids.lot_id')
    def _compute_allowed_lots_domain(self):
        """ Check if all the finished products has been assigned to a serial
        number or a lot in other workorders. If yes, restrict the selectable lot
        to the lot/sn used in other workorders.
        """
        productions = self.mapped('production_id')
        treated = self.browse()
        for production in productions:
            if production.product_id.tracking == 'none':
                continue

            rounding = production.product_uom_id.rounding
            finished_workorder_lines = production.workorder_ids.mapped('finished_workorder_line_ids').filtered(lambda wl: wl.product_id == production.product_id)
            qties_done_per_lot = defaultdict(list)
            for finished_workorder_line in finished_workorder_lines:
                # It is possible to have finished workorder lines without a lot (eg using the dummy
                # test type). Ignore them when computing the allowed lots.
                if finished_workorder_line.lot_id:
                    qties_done_per_lot[finished_workorder_line.lot_id.id].append(finished_workorder_line.qty_done)

            qty_to_produce = production.product_qty
            allowed_lot_ids = self.env['stock.production.lot']
            qty_produced = sum([max(qty_dones) for qty_dones in qties_done_per_lot.values()])
            if float_compare(qty_produced, qty_to_produce, precision_rounding=rounding) < 0:
                # If we haven't produced enough, all lots are available
                allowed_lot_ids = self.env['stock.production.lot'].search([('product_id', '=', production.product_id.id)])
            else:
                # If we produced enough, only the already produced lots are available
                allowed_lot_ids = self.env['stock.production.lot'].browse(qties_done_per_lot.keys())
            workorders = production.workorder_ids.filtered(lambda wo: wo.state not in ('done', 'cancel'))
            for workorder in workorders:
                if workorder.product_tracking == 'serial':
                    workorder.allowed_lots_domain = allowed_lot_ids - workorder.finished_workorder_line_ids.filtered(lambda wl: wl.product_id == production.product_id).mapped('lot_id')
                else:
                    workorder.allowed_lots_domain = allowed_lot_ids
                treated |= workorder
        (self - treated).allowed_lots_domain = False

    def name_get(self):
        return [(wo.id, "%s - %s - %s" % (wo.production_id.name, wo.product_id.name, wo.name)) for wo in self]

    def unlink(self):
        # Removes references to workorder to avoid Validation Error
        (self.mapped('move_raw_ids') | self.mapped('move_finished_ids')).write({'workorder_id': False})
        self.mapped('leave_id').unlink()
        return super(MrpWorkorder, self).unlink()

    @api.depends('production_id.product_qty', 'qty_produced')
    def _compute_is_produced(self):
        for order in self:
            rounding = order.production_id.product_uom_id.rounding
            order.is_produced = float_compare(order.qty_produced, order.production_id.product_qty, precision_rounding=rounding) >= 0

    @api.depends('time_ids.duration', 'qty_produced')
    def _compute_duration(self):
        for order in self:
            order.duration = sum(order.time_ids.mapped('duration'))
            order.duration_unit = round(order.duration / max(order.qty_produced, 1), 2)  # rounding 2 because it is a time
            if order.duration_expected:
                order.duration_percent = 100 * (order.duration_expected - order.duration) / order.duration_expected
            else:
                order.duration_percent = 0

    def _compute_working_users(self):
        """ Checks whether the current user is working, all the users currently working and the last user that worked. """
        for order in self:
            order.working_user_ids = [(4, order.id) for order in order.time_ids.filtered(lambda time: not time.date_end).sorted('date_start').mapped('user_id')]
            if order.working_user_ids:
                order.last_working_user_id = order.working_user_ids[-1]
            elif order.time_ids:
                order.last_working_user_id = order.time_ids.sorted('date_end')[-1].user_id
            if order.time_ids.filtered(lambda x: (x.user_id.id == self.env.user.id) and (not x.date_end) and (x.loss_type in ('productive', 'performance'))):
                order.is_user_working = True
            else:
                order.is_user_working = False

    def _compute_scrap_move_count(self):
        data = self.env['stock.scrap'].read_group([('workorder_id', 'in', self.ids)], ['workorder_id'], ['workorder_id'])
        count_data = dict((item['workorder_id'][0], item['workorder_id_count']) for item in data)
        for workorder in self:
            workorder.scrap_count = count_data.get(workorder.id, 0)

    @api.depends('date_planned_finished', 'production_id.date_planned_finished')
    def _compute_color(self):
        late_orders = self.filtered(lambda x: x.production_id.date_planned_finished and x.date_planned_finished > x.production_id.date_planned_finished)
        for order in late_orders:
            order.color = 4
        for order in (self - late_orders):
            order.color = 2

    def write(self, values):
        if list(values.keys()) != ['time_ids'] and any(workorder.state == 'done' for workorder in self):
            raise UserError(_('You can not change the finished work order.'))
        if 'date_planned_start' in values or 'date_planned_finished' in values:
            for workorder in self:
                start_date = fields.Datetime.to_datetime(values.get('date_planned_start')) or workorder.date_planned_start
                end_date = fields.Datetime.to_datetime(values.get('date_planned_finished')) or workorder.date_planned_finished
                if start_date and end_date and start_date > end_date:
                    raise UserError(_('The planned end date of the work order cannot be prior to the planned start date, please correct this to save the work order.'))
                # Update MO dates if the start date of the first WO or the
                # finished date of the last WO is update.
                if workorder == workorder.production_id.workorder_ids[0] and 'date_planned_start' in values:
                    workorder.production_id.with_context(force_date=True).write({
                        'date_planned_start': values['date_planned_start']
                    })
                if workorder == workorder.production_id.workorder_ids[-1] and 'date_planned_finished' in values:
                    workorder.production_id.with_context(force_date=True).write({
                        'date_planned_finished': values['date_planned_finished']
                    })
        return super(MrpWorkorder, self).write(values)

    def _generate_wo_lines(self):
        """ Generate workorder line """
        self.ensure_one()
        moves = (self.move_raw_ids | self.move_finished_ids).filtered(
            lambda move: move.state not in ('done', 'cancel')
        )
        for move in moves:
            qty_to_consume = self._prepare_component_quantity(move, self.qty_producing)
            line_values = self._generate_lines_values(move, qty_to_consume)
            self.env['mrp.workorder.line'].create(line_values)

    def _apply_update_workorder_lines(self):
        """ update existing line on the workorder. It could be trigger manually
        after a modification of qty_producing.
        """
        self.ensure_one()
        line_values = self._update_workorder_lines()
        self.env['mrp.workorder.line'].create(line_values['to_create'])
        if line_values['to_delete']:
            line_values['to_delete'].unlink()
        for line, vals in line_values['to_update'].items():
            line.write(vals)

    def _refresh_wo_lines(self):
        """ Modify exisiting workorder line in order to match the reservation on
        stock move line. The strategy is to remove the line that were not
        processed yet then call _generate_lines_values that recreate workorder
        line depending the reservation.
        """
        for workorder in self:
            raw_moves = workorder.move_raw_ids.filtered(
                lambda move: move.state not in ('done', 'cancel')
            )
            wl_to_unlink = self.env['mrp.workorder.line']
            for move in raw_moves:
                rounding = move.product_uom.rounding
                qty_already_consumed = 0.0
                workorder_lines = workorder.raw_workorder_line_ids.filtered(lambda w: w.move_id == move)
                for wl in workorder_lines:
                    if not wl.qty_done:
                        wl_to_unlink |= wl
                        continue

                    qty_already_consumed += wl.qty_done
                qty_to_consume = self._prepare_component_quantity(move, workorder.qty_producing)
                wl_to_unlink.unlink()
                if float_compare(qty_to_consume, qty_already_consumed, precision_rounding=rounding) > 0:
                    line_values = workorder._generate_lines_values(move, qty_to_consume - qty_already_consumed)
                    self.env['mrp.workorder.line'].create(line_values)

    def _defaults_from_finished_workorder_line(self, reference_lot_lines):
        for r_line in reference_lot_lines:
            # see which lot we could suggest and its related qty_producing
            if not r_line.lot_id:
                continue
            candidates = self.finished_workorder_line_ids.filtered(lambda line: line.lot_id == r_line.lot_id)
            rounding = self.product_uom_id.rounding
            if not candidates:
                self.write({
                    'finished_lot_id': r_line.lot_id.id,
                    'qty_producing': r_line.qty_done,
                })
                return True
            elif float_compare(candidates.qty_done, r_line.qty_done, precision_rounding=rounding) < 0:
                self.write({
                    'finished_lot_id': r_line.lot_id.id,
                    'qty_producing': r_line.qty_done - candidates.qty_done,
                })
                return True
        return False

    def record_production(self):
        if not self:
            return True

        self.ensure_one()
        if float_compare(self.qty_producing, 0, precision_rounding=self.product_uom_id.rounding) <= 0:
            raise UserError(_('Please set the quantity you are currently producing. It should be different from zero.'))

        # If last work order, then post lots used
        if not self.next_work_order_id:
            self._update_finished_move()

        # Transfer quantities from temporary to final move line or make them final
        self._update_moves()

        # Transfer lot (if present) and quantity produced to a finished workorder line
        if self.product_tracking != 'none':
            self._create_or_update_finished_line()

        # Update workorder quantity produced
        self.qty_produced += self.qty_producing

        # Suggest a finished lot on the next workorder
        if self.next_work_order_id and self.production_id.product_id.tracking != 'none' and not self.next_work_order_id.finished_lot_id:
            self.next_work_order_id._defaults_from_finished_workorder_line(self.finished_workorder_line_ids)
            # As we may have changed the quantity to produce on the next workorder,
            # make sure to update its wokorder lines
            self.next_work_order_id._apply_update_workorder_lines()

        # One a piece is produced, you can launch the next work order
        self._start_nextworkorder()

        # Test if the production is done
        rounding = self.production_id.product_uom_id.rounding
        if float_compare(self.qty_produced, self.production_id.product_qty, precision_rounding=rounding) < 0:
            previous_wo = self.env['mrp.workorder']
            if self.product_tracking != 'none':
                previous_wo = self.env['mrp.workorder'].search([
                    ('next_work_order_id', '=', self.id)
                ])
            candidate_found_in_previous_wo = False
            if previous_wo:
                candidate_found_in_previous_wo = self._defaults_from_finished_workorder_line(previous_wo.finished_workorder_line_ids)
            if not candidate_found_in_previous_wo:
                # self is the first workorder
                self.qty_producing = self.qty_remaining
                self.finished_lot_id = False
                if self.product_tracking == 'serial':
                    self.qty_producing = 1

            self._apply_update_workorder_lines()
        else:
            self.qty_producing = 0
            self.button_finish()
        return True

    def _get_byproduct_move_to_update(self):
        return self.production_id.move_finished_ids.filtered(lambda x: (x.product_id.id != self.production_id.product_id.id) and (x.state not in ('done', 'cancel')))

    def _create_or_update_finished_line(self):
        """
        1. Check that the final lot and the quantity producing is valid regarding
            other workorders of this production
        2. Save final lot and quantity producing to suggest on next workorder
        """
        self.ensure_one()
        final_lot_quantity = self.qty_production
        rounding = self.product_uom_id.rounding
        # Get the max quantity possible for current lot in other workorders
        for workorder in (self.production_id.workorder_ids - self):
            # We add the remaining quantity to the produced quantity for the
            # current lot. For 5 finished products: if in the first wo it
            # creates 4 lot A and 1 lot B and in the second it create 3 lot A
            # and it remains 2 units to product, it could produce 5 lot A.
            # In this case we select 4 since it would conflict with the first
            # workorder otherwise.
            line = workorder.finished_workorder_line_ids.filtered(lambda line: line.lot_id == self.finished_lot_id)
            line_without_lot = workorder.finished_workorder_line_ids.filtered(lambda line: line.product_id == workorder.product_id and not line.lot_id)
            quantity_remaining = workorder.qty_remaining + line_without_lot.qty_done
            quantity = line.qty_done + quantity_remaining
            if line and float_compare(quantity, final_lot_quantity, precision_rounding=rounding) <= 0:
                final_lot_quantity = quantity
            elif float_compare(quantity_remaining, final_lot_quantity, precision_rounding=rounding) < 0:
                final_lot_quantity = quantity_remaining

        # final lot line for this lot on this workorder.
        current_lot_lines = self.finished_workorder_line_ids.filtered(lambda line: line.lot_id == self.finished_lot_id)

        # this lot has already been produced
        if float_compare(final_lot_quantity, current_lot_lines.qty_done + self.qty_producing, precision_rounding=rounding) < 0:
            raise UserError(_('You have produced %s %s of lot %s in the previous workorder. You are trying to produce %s in this one') %
                (final_lot_quantity, self.product_id.uom_id.name, self.finished_lot_id.name, current_lot_lines.qty_done + self.qty_producing))

        # Update workorder line that regiter final lot created
        if not current_lot_lines:
            current_lot_lines = self.env['mrp.workorder.line'].create({
                'finished_workorder_id': self.id,
                'product_id': self.product_id.id,
                'lot_id': self.finished_lot_id.id,
                'qty_done': self.qty_producing,
            })
        else:
            current_lot_lines.qty_done += self.qty_producing

    def _start_nextworkorder(self):
        rounding = self.product_id.uom_id.rounding
        if self.next_work_order_id.state == 'pending' and (
                (self.operation_id.batch == 'no' and
                 float_compare(self.qty_production, self.qty_produced, precision_rounding=rounding) <= 0) or
                (self.operation_id.batch == 'yes' and
                 float_compare(self.operation_id.batch_size, self.qty_produced, precision_rounding=rounding) <= 0)):
            self.next_work_order_id.state = 'ready'

    def button_start(self):
        self.ensure_one()
        # As button_start is automatically called in the new view
        if self.state in ('done', 'cancel'):
            return True

        # Need a loss in case of the real time exceeding the expected
        timeline = self.env['mrp.workcenter.productivity']
        if self.duration < self.duration_expected:
            loss_id = self.env['mrp.workcenter.productivity.loss'].search([('loss_type','=','productive')], limit=1)
            if not len(loss_id):
                raise UserError(_("You need to define at least one productivity loss in the category 'Productivity'. Create one from the Manufacturing app, menu: Configuration / Productivity Losses."))
        else:
            loss_id = self.env['mrp.workcenter.productivity.loss'].search([('loss_type','=','performance')], limit=1)
            if not len(loss_id):
                raise UserError(_("You need to define at least one productivity loss in the category 'Performance'. Create one from the Manufacturing app, menu: Configuration / Productivity Losses."))
        if self.production_id.state != 'progress':
            self.production_id.write({
                'date_start': datetime.now(),
            })
        timeline.create({
            'workorder_id': self.id,
            'workcenter_id': self.workcenter_id.id,
            'description': _('Time Tracking: ')+self.env.user.name,
            'loss_id': loss_id[0].id,
            'date_start': datetime.now(),
            'user_id': self.env.user.id
        })
        return self.write({'state': 'progress',
                    'date_start': datetime.now(),
        })

    def button_finish(self):
        self.ensure_one()
        self.end_all()
        return self.write({'state': 'done', 'date_finished': fields.Datetime.now()})

    def end_previous(self, doall=False):
        """
        @param: doall:  This will close all open time lines on the open work orders when doall = True, otherwise
        only the one of the current user
        """
        # TDE CLEANME
        timeline_obj = self.env['mrp.workcenter.productivity']
        domain = [('workorder_id', 'in', self.ids), ('date_end', '=', False)]
        if not doall:
            domain.append(('user_id', '=', self.env.user.id))
        not_productive_timelines = timeline_obj.browse()
        for timeline in timeline_obj.search(domain, limit=None if doall else 1):
            wo = timeline.workorder_id
            if wo.duration_expected <= wo.duration:
                if timeline.loss_type == 'productive':
                    not_productive_timelines += timeline
                timeline.write({'date_end': fields.Datetime.now()})
            else:
                maxdate = fields.Datetime.from_string(timeline.date_start) + relativedelta(minutes=wo.duration_expected - wo.duration)
                enddate = datetime.now()
                if maxdate > enddate:
                    timeline.write({'date_end': enddate})
                else:
                    timeline.write({'date_end': maxdate})
                    not_productive_timelines += timeline.copy({'date_start': maxdate, 'date_end': enddate})
        if not_productive_timelines:
            loss_id = self.env['mrp.workcenter.productivity.loss'].search([('loss_type', '=', 'performance')], limit=1)
            if not len(loss_id):
                raise UserError(_("You need to define at least one unactive productivity loss in the category 'Performance'. Create one from the Manufacturing app, menu: Configuration / Productivity Losses."))
            not_productive_timelines.write({'loss_id': loss_id.id})
        return True

    def end_all(self):
        return self.end_previous(doall=True)

    def button_pending(self):
        self.end_previous()
        return True

    def button_unblock(self):
        for order in self:
            order.workcenter_id.unblock()
        return True

    def action_cancel(self):
        return self.write({'state': 'cancel'})

    def button_done(self):
        if any([x.state in ('done', 'cancel') for x in self]):
            raise UserError(_('A Manufacturing Order is already done or cancelled.'))
        self.end_all()
        return self.write({'state': 'done',
                    'date_finished': datetime.now()})

    def button_scrap(self):
        self.ensure_one()
        return {
            'name': _('Scrap'),
            'view_mode': 'form',
            'res_model': 'stock.scrap',
            'view_id': self.env.ref('stock.stock_scrap_form_view2').id,
            'type': 'ir.actions.act_window',
            'context': {'default_company_id': self.production_id.company_id.id,
                        'default_workorder_id': self.id,
                        'default_production_id': self.production_id.id,
                        'product_ids': (self.production_id.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) | self.production_id.move_finished_ids.filtered(lambda x: x.state == 'done')).mapped('product_id').ids},
            'target': 'new',
        }

    def action_see_move_scrap(self):
        self.ensure_one()
        action = self.env.ref('stock.action_stock_scrap').read()[0]
        action['domain'] = [('workorder_id', '=', self.id)]
        return action

    @api.depends('qty_production', 'qty_produced')
    def _compute_qty_remaining(self):
        for wo in self:
            wo.qty_remaining = float_round(wo.qty_production - wo.qty_produced, precision_rounding=wo.production_id.product_uom_id.rounding)
Example #17
0
class IrUiMenu(models.Model):
    _name = 'ir.ui.menu'
    _description = 'Menu'
    _order = "sequence,id"
    _parent_store = True

    def __init__(self, *args, **kwargs):
        super(IrUiMenu, self).__init__(*args, **kwargs)
        self.pool['ir.model.access'].register_cache_clearing_method(
            self._name, 'clear_caches')

    name = fields.Char(string='Menu', required=True, translate=True)
    active = fields.Boolean(default=True)
    sequence = fields.Integer(default=10)
    child_id = fields.One2many('ir.ui.menu', 'parent_id', string='Child IDs')
    parent_id = fields.Many2one('ir.ui.menu',
                                string='Parent Menu',
                                index=True,
                                ondelete="restrict")
    parent_path = fields.Char(index=True)
    groups_id = fields.Many2many('res.groups', 'ir_ui_menu_group_rel',
                                 'menu_id', 'gid', string='Groups',
                                 help="If you have groups, the visibility of this menu will be based on these groups. "\
                                      "If this field is empty, Odoo will compute visibility based on the related object's read access.")
    complete_name = fields.Char(compute='_compute_complete_name',
                                string='Full Path')
    web_icon = fields.Char(string='Web Icon File')
    action = fields.Reference(selection=[(
        'ir.actions.report', 'ir.actions.report'
    ), ('ir.actions.act_window',
        'ir.actions.act_window'), (
            'ir.actions.act_url',
            'ir.actions.act_url'), (
                'ir.actions.server',
                'ir.actions.server'), ('ir.actions.client',
                                       'ir.actions.client')])

    web_icon_data = fields.Binary(string='Web Icon Image', attachment=True)

    @api.depends('name', 'parent_id.complete_name')
    def _compute_complete_name(self):
        for menu in self:
            menu.complete_name = menu._get_full_name()

    def _get_full_name(self, level=6):
        """ Return the full name of ``self`` (up to a certain level). """
        if level <= 0:
            return '...'
        if self.parent_id:
            return self.parent_id._get_full_name(
                level - 1) + MENU_ITEM_SEPARATOR + (self.name or "")
        else:
            return self.name

    def read_image(self, path):
        if not path:
            return False
        path_info = path.split(',')
        icon_path = get_module_resource(path_info[0], path_info[1])
        icon_image = False
        if icon_path:
            with tools.file_open(icon_path, 'rb') as icon_file:
                icon_image = base64.encodebytes(icon_file.read())
        return icon_image

    @api.constrains('parent_id')
    def _check_parent_id(self):
        if not self._check_recursion():
            raise ValidationError(
                _('Error! You cannot create recursive menus.'))

    @api.model
    @tools.ormcache('frozenset(self.env.user.groups_id.ids)', 'debug')
    def _visible_menu_ids(self, debug=False):
        """ Return the ids of the menu items visible to the user. """
        # retrieve all menus, and determine which ones are visible
        context = {'ir.ui.menu.full_list': True}
        menus = self.with_context(context).search([])

        groups = self.env.user.groups_id
        if not debug:
            groups = groups - self.env.ref('base.group_no_one')
        # first discard all menus with groups the user does not have
        menus = menus.filtered(
            lambda menu: not menu.groups_id or menu.groups_id & groups)

        # take apart menus that have an action
        action_menus = menus.filtered(lambda m: m.action and m.action.exists())
        folder_menus = menus - action_menus
        visible = self.browse()

        # process action menus, check whether their action is allowed
        access = self.env['ir.model.access']
        MODEL_GETTER = {
            'ir.actions.act_window': lambda action: action.res_model,
            'ir.actions.report': lambda action: action.model,
            'ir.actions.server': lambda action: action.model_id.model,
        }
        for menu in action_menus:
            get_model = MODEL_GETTER.get(menu.action._name)
            if not get_model or not get_model(menu.action) or \
                    access.check(get_model(menu.action), 'read', False):
                # make menu visible, and its folder ancestors, too
                visible += menu
                menu = menu.parent_id
                while menu and menu in folder_menus and menu not in visible:
                    visible += menu
                    menu = menu.parent_id

        return set(visible.ids)

    @api.returns('self')
    def _filter_visible_menus(self):
        """ Filter `self` to only keep the menu items that should be visible in
            the menu hierarchy of the current user.
            Uses a cache for speeding up the computation.
        """
        visible_ids = self._visible_menu_ids(
            request.session.debug if request else False)
        return self.filtered(lambda menu: menu.id in visible_ids)

    @api.model
    def _search(self,
                args,
                offset=0,
                limit=None,
                order=None,
                count=False,
                access_rights_uid=None):
        menu_ids = super(IrUiMenu,
                         self)._search(args,
                                       offset=0,
                                       limit=None,
                                       order=order,
                                       count=False,
                                       access_rights_uid=access_rights_uid)
        menus = self.browse(menu_ids)
        if menus:
            # menu filtering is done only on main menu tree, not other menu lists
            if not self._context.get('ir.ui.menu.full_list'):
                menus = menus._filter_visible_menus()
            if offset:
                menus = menus[offset:]
            if limit:
                menus = menus[:limit]
        return len(menus) if count else menus.ids

    def name_get(self):
        return [(menu.id, menu._get_full_name()) for menu in self]

    @api.model_create_multi
    def create(self, vals_list):
        self.clear_caches()
        for values in vals_list:
            if 'web_icon' in values:
                values['web_icon_data'] = self._compute_web_icon_data(
                    values.get('web_icon'))
        return super(IrUiMenu, self).create(vals_list)

    def write(self, values):
        self.clear_caches()
        if 'web_icon' in values:
            values['web_icon_data'] = self._compute_web_icon_data(
                values.get('web_icon'))
        return super(IrUiMenu, self).write(values)

    def _compute_web_icon_data(self, web_icon):
        """ Returns the image associated to `web_icon`.
            `web_icon` can either be:
              - an image icon [module, path]
              - a built icon [icon_class, icon_color, background_color]
            and it only has to call `read_image` if it's an image.
        """
        if web_icon and len(web_icon.split(',')) == 2:
            return self.read_image(web_icon)

    def unlink(self):
        # Detach children and promote them to top-level, because it would be unwise to
        # cascade-delete submenus blindly. We also can't use ondelete=set null because
        # that is not supported when _parent_store is used (would silently corrupt it).
        # TODO: ideally we should move them under a generic "Orphans" menu somewhere?
        extra = {'ir.ui.menu.full_list': True, 'active_test': False}
        direct_children = self.with_context(**extra).search([('parent_id',
                                                              'in', self.ids)])
        direct_children.write({'parent_id': False})

        self.clear_caches()
        return super(IrUiMenu, self).unlink()

    def copy(self, default=None):
        record = super(IrUiMenu, self).copy(default=default)
        match = NUMBER_PARENS.search(record.name)
        if match:
            next_num = int(match.group(1)) + 1
            record.name = NUMBER_PARENS.sub('(%d)' % next_num, record.name)
        else:
            record.name = record.name + '(1)'
        return record

    @api.model
    @api.returns('self')
    def get_user_roots(self):
        """ Return all root menu ids visible for the user.

        :return: the root menu ids
        :rtype: list(int)
        """
        return self.search([('parent_id', '=', False)])

    @api.model
    @tools.ormcache_context('self._uid', keys=('lang', ))
    def load_menus_root(self):
        fields = ['name', 'sequence', 'parent_id', 'action', 'web_icon_data']
        menu_roots = self.get_user_roots()
        menu_roots_data = menu_roots.read(fields) if menu_roots else []

        menu_root = {
            'id': False,
            'name': 'root',
            'parent_id': [-1, ''],
            'children': menu_roots_data,
            'all_menu_ids': menu_roots.ids,
        }

        menu_roots._set_menuitems_xmlids(menu_root)

        return menu_root

    @api.model
    @tools.ormcache_context('self._uid', 'debug', keys=('lang', ))
    def load_menus(self, debug):
        """ Loads all menu items (all applications and their sub-menus).

        :return: the menu root
        :rtype: dict('children': menu_nodes)
        """
        fields = [
            'name', 'sequence', 'parent_id', 'action', 'web_icon',
            'web_icon_data'
        ]
        menu_roots = self.get_user_roots()
        menu_roots_data = menu_roots.read(fields) if menu_roots else []
        menu_root = {
            'id': False,
            'name': 'root',
            'parent_id': [-1, ''],
            'children': menu_roots_data,
            'all_menu_ids': menu_roots.ids,
        }

        if not menu_roots_data:
            return menu_root

        # menus are loaded fully unlike a regular tree view, cause there are a
        # limited number of items (752 when all 6.1 addons are installed)
        menus = self.search([('id', 'child_of', menu_roots.ids)])
        menu_items = menus.read(fields)

        # add roots at the end of the sequence, so that they will overwrite
        # equivalent menu items from full menu read when put into id:item
        # mapping, resulting in children being correctly set on the roots.
        menu_items.extend(menu_roots_data)
        menu_root['all_menu_ids'] = menus.ids  # includes menu_roots!

        # make a tree using parent_id
        menu_items_map = {
            menu_item["id"]: menu_item
            for menu_item in menu_items
        }
        for menu_item in menu_items:
            parent = menu_item['parent_id'] and menu_item['parent_id'][0]
            if parent in menu_items_map:
                menu_items_map[parent].setdefault('children',
                                                  []).append(menu_item)

        # sort by sequence a tree using parent_id
        for menu_item in menu_items:
            menu_item.setdefault('children',
                                 []).sort(key=operator.itemgetter('sequence'))

        (menu_roots + menus)._set_menuitems_xmlids(menu_root)

        return menu_root

    def _set_menuitems_xmlids(self, menu_root):
        menuitems = self.env['ir.model.data'].sudo().search([
            ('res_id', 'in', self.ids), ('model', '=', 'ir.ui.menu')
        ])

        xmlids = {menu.res_id: menu.complete_name for menu in menuitems}

        def _set_xmlids(tree, xmlids):
            tree['xmlid'] = xmlids.get(tree['id'], '')
            if 'children' in tree:
                for child in tree['children']:
                    _set_xmlids(child, xmlids)

        _set_xmlids(menu_root, xmlids)
Example #18
0
class Company(models.Model):
    _name = "res.company"
    _description = 'Companies'
    _order = 'sequence, name'

    @api.multi
    def copy(self, default=None):
        raise UserError(
            _('Duplicating a company is not allowed. Please create a new company instead.'
              ))

    def _get_logo(self):
        return base64.b64encode(
            open(
                os.path.join(tools.config['root_path'], 'addons', 'base',
                             'static', 'img', 'res_company_logo.png'),
                'rb').read())

    @api.model
    def _get_euro(self):
        return self.env['res.currency.rate'].search([('rate', '=', 1)],
                                                    limit=1).currency_id

    @api.model
    def _get_user_currency(self):
        currency_id = self.env['res.users'].browse(
            self._uid).company_id.currency_id
        return currency_id or self._get_euro()

    def _get_default_favicon(self, original=False):
        img_path = get_resource_path('web', 'static/src/img/favicon.ico')
        with tools.file_open(img_path, 'rb') as f:
            if original:
                return base64.b64encode(f.read())
            # Modify the source image to change the color of the 'O'.
            # This could seem overkill to modify the pixels 1 by 1, but
            # Pillow doesn't provide an easy way to do it, and this
            # is acceptable for a 16x16 image.
            color = (randrange(32, 224,
                               24), randrange(32, 224,
                                              24), randrange(32, 224, 24))
            original = Image.open(f)
            new_image = Image.new('RGBA', original.size)
            for y in range(original.size[1]):
                for x in range(original.size[0]):
                    pixel = original.getpixel((x, y))
                    if pixel[0] == 0 and pixel[1] == 0 and pixel[2] == 0:
                        new_image.putpixel((x, y), (0, 0, 0, 0))
                    else:
                        new_image.putpixel(
                            (x, y), (color[0], color[1], color[2], pixel[3]))
            stream = io.BytesIO()
            new_image.save(stream, format="ICO")
            return base64.b64encode(stream.getvalue())

    name = fields.Char(related='partner_id.name',
                       string='Company Name',
                       required=True,
                       store=True,
                       readonly=False)
    sequence = fields.Integer(
        help='Used to order Companies in the company switcher', default=10)
    parent_id = fields.Many2one('res.company',
                                string='Parent Company',
                                index=True)
    child_ids = fields.One2many('res.company',
                                'parent_id',
                                string='Child Companies')
    partner_id = fields.Many2one('res.partner',
                                 string='Partner',
                                 required=True)
    report_header = fields.Text(
        string='Company Tagline',
        help=
        "Appears by default on the top right corner of your printed documents (report header)."
    )
    report_footer = fields.Text(
        string='Report Footer',
        translate=True,
        help="Footer text displayed at the bottom of all reports.")
    logo = fields.Binary(related='partner_id.image',
                         default=_get_logo,
                         string="Company Logo",
                         readonly=False)
    # logo_web: do not store in attachments, since the image is retrieved in SQL for
    # performance reasons (see addons/web/controllers/main.py, Binary.company_logo)
    logo_web = fields.Binary(compute='_compute_logo_web',
                             store=True,
                             attachment=False)
    currency_id = fields.Many2one(
        'res.currency',
        string='Currency',
        required=True,
        default=lambda self: self._get_user_currency())
    user_ids = fields.Many2many('res.users',
                                'res_company_users_rel',
                                'cid',
                                'user_id',
                                string='Accepted Users')
    account_no = fields.Char(string='Account No.')
    street = fields.Char(compute='_compute_address', inverse='_inverse_street')
    street2 = fields.Char(compute='_compute_address',
                          inverse='_inverse_street2')
    zip = fields.Char(compute='_compute_address', inverse='_inverse_zip')
    city = fields.Char(compute='_compute_address', inverse='_inverse_city')
    state_id = fields.Many2one('res.country.state',
                               compute='_compute_address',
                               inverse='_inverse_state',
                               string="Fed. State")
    bank_ids = fields.One2many('res.partner.bank',
                               'company_id',
                               string='Bank Accounts',
                               help='Bank accounts related to this company')
    country_id = fields.Many2one('res.country',
                                 compute='_compute_address',
                                 inverse='_inverse_country',
                                 string="Country")
    email = fields.Char(related='partner_id.email', store=True, readonly=False)
    phone = fields.Char(related='partner_id.phone', store=True, readonly=False)
    website = fields.Char(related='partner_id.website', readonly=False)
    vat = fields.Char(related='partner_id.vat',
                      string="Tax ID",
                      readonly=False)
    company_registry = fields.Char()
    paperformat_id = fields.Many2one(
        'report.paperformat',
        'Paper format',
        default=lambda self: self.env.ref('base.paperformat_euro',
                                          raise_if_not_found=False))
    external_report_layout_id = fields.Many2one('ir.ui.view',
                                                'Document Template')
    base_onboarding_company_state = fields.Selection(
        [('not_done', "Not done"), ('just_done', "Just done"),
         ('done', "Done")],
        string="State of the onboarding company step",
        default='not_done')
    favicon = fields.Binary(
        string="Company Favicon",
        help=
        "This field holds the image used to display a favicon for a given company.",
        default=_get_default_favicon)

    _sql_constraints = [('name_uniq', 'unique (name)',
                         'The company name must be unique !')]

    @api.model_cr
    def init(self):
        for company in self.search([('paperformat_id', '=', False)]):
            paperformat_euro = self.env.ref('base.paperformat_euro', False)
            if paperformat_euro:
                company.write({'paperformat_id': paperformat_euro.id})
        sup = super(Company, self)
        if hasattr(sup, 'init'):
            sup.init()

    def _get_company_address_fields(self, partner):
        return {
            'street': partner.street,
            'street2': partner.street2,
            'city': partner.city,
            'zip': partner.zip,
            'state_id': partner.state_id,
            'country_id': partner.country_id,
        }

    # TODO @api.depends(): currently now way to formulate the dependency on the
    # partner's contact address
    def _compute_address(self):
        for company in self.filtered(lambda company: company.partner_id):
            address_data = company.partner_id.sudo().address_get(
                adr_pref=['contact'])
            if address_data['contact']:
                partner = company.partner_id.browse(
                    address_data['contact']).sudo()
                company.update(company._get_company_address_fields(partner))

    def _inverse_street(self):
        for company in self:
            company.partner_id.street = company.street

    def _inverse_street2(self):
        for company in self:
            company.partner_id.street2 = company.street2

    def _inverse_zip(self):
        for company in self:
            company.partner_id.zip = company.zip

    def _inverse_city(self):
        for company in self:
            company.partner_id.city = company.city

    def _inverse_state(self):
        for company in self:
            company.partner_id.state_id = company.state_id

    def _inverse_country(self):
        for company in self:
            company.partner_id.country_id = company.country_id

    @api.depends('partner_id', 'partner_id.image')
    def _compute_logo_web(self):
        for company in self:
            company.logo_web = tools.image_process(company.partner_id.image,
                                                   (180, None))

    @api.onchange('state_id')
    def _onchange_state(self):
        self.country_id = self.state_id.country_id

    @api.multi
    def on_change_country(self, country_id):
        # This function is called from account/models/chart_template.py, hence decorated with `multi`.
        self.ensure_one()
        currency_id = self._get_user_currency()
        if country_id:
            currency_id = self.env['res.country'].browse(
                country_id).currency_id
        return {'value': {'currency_id': currency_id.id}}

    @api.onchange('country_id')
    def _onchange_country_id_wrapper(self):
        res = {'domain': {'state_id': []}}
        if self.country_id:
            res['domain']['state_id'] = [('country_id', '=',
                                          self.country_id.id)]
        values = self.on_change_country(self.country_id.id)['value']
        for fname, value in values.items():
            setattr(self, fname, value)
        return res

    @api.model
    def _name_search(self,
                     name,
                     args=None,
                     operator='ilike',
                     limit=100,
                     name_get_uid=None):
        context = dict(self.env.context)
        newself = self
        if context.pop('user_preference', None):
            # We browse as superuser. Otherwise, the user would be able to
            # select only the currently visible companies (according to rules,
            # which are probably to allow to see the child companies) even if
            # she belongs to some other companies.
            companies = self.env.user.company_ids
            args = (args or []) + [('id', 'in', companies.ids)]
            newself = newself.sudo()
        return super(Company, newself.with_context(context))._name_search(
            name=name,
            args=args,
            operator=operator,
            limit=limit,
            name_get_uid=name_get_uid)

    @api.model
    @api.returns('self', lambda value: value.id)
    def _company_default_get(self, object=False, field=False):
        """ Returns the user's company
            - Deprecated
        """
        _logger.warning(
            _("The method '_company_default_get' on res.company is deprecated and shouldn't be used anymore"
              ))
        return self.env.company_id

    # deprecated, use clear_caches() instead
    def cache_restart(self):
        self.clear_caches()

    @api.model
    def create(self, vals):
        if not vals.get('favicon'):
            vals['favicon'] = self._get_default_favicon()
        if not vals.get('name') or vals.get('partner_id'):
            self.clear_caches()
            return super(Company, self).create(vals)
        partner = self.env['res.partner'].create({
            'name': vals['name'],
            'is_company': True,
            'image': vals.get('logo'),
            'customer': False,
            'email': vals.get('email'),
            'phone': vals.get('phone'),
            'website': vals.get('website'),
            'vat': vals.get('vat'),
        })
        vals['partner_id'] = partner.id
        self.clear_caches()
        company = super(Company, self).create(vals)
        # The write is made on the user to set it automatically in the multi company group.
        self.env.user.write({'company_ids': [(4, company.id)]})
        partner.write({'company_id': company.id})

        # Make sure that the selected currency is enabled
        if vals.get('currency_id'):
            currency = self.env['res.currency'].browse(vals['currency_id'])
            if not currency.active:
                currency.write({'active': True})
        return company

    @api.multi
    def write(self, values):
        self.clear_caches()
        # Make sure that the selected currency is enabled
        if values.get('currency_id'):
            currency = self.env['res.currency'].browse(values['currency_id'])
            if not currency.active:
                currency.write({'active': True})

        return super(Company, self).write(values)

    @api.constrains('parent_id')
    def _check_parent_id(self):
        if not self._check_recursion():
            raise ValidationError(_('You cannot create recursive companies.'))

    @api.multi
    def open_company_edit_report(self):
        self.ensure_one()
        return self.env['res.config.settings'].open_company()

    @api.multi
    def write_company_and_print_report(self, values):
        res = self.write(values)

        report_name = values.get('default_report_name')
        active_ids = values.get('active_ids')
        active_model = values.get('active_model')
        if report_name and active_ids and active_model:
            docids = self.env[active_model].browse(active_ids)
            return (self.env['ir.actions.report'].search(
                [('report_name', '=', report_name)],
                limit=1).with_context(values).report_action(docids))
        else:
            return res

    @api.model
    def action_open_base_onboarding_company(self):
        """ Onboarding step for company basic information. """
        action = self.env.ref(
            'base.action_open_base_onboarding_company').read()[0]
        action['res_id'] = self.env.company_id.id
        return action

    def set_onboarding_step_done(self, step_name):
        if self[step_name] == 'not_done':
            self[step_name] = 'just_done'

    def get_and_update_onbarding_state(self, onboarding_state, steps_states):
        """ Needed to display onboarding animations only one time. """
        old_values = {}
        all_done = True
        for step_state in steps_states:
            old_values[step_state] = self[step_state]
            if self[step_state] == 'just_done':
                self[step_state] = 'done'
            all_done = all_done and self[step_state] == 'done'

        if all_done:
            if self[onboarding_state] == 'not_done':
                # string `onboarding_state` instead of variable name is not an error
                old_values['onboarding_state'] = 'just_done'
            else:
                old_values['onboarding_state'] = 'done'
            self[onboarding_state] = 'done'
        return old_values

    @api.multi
    def action_save_onboarding_company_step(self):
        if bool(self.street):
            self.set_onboarding_step_done('base_onboarding_company_state')

    @api.model
    def _get_main_company(self):
        try:
            main_company = self.sudo().env.ref('base.main_company')
        except ValueError:
            main_company = self.env['res.company'].sudo().search([],
                                                                 limit=1,
                                                                 order="id")

        return main_company
Example #19
0
class attendance_raw_logs(models.Model):
    _name = 'attendance.raw.main'
    _description = 'Attendance Dat File Uploader'

    name = fields.Char(
        string='Sequence',
        default=lambda self: _('New Attendance Dat File'),
        size=64,
    )

    import_file = fields.Binary(string='Import Dat File')

    attendace_ids = fields.One2many('hr.attendance',
                                    'raw_attendance_id',
                                    string='Attendances',
                                    ondelete='restrict')

    raw_attendance_ids = fields.One2many('attendance.raw.list',
                                         'raw_attendance_id',
                                         string='Attendances')

    @api.model
    def create(self, values):
        if values.get(
                'name',
                _('New Attendance Dat File')) == _('New Attendance Dat File'):
            values['name'] = self.env['ir.sequence'].next_by_code(
                'attendance.raw.main')
        res = super(attendance_raw_logs, self).create(values)
        return res

    @api.one
    def create_raw_attendance(self):  #, raw_main_id, uid
        if not self.import_file:
            raise UserError('No File Found. Please Upload the Log File.')
        elif len(self.import_file) == 0:
            raise UserError('No File Found. Please Upload the Log File.')

        FILENAME = "/opt/Log_File/newfile_dat_file_" + str(self._uid) + ".dat"
        with open(FILENAME, "wb") as f:
            text = self.import_file
            f.write(base64.b64decode(text))
            #raise Warning(f.readline())

        f.close()
        with open(FILENAME, "r") as f:
            lines = f.readlines()
            user_tz = self.env.user.tz
            local = pytz.timezone(user_tz)
            if lines:
                raw_list_obj = self.env['attendance.raw.list'].search([
                    ('raw_attendance_id', '=', self.id)
                ])
                #if raw_list_obj:
                #for lst in raw_list_obj:
                #	if lst.name == '1':
                #		_logger.info(lst.date_time_logs)
                res = raw_list_obj.unlink()
                #if res:
                raw_list_obj_1 = self.env['attendance.raw.list']
                for line in lines:
                    if line:
                        line_list = line.split()
                        datetime_log = fields.Datetime.from_string(
                            line_list[1] + ' ' + line_list[2]
                        )  #datetime.strptime(line_list[1] + ' ' + line_list[2], '%Y-%m-%d %H:%M:%S').replace(tzinfo=local)
                        #if line_list[0] == '1' and line_list[1] == '2011-08-16':
                        #_logger.info(line.split())
                        #_logger.info(datetime_log)
                        #raise Warning(datetime_log.replace(tzinfo=local))
                        hour_date = timedelta(hours=8)

                        _logger.info(datetime_log)
                        _logger.info(datetime_log - hour_date)
                        raw_list_obj_1.create({
                            'raw_attendance_id': self.id,
                            'name': line_list[0],
                            'date_time_logs': datetime_log - hour_date,
                            'date_logs': line_list[1]
                        })
        f.close()
        os.remove(FILENAME)

    @api.one
    def create_attendance(self):
        user_tz = self.env.user.tz
        local = pytz.timezone(user_tz)
        if not self.import_file:
            raise UserError('No File Found. Please Upload the Log File.')
        elif len(self.import_file) == 0:
            raise UserError('No File Found. Please Upload the Log File.')

        raw_list_obj = self.env['attendance.raw.list'].search([
            ('raw_attendance_id', '=', self.id)
        ])
        obj_employees = self.env['hr.employee'].search([('biometric_number',
                                                         '!=', False)])
        raw_list_model = self.env['attendance.raw.list']

        if not raw_list_obj:
            raise UserError(
                'No Raw Attendance Found. Please Upload the Log File and Submit to Generate the Raw Attendance.'
            )
        elif len(raw_list_obj) == 0:
            raise UserError(
                'No Raw Attendance Found. Please Upload the Log File and Submit to Generate the Raw Attendance.'
            )

        #To Get the Distinct Dates in the Logs
        dates_lst = []
        if raw_list_obj:
            for raw in raw_list_obj:
                if len(dates_lst) > 0:
                    if raw.date_logs not in dates_lst:
                        dates_lst.append(raw.date_logs)
                else:
                    dates_lst.append(raw.date_logs)

        if obj_employees:
            for employee in obj_employees:
                for dt_lst in dates_lst:

                    #Due to Looping and Distinction of Records by Date, Some Logs have no Log in Dates
                    #This will handle the Logs that does not Exists
                    raw_list_obj_asc_rec = raw_list_model.search([
                        ('date_logs', '=', dt_lst),
                        ('name', '=', employee.biometric_number)
                    ])

                    if raw_list_obj_asc_rec:
                        raw_list_obj_asc_rec = False
                        raw_list_obj_desc_rec = False
                        raw_list_obj_asc_rec = raw_list_model.search(
                            [('date_logs', '=', dt_lst),
                             ('name', '=', employee.biometric_number),
                             ('raw_attendance_id', '=', self.id)],
                            limit=1,
                            order='date_time_logs asc')
                        raw_list_obj_desc_rec = raw_list_model.search(
                            [('date_logs', '=', dt_lst),
                             ('name', '=', employee.biometric_number),
                             ('raw_attendance_id', '=', self.id)],
                            limit=1,
                            order='date_time_logs desc')
                        #FIRST Criteria
                        hr_attendance_add = self.env['hr.attendance']
                        hr_attendance = self.env['hr.attendance'].search([
                            ('employee_id', '=', employee.id),
                            ('check_in', '=',
                             raw_list_obj_asc_rec.date_time_logs),
                            ('check_out', '=',
                             raw_list_obj_desc_rec.date_time_logs),
                        ])
                        if not hr_attendance:
                            hr_attendance_add.create({
                                'raw_attendance_id':
                                self.id,
                                'employee_id':
                                employee.id,
                                'check_in':
                                raw_list_obj_asc_rec.date_time_logs,
                                'check_out':
                                raw_list_obj_desc_rec.date_time_logs
                            })
class view_gmcreport2(models.TransientModel):
    _name = 'view.gmcreport2'
    _rec_name = 'datas_fname'
    datas_fname=fields.Char('File Name', size=256)
    file_name=fields.Binary('Report')
Example #21
0
class kardex_productos_inventario(models.TransientModel):
    _name = 'kardex.productos.mov'
    _description = "kardex productos"

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

    @api.model
    def _get_from_date(self):
        company = self.env.user.company_id
        current_date = datetime.date.today()
        from_date = company.compute_fiscalyear_dates(current_date)['date_from']
        return from_date

    excel_binary = fields.Binary('Field')
    file_name = fields.Char('Report_Name', readonly=True)
    product = fields.Many2one('product.product',
                              string='Product',
                              required=True)
    company = fields.Many2one('res.company',
                              required=True,
                              default=lambda self: self.env.user.company_id,
                              string='Current Company')

    ubicacion = fields.Many2one('stock.location',
                                domain=[('usage', '=', "internal")],
                                string='Location')

    date_from = fields.Date(string='Date from', default=_get_from_date)
    date_to = fields.Date(string='Date to', default=fields.Date.today)
    revisio = fields.Char(string='revision')

    cantidad_inicial = fields.Float('Duantity Ini:', readonly=True)
    costo_promedio_inicial = fields.Float('Cost  Ini:', readonly=True)
    costo_total_inicial = fields.Float('Cost Total Ini:', readonly=True)

    cantidad_final = fields.Float('Quantity End :', readonly=True)
    costo_promedio = fields.Float('Cost End:', readonly=True)
    costo_total = fields.Float('Costo Total End', readonly=True)

    aplica = fields.Selection([('todas', 'All '),
                               ('ubicacion', 'By location')],
                              required=True,
                              default='todas',
                              string='Selection location')

    currency_id = fields.Many2one(
        'res.currency',
        string='Company currency',
        required=True,
        default=lambda self: self.env.user.company_id.currency_id,
        readonly=True)

    obj_kardex = fields.One2many(comodel_name='kardex.productos.mov.detalle',
                                 inverse_name='obj_kardex_mostrarmovi')

    def _action_imprimir_excel(self, ):

        workbook = xlwt.Workbook()
        column_heading_style = easyxf('font:height 200;font:bold True;')
        worksheet = workbook.add_sheet('Kardex report')
        date_format = xlwt.XFStyle()
        date_format.num_format_str = 'dd/mm/yyyy'

        number_format = xlwt.XFStyle()
        number_format.num_format_str = '#,##0.00'

        #Ponemos los primeros encabezados
        worksheet.write(0, 0, _('Kardex report product'), column_heading_style)

        query_rorte = """
        select Max(id) as id  from kardex_productos_mov 
    """
        self.env.cr.execute(query_rorte, )
        tr = self.env.cr.dictfetchall()
        for tr_t in tr:
            todo_reporte = self.env['kardex.productos.mov'].search([
                ('id', '=', int(tr_t['id']))
            ])
            tf = 0
            for todfact in todo_reporte:
                worksheet.write(1, 0, "Date from:", column_heading_style)
                worksheet.write(1, 1, todfact.date_from, date_format)
                worksheet.write(2, 0, "Date to:", column_heading_style)
                worksheet.write(2, 1, todfact.date_to, date_format)

                worksheet.write(1, 2, "Product:", column_heading_style)
                worksheet.write(1, 3, todfact.product.name)
                worksheet.write(2, 2, "Current Company:", column_heading_style)
                worksheet.write(2, 3, todfact.company.name)
                worksheet.write(1, 4, "Location:", column_heading_style)
                ubi_hijo = todfact.ubicacion.name
                ubi_pad = todfact.ubicacion.location_id.name
                worksheet.write(1, 5, str(ubi_pad) + "/" + str(ubi_hijo))

                #Ponemos los primeros encabezados del detalle
        worksheet.write(4, 0, _('Date'), column_heading_style)
        worksheet.write(4, 1, _('Concept'), column_heading_style)
        worksheet.write(4, 2, _('U_In'), column_heading_style)
        worksheet.write(4, 3, _('U_Out'), column_heading_style)
        worksheet.write(4, 4, _('U_balance'), column_heading_style)
        worksheet.write(4, 5, _("Costo_Uni"), column_heading_style)
        worksheet.write(4, 6, _('V_In'), column_heading_style)
        worksheet.write(4, 7, _('V_Out'), column_heading_style)
        worksheet.write(4, 8, _('V_balance'), column_heading_style)
        worksheet.write(4, 9, _('Costo_Prom'), column_heading_style)
        worksheet.write(4, 10, _('Origin'), column_heading_style)
        worksheet.write(4, 11, _('Pickin'), column_heading_style)
        worksheet.write(4, 12, _('Invoice'), column_heading_style)
        worksheet.write(4, 13, _('Inventory'), column_heading_style)

        heading = "Product Kardex Detail"
        # worksheet.write_merge(5, 0, 5,13, heading, easyxf('font:height 200; align: horiz center;pattern: pattern solid, fore_color black; font: color white; font:bold True;' "borders: top thin,bottom thin"))
        # Se tiene que hacer de ultimo para saber cuanto mide todo

        # se recorre el reporte

        todo_reporte = self.env['kardex.productos.mov.detalle'].search([
            ('obj_kardex_mostrarmovi', '=', int(tr_t['id']))
        ])
        tf = 0
        for todfact in todo_reporte:

            tf += 1
            ini = 5
            worksheet.write(tf + ini, 0, todfact.date, date_format)
            worksheet.write(tf + ini, 1, todfact.concepto)
            worksheet.write(tf + ini, 2, todfact.u_entrada, number_format)
            worksheet.write(tf + ini, 3, todfact.u_salida, number_format)
            worksheet.write(tf + ini, 4, todfact.u_saldo, number_format)
            worksheet.write(tf + ini, 5, todfact.costo_unit, number_format)
            worksheet.write(tf + ini, 6, todfact.v_entrada, number_format)
            worksheet.write(tf + ini, 7, todfact.v_salida, number_format)
            worksheet.write(tf + ini, 8, todfact.v_saldo, number_format)
            worksheet.write(tf + ini, 9, todfact.costo_prom, number_format)
            worksheet.write(tf + ini, 10, todfact.origin, number_format)
            worksheet.write(tf + ini, 11, todfact.picking_id.name)
            worksheet.write(tf + ini, 12, todfact.account_invoice.name)
            worksheet.write(tf + ini, 13, todfact.inventario.name)

        fp = io.BytesIO()
        workbook.save(fp)
        excel_file = base64.encodestring(fp.getvalue())

        self.excel_binary = excel_file
        nombre_tabla = "Kardex Report.xls"
        self.file_name = nombre_tabla
        fp.close()

    # @api.depends('company')
    # def _actualizar_compania(self):
    #   self.company=domain=[('company_id', '=', self.company.id)]

    @api.onchange('company')
    def _cambio_company(self):
        #       # Set ubucacion ID
        if self.company:
            return {
                'domain': {
                    'ubicacion': [('company_id', '=', self.company.id),
                                  ('usage', '=', "internal")]
                }
            }

    #@api.one
    def _borracampos(self):
        self.cantidad_inicial = ""
        self.cantidad_final = ""
        self.costo_promedio = ""
        self.costo_total = ""
        self.aplica = "todas"
        self.company = ""

    def buscar_producto(self):
        if self.date_from > self.date_to:
            raise UserError(
                _("The Start date cannot be less than the end date "))
        else:
            self._borra_datos_tabla()

    def _borra_datos_tabla(self):
        query_rorte = """
        select Max(id) as id  from kardex_productos_mov 
    """
        self.env.cr.execute(query_rorte, )
        tr = self.env.cr.dictfetchall()
        for tr_t in tr:

            todo = self.env['kardex.productos.mov'].search([('id', '<',
                                                             int(tr_t['id']))])
            for tod in todo:
                tod.unlink()

            todo_reporte = self.env['kardex.productos.mov.detalle'].search([
                ('id', '<', int(tr_t['id']))
            ])
            for tod in todo_reporte:

                tod.unlink()

        for karde in self:
            karde.obj_kardex.unlink()

        #Empezamos a realizar el saldo
        self._saldo_anterior()

    def _saldo_anterior(self):

        query_total = self._moviento_completo()
        query_saldo_anterior = """
      select (SUM(u_entrada)-SUM(u_salida))as u_ante , 
      (SUM(v_entrada)-SUM(v_salida))as v_ante  from (
""" + query_total + """


)as saldo_ante where date < %s --) estes espara obteber el saldo anterior

        """
        producto = 0
        producto = self.product.id
        ubicacion = self.ubicacion.id
        date_from = self.date_from

        if self.aplica == "todas":
            query_saldo_anterior_param = (producto, producto, date_from)

        if self.aplica == "ubicacion":
            query_saldo_anterior_param = (producto, ubicacion, producto,
                                          ubicacion, date_from)

        self.env.cr.execute(query_saldo_anterior, query_saldo_anterior_param)

        saldo_anterior = self.env.cr.dictfetchall()
        for linea in saldo_anterior:
            self.cantidad_inicial = linea['u_ante']
            self.costo_total_inicial = linea['v_ante']
            if self.costo_total_inicial == 0:
                self.costo_promedio_inicial = 0
            if self.costo_total_inicial > 0:
                self.costo_promedio_inicial = self.costo_total_inicial / self.cantidad_inicial

        #Ponemos el saldo anteririor en la tabla
        self._saldo_anterior_tabla()

    def _saldo_anterior_tabla(self):
        for kardex in self:
            concepto = "Previous balance"
            u_saldo = self.cantidad_inicial
            costo_uni = self.costo_promedio_inicial
            v_saldo = self.costo_total_inicial
            line = ({
                'concepto': concepto,
                'u_saldo': u_saldo,
                'costo_unit': costo_uni,
                'v_saldo': v_saldo,
            })
            lines = [(0, 0, line)]
            kardex.write({'obj_kardex': lines})
        self._movimiento_producto()

    def _movimiento_producto(self):
        query_total = self._moviento_completo()
        query_movimiento = """
    select * from (
      """ + query_total + """
      
    ) as mov where date >=%s and date <=%s 

    """

        producto = 0
        producto = self.product.id
        ubicacion = self.ubicacion.id
        date_from = self.date_from
        date_to = self.date_to

        if self.aplica == "todas":
            query_movimiento_param = (producto, producto, date_from, date_to)

        if self.aplica == "ubicacion":
            query_movimiento_param = (producto, ubicacion, producto, ubicacion,
                                      date_from, date_to)

        self.env.cr.execute(query_movimiento, query_movimiento_param)

        movim = self.env.cr.dictfetchall()
        for mov in movim:
            for kardex in self:
                fecha = mov['date']
                concepto = mov['reference']
                u_entrada = mov['u_entrada']
                u_salida = mov['u_salida']
                u_saldo = mov['u_saldo']
                costo_unit = mov['costo_unit']
                v_entrada = mov['v_entrada']
                v_salida = mov['v_salida']
                v_saldo = mov['v_saldo']
                origin = mov['origin']
                picking_id = mov['picking_id']
                inventario = mov['inventory_id']

                line = ({
                    'date': fecha,
                    'concepto': concepto,
                    'u_entrada': u_entrada,
                    'u_salida': u_salida,
                    'u_saldo': u_saldo,
                    'costo_unit': costo_unit,
                    'v_entrada': v_entrada,
                    'v_salida': v_salida,
                    'v_saldo': v_saldo,
                    'origin': origin,
                    'picking_id': picking_id,
                    'inventario': inventario,
                })
                lines = [(0, 0, line)]
                kardex.write({'obj_kardex': lines})
        self._saldo_final()

    def _saldo_final(self):

        query_total = self._moviento_completo()
        query_saldo_final = """
      select (SUM(u_entrada)-SUM(u_salida))as u_saldo , 
      (SUM(v_entrada)-SUM(v_salida))as v_saldo  from (
    """ + query_total + """

     )as saldo_ante where date <= %s --) estes espara obteber el saldo final

        """
        producto = 0
        producto = self.product.id
        ubicacion = self.ubicacion.id
        date_to = self.date_to

        if self.aplica == "todas":
            query_saldo_final_param = (producto, producto, date_to)

        if self.aplica == "ubicacion":
            query_saldo_final_param = (producto, ubicacion, producto,
                                       ubicacion, date_to)

        self.env.cr.execute(query_saldo_final, query_saldo_final_param)

        saldo_final = self.env.cr.dictfetchall()
        for linea in saldo_final:

            self.cantidad_final = linea['u_saldo']
            self.costo_total = linea['v_saldo']
            if self.costo_total > 0:
                self.costo_promedio = self.costo_total / self.cantidad_final

        #buscamos las facturas
        self._buscar_factura()

    def _moviento_completo(self):
        local_des = ""
        location_id = ""
        if self.aplica == "todas":
            local_des = "sm.location_dest_id > 0"
            location_id = "sm.location_id > 0"
        if self.aplica == "ubicacion":
            local_des = "sm.location_dest_id=%s"
            location_id = "sm.location_id=%s"

        query_movimiento = """
 select id,CAST(date AS date),company_id, product,nombre,u_entrada,
 u_salida,u_saldo,costo_unit,v_entrada,v_salida,
v_saldo,state,origin,reference,usage,complete_name,ubicacion,inventory_id ,picking_id  
from (
-------------3)Comienza el segundo select
	select id,date_expected as date,company_id ,product_id as product,
  name as nombre,u_entrada,u_salida,
         SUM(u_entrada-u_salida)over (order by date_expected asc,id asc)as u_saldo,
         costo_unit,v_entrada,v_salida,
	SUM(v_entrada-v_salida)over (order by date_expected asc,id asc)as  v_saldo,state
  ,origin,reference,usage,
        complete_name,ubicacion,inventory_id ,picking_id  
	  from (

--------------- EMPIEZA LA UNION 

		--- unimos entradas
		select id,date_expected,product_id,name,company_id, u_entrada,u_salida,  
    costo_unit
		, v_entrada,v_salida,v_saldo,state,origin,reference,
		usage,complete_name,ubicacion,create_uid,inventory_id,picking_id   
		from 
		(
			select sm.id,sm.date_expected,sm.product_id,sm.name,sm.company_id,sm.product_qty as u_entrada,(sm.product_qty * 0)u_salida,
			(sm.price_unit) as costo_unit
			,(sm.product_qty * sm.price_unit) as v_entrada,(sm.product_qty  * 0)v_salida,(sm.product_qty  *0)v_saldo,sm.state,sm.origin,sm.reference,
			sl.usage,sl.complete_name,(sm.location_dest_id)as ubicacion,sm.create_uid,sm.inventory_id,sm.picking_id
			  from stock_move sm  inner join stock_location sl on sm.location_dest_id=sl.id where sl.usage='internal'
			 and sm.product_id=%s and sm.state='done' and """ + local_des + """

			order by date_expected asc 
		)as    sl   
							
		---- para las entrada

		UNION

		 -------------unimos salidas
		select id,date_expected,product_id,name,company_id, u_entrada,u_salida,costo_unit
		,v_entrada,v_salida,v_saldo,state,origin,reference,
		usage,complete_name,ubicacion,create_uid,inventory_id,picking_id  
		from
		   (
				select sm.id,sm.date_expected,sm.product_id,sm.name,sm.company_id,(sm.product_qty * 0) as u_entrada,
                                 sm.product_qty as u_salida,(am.amount_total_signed/sm.product_qty ) as costo_unit
                                 ,(sm.product_qty *0)as v_entrada,(am.amount_total_signed )v_salida,(sm.product_qty  *0)v_saldo,
                                  sm.state,sm.origin,sm.reference
				,sl.usage,sl.complete_name,(sm.location_id)as ubicacion,sm.create_uid,sm.inventory_id,sm.picking_id 
				  from stock_move sm  inner join stock_location sl on sm.location_id=sl.id 
           inner join account_move am on am.stock_move_id= sm.id
          where sl.usage='internal'
				   and sm.product_id=%s and sm.state='done' and """ + location_id + """
				order by date_expected asc
		  )sl 
		-------------- para las salidas

	) as kardex order by date asc -------2)TERMINA EL 2DO SELECT
)as kardex2   ------1)TERMINA EL PRIMER SELECT
        """
        return query_movimiento

    def _buscar_factura(self):
        for fact in self.obj_kardex:
            if fact.origin:
                query_origen = """
        select Min(id) as id from account_move where invoice_origin = %s 
        """
                query_origen_param = (fact.origin, )

                self.env.cr.execute(query_origen, query_origen_param)

                movim = self.env.cr.dictfetchall()
                for mov in movim:
                    # #  facturas=self.env['account.invoice'].search([('origin','=',fact.origin)])

                    fact.account_invoice = mov['id']

        self._action_imprimir_excel()
Example #22
0
class ShopeeProductPreset(models.Model):
    _name = 'shopee.product.preset'
    _inherit = 'ecommerce.product.preset'

    product_tmpl_ids = fields.One2many('product.template',
                                       'shopee_product_preset_id',
                                       readonly=True)
    name = fields.Char("Name")
    #    category_id = fields.Integer(related='ecomm_categ_id.platform_categ_idn', store=True, readonly=True)
    #    category_name = fields.Char(string=_("Shopee Category"), related='ecomm_categ_id.complete_name', readonly=True)
    description = fields.Text(string=_("Description"))
    weight = fields.Float(string=_("Package Weight (kg)"))
    package_length = fields.Float(string=_("Package Lenghth (cm)"))
    package_width = fields.Float(string=_("Package Width (cm)"))
    package_height = fields.Float(string=_("Package Height (cm)"))
    size_chart = fields.Binary(string=_("Size Chart"))
    condition = fields.Selection(selection=[('NEW', _("New")),
                                            ('USED', _("Used"))],
                                 string=_("Shopee Condition"))
    status = fields.Selection(selection=[('NORMAL', _("Normal")),
                                         ('DELETED', _("Deleted")),
                                         ('BANNED', _("Banned")),
                                         ('UNLIST', _("Unlisted"))],
                              string=_("Status"),
                              readonly=True)
    is_pre_order = fields.Boolean(string=_("Is Pre-order"))
    days_to_ship = fields.Integer(string=_("Days To Ship"))

    def format_attr_values(self):
        self.ensure_one()
        return [{
            'idn': line.attr_id.platform_attr_idn,
            'value': line.value_id.name
        } for line in self.ecomm_attribute_lines]

    #modify this if use stock delivery (logistic_id, enabled, shipping_fee, size_id, is_free)


#   shopee_wholesales(min, max, unit_price) > later

#   shopee_name, shopee_description, shopee_price, shopee_stock, shopee_item_sku, shopee_variations(name, stock, price, variation_sku), images(url)

    def _onchange_ecomm_categ_id_shopee(self):
        shop = self.env['ecommerce.shop'].search([('platform_id.platform', '=',
                                                   'shopee')])[:1]
        shop.ensure_one()
        attrs = shop._py_client_shopee().item.get_attributes(
            category_id=self.ecomm_categ_id.platform_categ_idn).get(
                'attributes')
        triplets = [(5, _, _)]
        for attr in attrs:
            ecomm_attrs = self.env['ecommerce.attribute'].search([
                ('platform_id', '=', self.platform_id.id),
                ('platform_attr_idn', '=', attr['attribute_id'])
            ])
            ecomm_attr = ecomm_attrs and ecomm_attrs[0] or self.env[
                'ecommerce.attribute'].create({
                    'name':
                    attr['attribute_name'],
                    'platform_id':
                    self.platform_id.id,
                    'platform_attr_idn':
                    attr['attribute_id'],
                    'mandatory':
                    attr['is_mandatory'],
                    'attr_type':
                    attr['attribute_type'],
                    'input_type':
                    attr['input_type'],
                    'value_ids': [(0, _, {
                        'name': option,
                    }) for option in attr['options']],
                })
            triplets += [(0, _, {
                'attr_id': ecomm_attr.id,
                'res_id': self.id,
                'res_model': self._name
            })]
        self.update({'ecomm_attribute_lines': triplets})
        _logger.info(self.ecomm_attribute_lines.mapped('res_model'))
Example #23
0
class Partner(models.Model):
    _description = 'Contact'
    _inherit = ['format.address.mixin']
    _name = "res.partner"
    _order = "display_name"

    def _default_category(self):
        return self.env['res.partner.category'].browse(self._context.get('category_id'))

    def _default_company(self):
        return self.env['res.company']._company_default_get('res.partner')

    name = fields.Char(index=True)
    display_name = fields.Char(compute='_compute_display_name', store=True, index=True)
    date = fields.Date(index=True)
    title = fields.Many2one('res.partner.title')
    parent_id = fields.Many2one('res.partner', string='Related Company', index=True)
    parent_name = fields.Char(related='parent_id.name', readonly=True, string='Parent name')
    child_ids = fields.One2many('res.partner', 'parent_id', string='Contacts', domain=[('active', '=', True)])  # force "active_test" domain to bypass _search() override
    ref = fields.Char(string='Internal Reference', index=True)
    lang = fields.Selection(_lang_get, string='Language', default=lambda self: self.env.lang,
                            help="If the selected language is loaded in the system, all documents related to "
                                 "this contact will be printed in this language. If not, it will be English.")
    tz = fields.Selection(_tz_get, string='Timezone', default=lambda self: self._context.get('tz'),
                          help="The partner's timezone, used to output proper date and time values "
                               "inside printed reports. It is important to set a value for this field. "
                               "You should use the same timezone that is otherwise used to pick and "
                               "render date and time values: your computer's timezone.")
    tz_offset = fields.Char(compute='_compute_tz_offset', string='Timezone offset', invisible=True)
    user_id = fields.Many2one('res.users', string='Salesperson',
      help='The internal user that is in charge of communicating with this contact if any.')
    vat = fields.Char(string='TIN', help="Tax Identification Number. "
                                         "Fill it if the company is subjected to taxes. "
                                         "Used by the some of the legal statements.")
    bank_ids = fields.One2many('res.partner.bank', 'partner_id', string='Banks')
    website = fields.Char(help="Website of Partner or Company")
    comment = fields.Text(string='Notes')

    category_id = fields.Many2many('res.partner.category', column1='partner_id',
                                    column2='category_id', string='Tags', default=_default_category)
    credit_limit = fields.Float(string='Credit Limit')
    barcode = fields.Char(oldname='ean13')
    active = fields.Boolean(default=True)
    customer = fields.Boolean(string='Is a Customer', default=True,
                               help="Check this box if this contact is a customer.")
    supplier = fields.Boolean(string='Is a Vendor',
                               help="Check this box if this contact is a vendor. "
                               "If it's not checked, purchase people will not see it when encoding a purchase order.")
    employee = fields.Boolean(help="Check this box if this contact is an Employee.")
    function = fields.Char(string='Job Position')
    type = fields.Selection(
        [('contact', 'Contact'),
         ('invoice', 'Invoice address'),
         ('delivery', 'Shipping address'),
         ('other', 'Other address')], string='Address Type',
        default='contact',
        help="Used to select automatically the right address according to the context in sales and purchases documents.")
    street = fields.Char()
    street2 = fields.Char()
    zip = fields.Char(change_default=True)
    city = fields.Char()
    state_id = fields.Many2one("res.country.state", string='State', ondelete='restrict')
    country_id = fields.Many2one('res.country', string='Country', ondelete='restrict')
    email = fields.Char()
    email_formatted = fields.Char(
        'Formatted Email', compute='_compute_email_formatted',
        help='Format email address "Name <email@domain>"')
    phone = fields.Char()
    mobile = fields.Char()
    is_company = fields.Boolean(string='Is a Company', default=False,
        help="Check if the contact is a company, otherwise it is a person")
    industry_id = fields.Many2one('res.partner.industry', 'Sector of Activity')
    # company_type is only an interface field, do not use it in business logic
    company_type = fields.Selection(string='Company Type',
        selection=[('person', 'Individual'), ('company', 'Company')],
        compute='_compute_company_type', readonly=False)
    company_id = fields.Many2one('res.company', 'Company', index=True, default=_default_company)
    color = fields.Integer(string='Color Index', default=0)
    user_ids = fields.One2many('res.users', 'partner_id', string='Users', auto_join=True)
    partner_share = fields.Boolean(
        'Share Partner', compute='_compute_partner_share', store=True,
        help="Either customer (no user), either shared user. Indicated the current partner is a customer without "
             "access or with a limited access created for sharing data.")
    contact_address = fields.Char(compute='_compute_contact_address', string='Complete Address')

    # technical field used for managing commercial fields
    commercial_partner_id = fields.Many2one('res.partner', compute='_compute_commercial_partner',
                                             string='Commercial Entity', store=True, index=True)
    commercial_partner_country_id = fields.Many2one('res.country', related='commercial_partner_id.country_id', store=True)
    commercial_company_name = fields.Char('Company Name Entity', compute='_compute_commercial_company_name',
                                          store=True)
    company_name = fields.Char('Company Name')

    # image: all image fields are base64 encoded and PIL-supported
    image = fields.Binary("Image", attachment=True,
        help="This field holds the image used as avatar for this contact, limited to 1024x1024px",)
    image_medium = fields.Binary("Medium-sized image", attachment=True,
        help="Medium-sized image of this contact. It is automatically "\
             "resized as a 128x128px image, with aspect ratio preserved. "\
             "Use this field in form views or some kanban views.")
    image_small = fields.Binary("Small-sized image", attachment=True,
        help="Small-sized image of this contact. It is automatically "\
             "resized as a 64x64px image, with aspect ratio preserved. "\
             "Use this field anywhere a small image is required.")
    # hack to allow using plain browse record in qweb views, and used in ir.qweb.field.contact
    self = fields.Many2one(comodel_name=_name, compute='_compute_get_ids')

    _sql_constraints = [
        ('check_name', "CHECK( (type='contact' AND name IS NOT NULL) or (type!='contact') )", 'Contacts require a name.'),
    ]

    @api.depends('is_company', 'name', 'parent_id.name', 'type', 'company_name')
    def _compute_display_name(self):
        diff = dict(show_address=None, show_address_only=None, show_email=None)
        names = dict(self.with_context(**diff).name_get())
        for partner in self:
            partner.display_name = names.get(partner.id)

    @api.depends('tz')
    def _compute_tz_offset(self):
        for partner in self:
            partner.tz_offset = datetime.datetime.now(pytz.timezone(partner.tz or 'GMT')).strftime('%z')

    @api.depends('user_ids.share')
    def _compute_partner_share(self):
        for partner in self:
            partner.partner_share = not partner.user_ids or any(user.share for user in partner.user_ids)

    @api.depends(lambda self: self._display_address_depends())
    def _compute_contact_address(self):
        for partner in self:
            partner.contact_address = partner._display_address()

    @api.one
    def _compute_get_ids(self):
        self.self = self.id

    @api.depends('is_company', 'parent_id.commercial_partner_id')
    def _compute_commercial_partner(self):
        for partner in self:
            if partner.is_company or not partner.parent_id:
                partner.commercial_partner_id = partner
            else:
                partner.commercial_partner_id = partner.parent_id.commercial_partner_id

    @api.depends('company_name', 'parent_id.is_company', 'commercial_partner_id.name')
    def _compute_commercial_company_name(self):
        for partner in self:
            p = partner.commercial_partner_id
            partner.commercial_company_name = p.is_company and p.name or partner.company_name

    @api.model
    def _fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
        if (not view_id) and (view_type == 'form') and self._context.get('force_email'):
            view_id = self.env.ref('base.view_partner_simple_form').id
        res = super(Partner, self)._fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
        if view_type == 'form':
            res['arch'] = self._fields_view_get_address(res['arch'])
        return res

    @api.constrains('parent_id')
    def _check_parent_id(self):
        if not self._check_recursion():
            raise ValidationError(_('You cannot create recursive Partner hierarchies.'))

    @api.multi
    def copy(self, default=None):
        self.ensure_one()
        default = dict(default or {}, name=_('%s (copy)') % self.name)
        return super(Partner, self).copy(default)

    @api.onchange('parent_id')
    def onchange_parent_id(self):
        # return values in result, as this method is used by _fields_sync()
        if not self.parent_id:
            return
        result = {}
        partner = getattr(self, '_origin', self)
        if partner.parent_id and partner.parent_id != self.parent_id:
            result['warning'] = {
                'title': _('Warning'),
                'message': _('Changing the company of a contact should only be done if it '
                             'was never correctly set. If an existing contact starts working for a new '
                             'company then a new contact should be created under that new '
                             'company. You can use the "Discard" button to abandon this change.')}
        if partner.type == 'contact' or self.type == 'contact':
            # for contacts: copy the parent address, if set (aka, at least one
            # value is set in the address: otherwise, keep the one from the
            # contact)
            address_fields = self._address_fields()
            if any(self.parent_id[key] for key in address_fields):
                def convert(value):
                    return value.id if isinstance(value, models.BaseModel) else value
                result['value'] = {key: convert(self.parent_id[key]) for key in address_fields}
        return result

    @api.onchange('country_id')
    def _onchange_country_id(self):
        if self.country_id:
            return {'domain': {'state_id': [('country_id', '=', self.country_id.id)]}}
        else:
            return {'domain': {'state_id': []}}

    @api.onchange('email')
    def onchange_email(self):
        if not self.image and self._context.get('gravatar_image') and self.email:
            self.image = self._get_gravatar_image(self.email)

    @api.depends('name', 'email')
    def _compute_email_formatted(self):
        for partner in self:
            partner.email_formatted = formataddr((partner.name, partner.email))

    @api.depends('is_company')
    def _compute_company_type(self):
        for partner in self:
            partner.company_type = 'company' if partner.is_company else 'person'

    @api.onchange('company_type')
    def onchange_company_type(self):
        self.is_company = (self.company_type == 'company')

    @api.multi
    def _update_fields_values(self, fields):
        """ Returns dict of write() values for synchronizing ``fields`` """
        values = {}
        for fname in fields:
            field = self._fields[fname]
            if field.type == 'many2one':
                values[fname] = self[fname].id
            elif field.type == 'one2many':
                raise AssertionError(_('One2Many fields cannot be synchronized as part of `commercial_fields` or `address fields`'))
            elif field.type == 'many2many':
                values[fname] = [(6, 0, self[fname].ids)]
            else:
                values[fname] = self[fname]
        return values

    @api.model
    def _address_fields(self):
        """Returns the list of address fields that are synced from the parent."""
        return list(ADDRESS_FIELDS)

    @api.multi
    def update_address(self, vals):
        addr_vals = {key: vals[key] for key in self._address_fields() if key in vals}
        if addr_vals:
            return super(Partner, self).write(addr_vals)

    @api.model
    def _commercial_fields(self):
        """ Returns the list of fields that are managed by the commercial entity
        to which a partner belongs. These fields are meant to be hidden on
        partners that aren't `commercial entities` themselves, and will be
        delegated to the parent `commercial entity`. The list is meant to be
        extended by inheriting classes. """
        return ['vat', 'credit_limit']

    @api.multi
    def _commercial_sync_from_company(self):
        """ Handle sync of commercial fields when a new parent commercial entity is set,
        as if they were related fields """
        commercial_partner = self.commercial_partner_id
        if commercial_partner != self:
            sync_vals = commercial_partner._update_fields_values(self._commercial_fields())
            self.write(sync_vals)

    @api.multi
    def _commercial_sync_to_children(self):
        """ Handle sync of commercial fields to descendants """
        commercial_partner = self.commercial_partner_id
        sync_vals = commercial_partner._update_fields_values(self._commercial_fields())
        sync_children = self.child_ids.filtered(lambda c: not c.is_company)
        for child in sync_children:
            child._commercial_sync_to_children()
        sync_children._compute_commercial_partner()
        return sync_children.write(sync_vals)

    @api.multi
    def _fields_sync(self, values):
        """ Sync commercial fields and address fields from company and to children after create/update,
        just as if those were all modeled as fields.related to the parent """
        # 1. From UPSTREAM: sync from parent
        if values.get('parent_id') or values.get('type', 'contact'):
            # 1a. Commercial fields: sync if parent changed
            if values.get('parent_id'):
                self._commercial_sync_from_company()
            # 1b. Address fields: sync if parent or use_parent changed *and* both are now set 
            if self.parent_id and self.type == 'contact':
                onchange_vals = self.onchange_parent_id().get('value', {})
                self.update_address(onchange_vals)

        # 2. To DOWNSTREAM: sync children
        if self.child_ids:
            # 2a. Commercial Fields: sync if commercial entity
            if self.commercial_partner_id == self:
                commercial_fields = self._commercial_fields()
                if any(field in values for field in commercial_fields):
                    self._commercial_sync_to_children()
            for child in self.child_ids.filtered(lambda c: not c.is_company):
                if child.commercial_partner_id != self.commercial_partner_id :
                    self._commercial_sync_to_children()
                    break
            # 2b. Address fields: sync if address changed
            address_fields = self._address_fields()
            if any(field in values for field in address_fields):
                contacts = self.child_ids.filtered(lambda c: c.type == 'contact')
                contacts.update_address(values)

    @api.multi
    def _handle_first_contact_creation(self):
        """ On creation of first contact for a company (or root) that has no address, assume contact address
        was meant to be company address """
        parent = self.parent_id
        address_fields = self._address_fields()
        if (parent.is_company or not parent.parent_id) and len(parent.child_ids) == 1 and \
            any(self[f] for f in address_fields) and not any(parent[f] for f in address_fields):
            addr_vals = self._update_fields_values(address_fields)
            parent.update_address(addr_vals)

    def _clean_website(self, website):
        url = urls.url_parse(website)
        if not url.scheme:
            if not url.netloc:
                url = url.replace(netloc=url.path, path='')
            website = url.replace(scheme='http').to_url()
        return website

    @api.multi
    def write(self, vals):
        # res.partner must only allow to set the company_id of a partner if it
        # is the same as the company of all users that inherit from this partner
        # (this is to allow the code from res_users to write to the partner!) or
        # if setting the company_id to False (this is compatible with any user
        # company)
        if vals.get('website'):
            vals['website'] = self._clean_website(vals['website'])
        if vals.get('parent_id'):
            vals['company_name'] = False
        if vals.get('company_id'):
            company = self.env['res.company'].browse(vals['company_id'])
            for partner in self:
                if partner.user_ids:
                    companies = set(user.company_id for user in partner.user_ids)
                    if len(companies) > 1 or company not in companies:
                        raise UserError(_("You can not change the company as the partner/user has multiple user linked with different companies."))
        tools.image_resize_images(vals)

        result = True
        # To write in SUPERUSER on field is_company and avoid access rights problems.
        if 'is_company' in vals and self.user_has_groups('base.group_partner_manager') and not self.env.uid == SUPERUSER_ID:
            result = super(Partner, self).sudo().write({'is_company': vals.get('is_company')})
            del vals['is_company']
        result = result and super(Partner, self).write(vals)
        for partner in self:
            if any(u.has_group('base.group_user') for u in partner.user_ids if u != self.env.user):
                self.env['res.users'].check_access_rights('write')
            partner._fields_sync(vals)
        return result

    @api.model
    def create(self, vals):
        if vals.get('website'):
            vals['website'] = self._clean_website(vals['website'])
        if vals.get('parent_id'):
            vals['company_name'] = False
        tools.image_resize_images(vals)
        partner = super(Partner, self).create(vals)
        partner._fields_sync(vals)
        partner._handle_first_contact_creation()
        return partner

    @api.multi
    def create_company(self):
        self.ensure_one()
        if self.company_name:
            # Create parent company
            values = dict(name=self.company_name, is_company=True)
            values.update(self._update_fields_values(self._address_fields()))
            new_company = self.create(values)
            # Set new company as my parent
            self.write({
                'parent_id': new_company.id,
                'child_ids': [(1, partner_id, dict(parent_id=new_company.id)) for partner_id in self.child_ids.ids]
            })
        return True

    @api.multi
    def open_commercial_entity(self):
        """ Utility method used to add an "Open Company" button in partner views """
        self.ensure_one()
        return {'type': 'ir.actions.act_window',
                'res_model': 'res.partner',
                'view_mode': 'form',
                'res_id': self.commercial_partner_id.id,
                'target': 'current',
                'flags': {'form': {'action_buttons': True}}}

    @api.multi
    def open_parent(self):
        """ Utility method used to add an "Open Parent" button in partner views """
        self.ensure_one()
        address_form_id = self.env.ref('base.view_partner_address_form').id
        return {'type': 'ir.actions.act_window',
                'res_model': 'res.partner',
                'view_mode': 'form',
                'views': [(address_form_id, 'form')],
                'res_id': self.parent_id.id,
                'target': 'new',
                'flags': {'form': {'action_buttons': True}}}

    @api.multi
    def name_get(self):
        res = []
        for partner in self:
            name = partner.name or ''

            if partner.company_name or partner.parent_id:
                if not name and partner.type in ['invoice', 'delivery', 'other']:
                    name = dict(self.fields_get(['type'])['type']['selection'])[partner.type]
                if not partner.is_company:
                    name = "%s, %s" % (partner.commercial_company_name or partner.parent_id.name, name)
            if self._context.get('show_address_only'):
                name = partner._display_address(without_company=True)
            if self._context.get('show_address'):
                name = name + "\n" + partner._display_address(without_company=True)
            name = name.replace('\n\n', '\n')
            name = name.replace('\n\n', '\n')
            if self._context.get('show_email') and partner.email:
                name = "%s <%s>" % (name, partner.email)
            if self._context.get('html_format'):
                name = name.replace('\n', '<br/>')
            res.append((partner.id, name))
        return res

    def _parse_partner_name(self, text, context=None):
        """ Supported syntax:
            - 'Raoul <*****@*****.**>': will find name and email address
            - otherwise: default, everything is set as the name """
        emails = tools.email_split(text.replace(' ', ','))
        if emails:
            email = emails[0]
            name = text[:text.index(email)].replace('"', '').replace('<', '').strip()
        else:
            name, email = text, ''
        return name, email

    @api.model
    def name_create(self, name):
        """ Override of orm's name_create method for partners. The purpose is
            to handle some basic formats to create partners using the
            name_create.
            If only an email address is received and that the regex cannot find
            a name, the name will have the email value.
            If 'force_email' key in context: must find the email address. """
        name, email = self._parse_partner_name(name)
        if self._context.get('force_email') and not email:
            raise UserError(_("Couldn't create contact without email address!"))
        if not name and email:
            name = email
        partner = self.create({self._rec_name: name or email, 'email': email or self.env.context.get('default_email', False)})
        return partner.name_get()[0]

    @api.model
    def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
        """ Override search() to always show inactive children when searching via ``child_of`` operator. The ORM will
        always call search() with a simple domain of the form [('parent_id', 'in', [ids])]. """
        # a special ``domain`` is set on the ``child_ids`` o2m to bypass this logic, as it uses similar domain expressions
        if len(args) == 1 and len(args[0]) == 3 and args[0][:2] == ('parent_id','in') \
                and args[0][2] != [False]:
            self = self.with_context(active_test=False)
        return super(Partner, self)._search(args, offset=offset, limit=limit, order=order,
                                            count=count, access_rights_uid=access_rights_uid)

    @api.model
    def name_search(self, name, args=None, operator='ilike', limit=100):
        if args is None:
            args = []
        if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'):
            self.check_access_rights('read')
            where_query = self._where_calc(args)
            self._apply_ir_rules(where_query, 'read')
            from_clause, where_clause, where_clause_params = where_query.get_sql()
            where_str = where_clause and (" WHERE %s AND " % where_clause) or ' WHERE '

            # search on the name of the contacts and of its company
            search_name = name
            if operator in ('ilike', 'like'):
                search_name = '%%%s%%' % name
            if operator in ('=ilike', '=like'):
                operator = operator[1:]

            unaccent = get_unaccent_wrapper(self.env.cr)

            query = """SELECT id
                         FROM res_partner
                      {where} ({email} {operator} {percent}
                           OR {display_name} {operator} {percent}
                           OR {reference} {operator} {percent}
                           OR {vat} {operator} {percent})
                           -- don't panic, trust postgres bitmap
                     ORDER BY {display_name} {operator} {percent} desc,
                              {display_name}
                    """.format(where=where_str,
                               operator=operator,
                               email=unaccent('email'),
                               display_name=unaccent('display_name'),
                               reference=unaccent('ref'),
                               percent=unaccent('%s'),
                               vat=unaccent('vat'),)

            where_clause_params += [search_name]*5
            if limit:
                query += ' limit %s'
                where_clause_params.append(limit)
            self.env.cr.execute(query, where_clause_params)
            partner_ids = [row[0] for row in self.env.cr.fetchall()]

            if partner_ids:
                return self.browse(partner_ids).name_get()
            else:
                return []
        return super(Partner, self).name_search(name, args, operator=operator, limit=limit)

    @api.model
    def find_or_create(self, email):
        """ Find a partner with the given ``email`` or use :py:method:`~.name_create`
            to create one

            :param str email: email-like string, which should contain at least one email,
                e.g. ``"Raoul Grosbedon <*****@*****.**>"``"""
        assert email, 'an email is required for find_or_create to work'
        emails = tools.email_split(email)
        if emails:
            email = emails[0]
        partners = self.search([('email', '=ilike', email)], limit=1)
        return partners.id or self.name_create(email)[0]

    def _get_gravatar_image(self, email):
        email_hash = hashlib.md5(email.lower()).hexdigest()
        url = "https://www.gravatar.com/avatar/" + email_hash
        try:
            res = requests.get(url, params={'d': '404', 's': '128'}, timeout=5)
            if res.status_code != requests.codes.ok:
                return False
        except requests.exceptions.ConnectionError as e:
            return False
        return base64.b64encode(res.content)

    @api.multi
    def _email_send(self, email_from, subject, body, on_error=None):
        for partner in self.filtered('email'):
            tools.email_send(email_from, [partner.email], subject, body, on_error)
        return True

    @api.multi
    def address_get(self, adr_pref=None):
        """ Find contacts/addresses of the right type(s) by doing a depth-first-search
        through descendants within company boundaries (stop at entities flagged ``is_company``)
        then continuing the search at the ancestors that are within the same company boundaries.
        Defaults to partners of type ``'default'`` when the exact type is not found, or to the
        provided partner itself if no type ``'default'`` is found either. """
        adr_pref = set(adr_pref or [])
        if 'contact' not in adr_pref:
            adr_pref.add('contact')
        result = {}
        visited = set()
        for partner in self:
            current_partner = partner
            while current_partner:
                to_scan = [current_partner]
                # Scan descendants, DFS
                while to_scan:
                    record = to_scan.pop(0)
                    visited.add(record)
                    if record.type in adr_pref and not result.get(record.type):
                        result[record.type] = record.id
                    if len(result) == len(adr_pref):
                        return result
                    to_scan = [c for c in record.child_ids
                                 if c not in visited
                                 if not c.is_company] + to_scan

                # Continue scanning at ancestor if current_partner is not a commercial entity
                if current_partner.is_company or not current_partner.parent_id:
                    break
                current_partner = current_partner.parent_id

        # default to type 'contact' or the partner itself
        default = result.get('contact', self.id or False)
        for adr_type in adr_pref:
            result[adr_type] = result.get(adr_type) or default
        return result

    @api.model
    def view_header_get(self, view_id, view_type):
        res = super(Partner, self).view_header_get(view_id, view_type)
        if res: return res
        if not self._context.get('category_id'):
            return False
        return _('Partners: ') + self.env['res.partner.category'].browse(self._context['category_id']).name

    @api.model
    @api.returns('self')
    def main_partner(self):
        ''' Return the main partner '''
        return self.env.ref('base.main_partner')

    @api.model
    def _get_default_address_format(self):
        return "%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s"

    @api.multi
    def _display_address(self, without_company=False):

        '''
        The purpose of this function is to build and return an address formatted accordingly to the
        standards of the country where it belongs.

        :param address: browse record of the res.partner to format
        :returns: the address formatted in a display that fit its country habits (or the default ones
            if not country is specified)
        :rtype: string
        '''
        # get the information that will be injected into the display format
        # get the address format
        address_format = self.country_id.address_format or \
            self._get_default_address_format()
        args = {
            'state_code': self.state_id.code or '',
            'state_name': self.state_id.name or '',
            'country_code': self.country_id.code or '',
            'country_name': self.country_id.name or '',
            'company_name': self.commercial_company_name or '',
        }
        for field in self._address_fields():
            args[field] = getattr(self, field) or ''
        if without_company:
            args['company_name'] = ''
        elif self.commercial_company_name:
            address_format = '%(company_name)s\n' + address_format
        return address_format % args

    def _display_address_depends(self):
        # field dependencies of method _display_address()
        return self._address_fields() + [
            'country_id.address_format', 'country_id.code', 'country_id.name',
            'company_name', 'state_id.code', 'state_id.name',
        ]
Example #24
0
class LabTest(models.Model):
    _name = "lab.test"

    name = fields.Char(string="Name", readonly=True)
    master_id = fields.Many2one(comodel_name="lab.master", string="Lab-Test")
    price = fields.Float(string="Price")
    template = fields.Html(string="Template")
    report = fields.Html(string="Report")
    value_ids = fields.One2many(comodel_name="lab.test.detail.value",
                                inverse_name="lab_id")
    image_ids = fields.One2many(comodel_name="lab.test.detail.image",
                                inverse_name="lab_id")
    laboratory_id = fields.Many2one(comodel_name="arc.lab",
                                    string="Laboratory")
    report_pdf = fields.Binary(string="Report")
    file_name = fields.Char(string="File Name", default="report.pdf")
    progress = fields.Selection(selection=PROGRESS_INFO,
                                string="Progress",
                                default="draft")
    comment = fields.Text(string="Comment")
    company_id = fields.Many2one(
        comodel_name="res.company",
        string="Company",
        default=lambda self: self.env.user.company_id.id)

    @api.multi
    def trigger_report(self):
        value_recs = self.value_ids
        image_recs = self.image_ids

        data = {}
        for rec in value_recs:
            if rec.value:
                data[rec.sequence] = rec.value
            else:
                data[rec.sequence] = ""

        for rec in image_recs:
            if rec.image:
                data[rec.sequence] = rec.image
            else:
                data[rec.sequence] = ""

        data[0] = self.laboratory_id.name
        data[1] = self.laboratory_id.patient_id.name
        data[2] = self.laboratory_id.patient_id.patient_uid
        data[3] = self.laboratory_id.patient_id.address
        data[4] = self.laboratory_id.patient_id.mobile
        data[5] = self.laboratory_id.patient_id.email

        print data

        template = self.template

        if self.paramsi == 13:
            report = template.format(data[0], data[1], data[2], data[3],
                                     data[4], data[5], data[6], data[7],
                                     data[8], data[9], data[10], data[11],
                                     data[12], data[13])
        if self.paramsi == 9:
            report = template.format(data[0], data[1], data[2], data[3],
                                     data[4], data[5], data[6], data[7],
                                     data[8])

        if self.paramsi == 7:
            report = template.format(data[0], data[1], data[2], data[3],
                                     data[4], data[5], data[6], data[7])

        self.report = report
        pdfkit.from_string(report, '/opt/kon/out.pdf')

        pdf_file = open('/opt/kon/out.pdf', 'rb')
        out = pdf_file.read()
        pdf_file.close()
        gentextfile = base64.b64encode(out)

        self.report_pdf = gentextfile
        self.progress = "completed"

    @api.model
    def create(self, vals):
        master_rec = self.env["lab.master"].search([("id", "=",
                                                     vals["master_id"])])
        value_recs = self.env["lab.master.detail.value"].search([
            ("lab_id", "=", vals["master_id"])
        ])
        image_recs = self.env["lab.master.detail.image"].search([
            ("lab_id", "=", vals["master_id"])
        ])

        vals["price"] = master_rec.price
        vals["template"] = master_rec.template
        vals["paramsi"] = master_rec.paramsi
        vals["name"] = self.env["ir.sequence"].next_by_code(self._name)
        lab_id = super(LabTest, self).create(vals)

        for rec in value_recs:
            self.env["lab.test.detail.value"].create({
                "sequence": rec.sequence,
                "name": rec.name,
                "lab_id": lab_id.id
            })

        for rec in image_recs:
            self.env["lab.test.detail.image"].create({
                "sequence": rec.sequence,
                "name": rec.name,
                "lab_id": lab_id.id
            })

        return lab_id
Example #25
0
class IrActionsActWindow(models.Model):
    _name = 'ir.actions.act_window'
    _description = 'Action Window'
    _table = 'ir_act_window'
    _inherit = 'ir.actions.actions'
    _sequence = 'ir_actions_id_seq'
    _order = 'name'

    @api.constrains('res_model', 'src_model')
    def _check_model(self):
        for action in self:
            if action.res_model not in self.env:
                raise ValidationError(
                    _('Invalid model name %r in action definition.') %
                    action.res_model)
            if action.src_model and action.src_model not in self.env:
                raise ValidationError(
                    _('Invalid model name %r in action definition.') %
                    action.src_model)

    @api.depends('view_ids.view_mode', 'view_mode', 'view_id.type')
    def _compute_views(self):
        """ Compute an ordered list of the specific view modes that should be
            enabled when displaying the result of this action, along with the
            ID of the specific view to use for each mode, if any were required.

            This function hides the logic of determining the precedence between
            the view_modes string, the view_ids o2m, and the view_id m2o that
            can be set on the action.
        """
        for act in self:
            act.views = [(view.view_id.id, view.view_mode)
                         for view in act.view_ids]
            got_modes = [view.view_mode for view in act.view_ids]
            all_modes = act.view_mode.split(',')
            missing_modes = [
                mode for mode in all_modes if mode not in got_modes
            ]
            if missing_modes:
                if act.view_id.type in missing_modes:
                    # reorder missing modes to put view_id first if present
                    missing_modes.remove(act.view_id.type)
                    act.views.append((act.view_id.id, act.view_id.type))
                act.views.extend([(False, mode) for mode in missing_modes])

    @api.depends('res_model', 'search_view_id')
    def _compute_search_view(self):
        for act in self:
            fvg = self.env[act.res_model].fields_view_get(
                act.search_view_id.id, 'search')
            act.search_view = str(fvg)

    name = fields.Char(string='Action Name', translate=True)
    type = fields.Char(default="ir.actions.act_window")
    view_id = fields.Many2one('ir.ui.view',
                              string='View Ref.',
                              ondelete='set null')
    domain = fields.Char(
        string='Domain Value',
        help=
        "Optional domain filtering of the destination data, as a Python expression"
    )
    context = fields.Char(
        string='Context Value',
        default={},
        required=True,
        help=
        "Context dictionary as Python expression, empty by default (Default: {})"
    )
    res_id = fields.Integer(
        string='Record ID',
        help=
        "Database ID of record to open in form view, when ``view_mode`` is set to 'form' only"
    )
    res_model = fields.Char(
        string='Destination Model',
        required=True,
        help="Model name of the object to open in the view window")
    src_model = fields.Char(
        string='Source Model',
        help=
        "Optional model name of the objects on which this action should be visible"
    )
    target = fields.Selection([('current', 'Current Window'),
                               ('new', 'New Window'),
                               ('inline', 'Inline Edit'),
                               ('fullscreen', 'Full Screen'),
                               ('main', 'Main action of Current Window')],
                              default="current",
                              string='Target Window')
    view_mode = fields.Char(
        required=True,
        default='tree,form',
        help=
        "Comma-separated list of allowed view modes, such as 'form', 'tree', 'calendar', etc. (Default: tree,form)"
    )
    view_type = fields.Selection(
        [('tree', 'Tree'), ('form', 'Form')],
        default="form",
        string='View Type',
        required=True,
        help=
        "View type: Tree type to use for the tree view, set to 'tree' for a hierarchical tree view, or 'form' for a regular list view"
    )
    usage = fields.Char(
        string='Action Usage',
        help="Used to filter menu and home actions from the user form.")
    view_ids = fields.One2many('ir.actions.act_window.view',
                               'act_window_id',
                               string='No of Views')
    views = fields.Binary(compute='_compute_views',
                          help="This function field computes the ordered list of views that should be enabled " \
                               "when displaying the result of an action, federating view mode, views and " \
                               "reference view. The result is returned as an ordered list of pairs (view_id,view_mode).")
    limit = fields.Integer(default=80, help='Default limit for the list view')
    groups_id = fields.Many2many('res.groups',
                                 'ir_act_window_group_rel',
                                 'act_id',
                                 'gid',
                                 string='Groups')
    search_view_id = fields.Many2one('ir.ui.view', string='Search View Ref.')
    filter = fields.Boolean()
    auto_search = fields.Boolean(default=True)
    search_view = fields.Text(compute='_compute_search_view')
    multi = fields.Boolean(
        string='Restrict to lists',
        help=
        "If checked and the action is bound to a model, it will only appear in the More menu on list views"
    )

    @api.multi
    def read(self, fields=None, load='_classic_read'):
        """ call the method get_empty_list_help of the model and set the window action help message
        """
        result = super(IrActionsActWindow, self).read(fields, load=load)
        if not fields or 'help' in fields:
            for values in result:
                model = values.get('res_model')
                if model in self.env:
                    eval_ctx = dict(self.env.context)
                    try:
                        ctx = safe_eval(values.get('context', '{}'), eval_ctx)
                    except:
                        ctx = {}
                    values['help'] = self.with_context(
                        **ctx).env[model].get_empty_list_help(
                            values.get('help', ''))
        return result

    @api.model
    def for_xml_id(self, module, xml_id):
        """ Returns the act_window object created for the provided xml_id

        :param module: the module the act_window originates in
        :param xml_id: the namespace-less id of the action (the @id
                       attribute from the XML file)
        :return: A read() view of the ir.actions.act_window
        """
        record = self.env.ref("%s.%s" % (module, xml_id))
        return record.read()[0]

    @api.model_create_multi
    def create(self, vals_list):
        self.clear_caches()
        return super(IrActionsActWindow, self).create(vals_list)

    @api.multi
    def unlink(self):
        self.clear_caches()
        return super(IrActionsActWindow, self).unlink()

    @api.multi
    def exists(self):
        ids = self._existing()
        existing = self.filtered(lambda rec: rec.id in ids)
        if len(existing) < len(self):
            # mark missing records in cache with a failed value
            exc = MissingError(_("Record does not exist or has been deleted."))
            for record in (self - existing):
                record._cache.set_failed(self._fields, exc)
        return existing

    @api.model
    @tools.ormcache()
    def _existing(self):
        self._cr.execute("SELECT id FROM %s" % self._table)
        return set(row[0] for row in self._cr.fetchall())
Example #26
0
class HrEmployeePrivate(models.Model):
    """
    NB: Any field only available on the model hr.employee (i.e. not on the
    hr.employee.public model) should have `groups="hr.group_hr_user"` on its
    definition to avoid being prefetched when the user hasn't access to the
    hr.employee model. Indeed, the prefetch loads the data for all the fields
    that are available according to the group defined on them.
    """
    _name = "hr.employee"
    _description = "Employee"
    _order = 'name'
    _inherit = [
        'hr.employee.base', 'mail.thread', 'mail.activity.mixin',
        'resource.mixin', 'avatar.mixin'
    ]
    _mail_post_access = 'read'

    # resource and user
    # required on the resource, make sure required="True" set in the view
    name = fields.Char(string="Employee Name",
                       related='resource_id.name',
                       store=True,
                       readonly=False,
                       tracking=True)
    user_id = fields.Many2one('res.users',
                              'User',
                              related='resource_id.user_id',
                              store=True,
                              readonly=False)
    user_partner_id = fields.Many2one(related='user_id.partner_id',
                                      related_sudo=False,
                                      string="User's partner")
    active = fields.Boolean('Active',
                            related='resource_id.active',
                            default=True,
                            store=True,
                            readonly=False)
    company_id = fields.Many2one('res.company', required=True)
    company_country_id = fields.Many2one('res.country',
                                         'Company Country',
                                         related='company_id.country_id',
                                         readonly=True)
    company_country_code = fields.Char(related='company_country_id.code',
                                       readonly=True)
    # private partner
    address_home_id = fields.Many2one(
        'res.partner',
        'Address',
        help=
        'Enter here the private address of the employee, not the one linked to your company.',
        groups="hr.group_hr_user",
        tracking=True,
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    is_address_home_a_company = fields.Boolean(
        'The employee address has a company linked',
        compute='_compute_is_address_home_a_company',
    )
    private_email = fields.Char(related='address_home_id.email',
                                string="Private Email",
                                groups="hr.group_hr_user")
    lang = fields.Selection(related='address_home_id.lang',
                            string="Lang",
                            groups="hr.group_hr_user",
                            readonly=False)
    country_id = fields.Many2one('res.country',
                                 'Nationality (Country)',
                                 groups="hr.group_hr_user",
                                 tracking=True)
    gender = fields.Selection([('male', 'Male'), ('female', 'Female'),
                               ('other', 'Other')],
                              groups="hr.group_hr_user",
                              tracking=True)
    marital = fields.Selection([('single', 'Single'), ('married', 'Married'),
                                ('cohabitant', 'Legal Cohabitant'),
                                ('widower', 'Widower'),
                                ('divorced', 'Divorced')],
                               string='Marital Status',
                               groups="hr.group_hr_user",
                               default='single',
                               tracking=True)
    spouse_complete_name = fields.Char(string="Spouse Complete Name",
                                       groups="hr.group_hr_user",
                                       tracking=True)
    spouse_birthdate = fields.Date(string="Spouse Birthdate",
                                   groups="hr.group_hr_user",
                                   tracking=True)
    children = fields.Integer(string='Number of Children',
                              groups="hr.group_hr_user",
                              tracking=True)
    place_of_birth = fields.Char('Place of Birth',
                                 groups="hr.group_hr_user",
                                 tracking=True)
    country_of_birth = fields.Many2one('res.country',
                                       string="Country of Birth",
                                       groups="hr.group_hr_user",
                                       tracking=True)
    birthday = fields.Date('Date of Birth',
                           groups="hr.group_hr_user",
                           tracking=True)
    ssnid = fields.Char('SSN No',
                        help='Social Security Number',
                        groups="hr.group_hr_user",
                        tracking=True)
    sinid = fields.Char('SIN No',
                        help='Social Insurance Number',
                        groups="hr.group_hr_user",
                        tracking=True)
    identification_id = fields.Char(string='Identification No',
                                    groups="hr.group_hr_user",
                                    tracking=True)
    passport_id = fields.Char('Passport No',
                              groups="hr.group_hr_user",
                              tracking=True)
    bank_account_id = fields.Many2one(
        'res.partner.bank',
        'Bank Account Number',
        domain=
        "[('partner_id', '=', address_home_id), '|', ('company_id', '=', False), ('company_id', '=', company_id)]",
        groups="hr.group_hr_user",
        tracking=True,
        help='Employee bank salary account')
    permit_no = fields.Char('Work Permit No',
                            groups="hr.group_hr_user",
                            tracking=True)
    visa_no = fields.Char('Visa No', groups="hr.group_hr_user", tracking=True)
    visa_expire = fields.Date('Visa Expire Date',
                              groups="hr.group_hr_user",
                              tracking=True)
    work_permit_expiration_date = fields.Date('Work Permit Expiration Date',
                                              groups="hr.group_hr_user",
                                              tracking=True)
    has_work_permit = fields.Binary(string="Work Permit",
                                    groups="hr.group_hr_user",
                                    tracking=True)
    work_permit_scheduled_activity = fields.Boolean(default=False,
                                                    groups="hr.group_hr_user")
    additional_note = fields.Text(string='Additional Note',
                                  groups="hr.group_hr_user",
                                  tracking=True)
    certificate = fields.Selection([
        ('graduate', 'Graduate'),
        ('bachelor', 'Bachelor'),
        ('master', 'Master'),
        ('doctor', 'Doctor'),
        ('other', 'Other'),
    ],
                                   'Certificate Level',
                                   default='other',
                                   groups="hr.group_hr_user",
                                   tracking=True)
    study_field = fields.Char("Field of Study",
                              groups="hr.group_hr_user",
                              tracking=True)
    study_school = fields.Char("School",
                               groups="hr.group_hr_user",
                               tracking=True)
    emergency_contact = fields.Char("Emergency Contact",
                                    groups="hr.group_hr_user",
                                    tracking=True)
    emergency_phone = fields.Char("Emergency Phone",
                                  groups="hr.group_hr_user",
                                  tracking=True)
    km_home_work = fields.Integer(string="Home-Work Distance",
                                  groups="hr.group_hr_user",
                                  tracking=True)

    job_id = fields.Many2one(tracking=True)
    phone = fields.Char(related='address_home_id.phone',
                        related_sudo=False,
                        readonly=False,
                        string="Private Phone",
                        groups="hr.group_hr_user")
    # employee in company
    child_ids = fields.One2many('hr.employee',
                                'parent_id',
                                string='Direct subordinates')
    category_ids = fields.Many2many('hr.employee.category',
                                    'employee_category_rel',
                                    'emp_id',
                                    'category_id',
                                    groups="hr.group_hr_manager",
                                    string='Tags')
    # misc
    notes = fields.Text('Notes', groups="hr.group_hr_user")
    color = fields.Integer('Color Index', default=0, groups="hr.group_hr_user")
    barcode = fields.Char(string="Badge ID",
                          help="ID used for employee identification.",
                          groups="hr.group_hr_user",
                          copy=False)
    pin = fields.Char(
        string="PIN",
        groups="hr.group_hr_user",
        copy=False,
        help=
        "PIN used to Check In/Out in the Kiosk Mode of the Attendance application (if enabled in Configuration) and to change the cashier in the Point of Sale application."
    )
    departure_reason_id = fields.Many2one("hr.departure.reason",
                                          string="Departure Reason",
                                          groups="hr.group_hr_user",
                                          copy=False,
                                          tracking=True,
                                          ondelete='restrict')
    departure_description = fields.Text(string="Additional Information",
                                        groups="hr.group_hr_user",
                                        copy=False,
                                        tracking=True)
    departure_date = fields.Date(string="Departure Date",
                                 groups="hr.group_hr_user",
                                 copy=False,
                                 tracking=True)
    message_main_attachment_id = fields.Many2one(groups="hr.group_hr_user")
    id_card = fields.Binary(string="ID Card Copy", groups="hr.group_hr_user")
    driving_license = fields.Binary(string="Driving License",
                                    groups="hr.group_hr_user")

    _sql_constraints = [
        ('barcode_uniq', 'unique (barcode)',
         "The Badge ID must be unique, this one is already assigned to another employee."
         ),
        ('user_uniq', 'unique (user_id, company_id)',
         "A user cannot be linked to multiple employees in the same company.")
    ]

    @api.depends('name', 'user_id.avatar_1920', 'image_1920')
    def _compute_avatar_1920(self):
        super()._compute_avatar_1920()

    @api.depends('name', 'user_id.avatar_1024', 'image_1024')
    def _compute_avatar_1024(self):
        super()._compute_avatar_1024()

    @api.depends('name', 'user_id.avatar_512', 'image_512')
    def _compute_avatar_512(self):
        super()._compute_avatar_512()

    @api.depends('name', 'user_id.avatar_256', 'image_256')
    def _compute_avatar_256(self):
        super()._compute_avatar_256()

    @api.depends('name', 'user_id.avatar_128', 'image_128')
    def _compute_avatar_128(self):
        super()._compute_avatar_128()

    def _compute_avatar(self, avatar_field, image_field):
        for employee in self:
            avatar = employee[image_field]
            if not avatar:
                if employee.user_id:
                    avatar = employee.user_id[avatar_field]
                else:
                    avatar = employee._avatar_get_placeholder()
            employee[avatar_field] = avatar

    def name_get(self):
        if self.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate, self).name_get()
        return self.env['hr.employee.public'].browse(self.ids).name_get()

    def _read(self, fields):
        if self.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate, self)._read(fields)

        res = self.env['hr.employee.public'].browse(self.ids).read(fields)
        for r in res:
            record = self.browse(r['id'])
            record._update_cache({k: v
                                  for k, v in r.items() if k in fields},
                                 validate=False)

    @api.model
    def _cron_check_work_permit_validity(self):
        # Called by a cron
        # Schedule an activity 1 month before the work permit expires
        outdated_days = fields.Date.today() + relativedelta(months=+1)
        nearly_expired_work_permits = self.search([
            ('work_permit_scheduled_activity', '=', False),
            ('work_permit_expiration_date', '<', outdated_days)
        ])
        employees_scheduled = self.env['hr.employee']
        for employee in nearly_expired_work_permits.filtered(
                lambda employee: employee.parent_id):
            responsible_user_id = employee.parent_id.user_id.id
            if responsible_user_id:
                employees_scheduled |= employee
                lang = self.env['res.partner'].browse(responsible_user_id).lang
                formated_date = format_date(
                    employee.env,
                    employee.work_permit_expiration_date,
                    date_format="dd MMMM y",
                    lang_code=lang)
                employee.activity_schedule(
                    'mail.mail_activity_data_todo',
                    note=_(
                        'The work permit of %(employee)s expires at %(date)s.',
                        employee=employee.name,
                        date=formated_date),
                    user_id=responsible_user_id)
        employees_scheduled.write({'work_permit_scheduled_activity': True})

    def read(self, fields, load='_classic_read'):
        if self.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate, self).read(fields, load=load)
        private_fields = set(fields).difference(
            self.env['hr.employee.public']._fields.keys())
        if private_fields:
            raise AccessError(
                _('The fields "%s" you try to read is not available on the public employee profile.'
                  ) % (','.join(private_fields)))
        return self.env['hr.employee.public'].browse(self.ids).read(fields,
                                                                    load=load)

    @api.model
    def load_views(self, views, options=None):
        if self.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate, self).load_views(views,
                                                             options=options)
        return self.env['hr.employee.public'].load_views(views,
                                                         options=options)

    @api.model
    def _search(self,
                args,
                offset=0,
                limit=None,
                order=None,
                count=False,
                access_rights_uid=None):
        """
            We override the _search because it is the method that checks the access rights
            This is correct to override the _search. That way we enforce the fact that calling
            search on an hr.employee returns a hr.employee recordset, even if you don't have access
            to this model, as the result of _search (the ids of the public employees) is to be
            browsed on the hr.employee model. This can be trusted as the ids of the public
            employees exactly match the ids of the related hr.employee.
        """
        if self.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate,
                         self)._search(args,
                                       offset=offset,
                                       limit=limit,
                                       order=order,
                                       count=count,
                                       access_rights_uid=access_rights_uid)
        ids = self.env['hr.employee.public']._search(
            args,
            offset=offset,
            limit=limit,
            order=order,
            count=count,
            access_rights_uid=access_rights_uid)
        if not count and isinstance(ids, Query):
            # the result is expected from this table, so we should link tables
            ids = super(HrEmployeePrivate,
                        self.sudo())._search([('id', 'in', ids)])
        return ids

    def get_formview_id(self, access_uid=None):
        """ Override this method in order to redirect many2one towards the right model depending on access_uid """
        if access_uid:
            self_sudo = self.with_user(access_uid)
        else:
            self_sudo = self

        if self_sudo.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate,
                         self).get_formview_id(access_uid=access_uid)
        # Hardcode the form view for public employee
        return self.env.ref('hr.hr_employee_public_view_form').id

    def get_formview_action(self, access_uid=None):
        """ Override this method in order to redirect many2one towards the right model depending on access_uid """
        res = super(HrEmployeePrivate,
                    self).get_formview_action(access_uid=access_uid)
        if access_uid:
            self_sudo = self.with_user(access_uid)
        else:
            self_sudo = self

        if not self_sudo.check_access_rights('read', raise_exception=False):
            res['res_model'] = 'hr.employee.public'

        return res

    @api.constrains('pin')
    def _verify_pin(self):
        for employee in self:
            if employee.pin and not employee.pin.isdigit():
                raise ValidationError(
                    _("The PIN must be a sequence of digits."))

    @api.onchange('user_id')
    def _onchange_user(self):
        if self.user_id:
            self.update(self._sync_user(self.user_id, (bool(self.image_1920))))
            if not self.name:
                self.name = self.user_id.name

    @api.onchange('resource_calendar_id')
    def _onchange_timezone(self):
        if self.resource_calendar_id and not self.tz:
            self.tz = self.resource_calendar_id.tz

    def _sync_user(self, user, employee_has_image=False):
        vals = dict(
            work_email=user.email,
            user_id=user.id,
        )
        if not employee_has_image:
            vals['image_1920'] = user.image_1920
        if user.tz:
            vals['tz'] = user.tz
        return vals

    @api.model
    def create(self, vals):
        if vals.get('user_id'):
            user = self.env['res.users'].browse(vals['user_id'])
            vals.update(self._sync_user(user, bool(vals.get('image_1920'))))
            vals['name'] = vals.get('name', user.name)
        employee = super(HrEmployeePrivate, self).create(vals)
        if employee.department_id:
            self.env['mail.channel'].sudo().search([
                ('subscription_department_ids', 'in',
                 employee.department_id.id)
            ])._subscribe_users_automatically()
        # Launch onboarding plans
        if not employee._launch_plans_from_trigger(
                trigger='employee_creation'):
            # Keep the recommend message if no plans are launched
            url = '/web#%s' % url_encode(
                {
                    'action': 'hr.plan_wizard_action',
                    'active_id': employee.id,
                    'active_model': 'hr.employee',
                    'menu_id': self.env.ref('hr.menu_hr_root').id,
                })
            employee._message_log(body=_(
                '<b>Congratulations!</b> May I recommend you to setup an <a href="%s">onboarding plan?</a>'
            ) % (url))
        return employee

    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']
        if vals.get('user_id'):
            # Update the profile pictures with user, except if provided
            vals.update(
                self._sync_user(self.env['res.users'].browse(vals['user_id']),
                                (bool(self.image_1920))))
        if 'work_permit_expiration_date' in vals:
            vals['work_permit_scheduled_activity'] = False
        res = super(HrEmployeePrivate, self).write(vals)
        if vals.get('department_id') or vals.get('user_id'):
            department_id = vals['department_id'] if vals.get(
                'department_id') else self[:1].department_id.id
            # When added to a department or changing user, subscribe to the channels auto-subscribed by department
            self.env['mail.channel'].sudo().search([
                ('subscription_department_ids', 'in', department_id)
            ])._subscribe_users_automatically()
        return res

    def unlink(self):
        resources = self.mapped('resource_id')
        super(HrEmployeePrivate, self).unlink()
        return resources.unlink()

    def _get_employee_m2o_to_empty_on_archived_employees(self):
        return ['parent_id', 'coach_id']

    def _get_user_m2o_to_empty_on_archived_employees(self):
        return []

    def toggle_active(self):
        res = super(HrEmployeePrivate, self).toggle_active()
        unarchived_employees = self.filtered(lambda employee: employee.active)
        unarchived_employees.write({
            'departure_reason_id': False,
            'departure_description': False,
            'departure_date': False
        })
        archived_addresses = unarchived_employees.mapped(
            'address_home_id').filtered(lambda addr: not addr.active)
        archived_addresses.toggle_active()

        archived_employees = self.filtered(lambda e: not e.active)
        if archived_employees:
            # Empty links to this employees (example: manager, coach, time off responsible, ...)
            employee_fields_to_empty = self._get_employee_m2o_to_empty_on_archived_employees(
            )
            user_fields_to_empty = self._get_user_m2o_to_empty_on_archived_employees(
            )
            employee_domain = [[(field, 'in', archived_employees.ids)]
                               for field in employee_fields_to_empty]
            user_domain = [[(field, 'in', archived_employees.user_id.ids)
                            for field in user_fields_to_empty]]
            employees = self.env['hr.employee'].search(
                expression.OR(employee_domain + user_domain))
            for employee in employees:
                for field in employee_fields_to_empty:
                    if employee[field] in archived_employees:
                        employee[field] = False
                for field in user_fields_to_empty:
                    if employee[field] in archived_employees.user_id:
                        employee[field] = False

            # Launch automatic offboarding plans
            archived_employees._launch_plans_from_trigger(
                trigger='employee_archive')

        if len(self) == 1 and not self.active:
            return {
                'type': 'ir.actions.act_window',
                'name': _('Register Departure'),
                'res_model': 'hr.departure.wizard',
                'view_mode': 'form',
                'target': 'new',
                'context': {
                    'active_id': self.id
                },
                'views': [[False, 'form']]
            }
        return res

    @api.onchange('company_id')
    def _onchange_company_id(self):
        if self._origin:
            return {
                'warning': {
                    'title':
                    _("Warning"),
                    'message':
                    _("To avoid multi company issues (loosing the access to your previous contracts, leaves, ...), you should create another employee in the new company instead."
                      )
                }
            }

    def generate_random_barcode(self):
        for employee in self:
            employee.barcode = '041' + "".join(
                choice(digits) for i in range(9))

    @api.depends('address_home_id.parent_id')
    def _compute_is_address_home_a_company(self):
        """Checks that chosen address (res.partner) is not linked to a company.
        """
        for employee in self:
            try:
                employee.is_address_home_a_company = employee.address_home_id.parent_id.id is not False
            except AccessError:
                employee.is_address_home_a_company = False

    def _get_tz(self):
        # Finds the first valid timezone in his tz, his work hours tz,
        #  the company calendar tz or UTC and returns it as a string
        self.ensure_one()
        return self.tz or\
               self.resource_calendar_id.tz or\
               self.company_id.resource_calendar_id.tz or\
               'UTC'

    def _get_tz_batch(self):
        # Finds the first valid timezone in his tz, his work hours tz,
        #  the company calendar tz or UTC
        # Returns a dict {employee_id: tz}
        return {emp.id: emp._get_tz() for emp in self}

    # ---------------------------------------------------------
    # Business Methods
    # ---------------------------------------------------------

    @api.model
    def get_import_templates(self):
        return [{
            'label': _('Import Template for Employees'),
            'template': '/hr/static/xls/hr_employee.xls'
        }]

    def _post_author(self):
        """
        When a user updates his own employee's data, all operations are performed
        by super user. However, tracking messages should not be posted as OdooBot
        but as the actual user.
        This method is used in the overrides of `_message_log` and `message_post`
        to post messages as the correct user.
        """
        real_user = self.env.context.get('binary_field_real_user')
        if self.env.is_superuser() and real_user:
            self = self.with_user(real_user)
        return self

    def _launch_plans_from_trigger(self, trigger):
        '''
        Launches all plans for given trigger

        Returns False if no plans are launched, True otherwise
        '''
        plan_ids = self.env['hr.plan'].search([('trigger', '=', trigger)])
        if not plan_ids or not self:
            return False
        #Group plans and employees by company id
        plans_per_company = defaultdict(lambda: self.env['hr.plan'])
        for plan_id in plan_ids:
            plans_per_company[plan_id.company_id.id] |= plan_id
        employees_per_company = defaultdict(lambda: self.env['hr.employee'])
        for employee_id in self:
            employees_per_company[employee_id.company_id.id] |= employee_id
        #Launch the plans
        for company_id in employees_per_company:
            employees_per_company[company_id]._launch_plan(
                plans_per_company[company_id])
        return True

    def _launch_plan(self, plan_ids):
        '''
        Launch all given plans
        '''
        for employee_id in self:
            for plan_id in plan_ids:
                employee_id._message_log(body=_('Plan %s has been launched.',
                                                plan_id.name), )
                errors = []
                for activity_type in plan_id.plan_activity_type_ids:
                    responsible = False
                    try:
                        responsible = activity_type.get_responsible_id(
                            employee_id)
                    except UserError as error:
                        errors.append(
                            _(
                                'Warning ! The step "%(name)s: %(summary)s" assigned to %(responsible)s '
                                'could not be started because: "%(error)s"',
                                name=activity_type.activity_type_id.name,
                                summary=activity_type.summary,
                                responsible=activity_type.responsible,
                                error=str(error)))
                        continue

                    if self.env['hr.employee'].with_user(
                            responsible).check_access_rights(
                                'read', raise_exception=False):
                        if activity_type.deadline_type == 'default':
                            date_deadline = self.env[
                                'mail.activity']._calculate_date_deadline(
                                    activity_type.activity_type_id)
                        elif activity_type.deadline_type == 'plan_active':
                            date_deadline = fields.Date.context_today(self)
                        elif activity_type.deadline_type == 'trigger_offset':
                            date_deadline = fields.Date.add(
                                fields.Date.context_today(self),
                                days=activity_type.deadline_days)

                        employee_id.activity_schedule(
                            activity_type_id=activity_type.activity_type_id.id,
                            summary=activity_type.summary,
                            note=activity_type.note,
                            user_id=responsible.id,
                            date_deadline=date_deadline,
                        )
                employee_id._message_log(body='<br/>'.join(errors), )

    def _get_unusual_days(self, date_from, date_to=None):
        # Checking the calendar directly allows to not grey out the leaves taken
        # by the employee
        self.ensure_one()
        calendar = self.resource_calendar_id
        if not calendar:
            return {}
        dfrom = datetime.combine(fields.Date.from_string(date_from),
                                 time.min).replace(tzinfo=pytz.UTC)
        dto = datetime.combine(fields.Date.from_string(date_to),
                               time.max).replace(tzinfo=pytz.UTC)

        works = {
            d[0].date()
            for d in calendar._work_intervals_batch(dfrom, dto)[False]
        }
        return {
            fields.Date.to_string(day.date()): (day.date() not in works)
            for day in rrule(DAILY, dfrom, until=dto)
        }

    # ---------------------------------------------------------
    # Messaging
    # ---------------------------------------------------------

    def _message_log(self, **kwargs):
        return super(HrEmployeePrivate,
                     self._post_author())._message_log(**kwargs)

    @api.returns('mail.message', lambda value: value.id)
    def message_post(self, **kwargs):
        return super(HrEmployeePrivate,
                     self._post_author()).message_post(**kwargs)

    def _sms_get_partner_fields(self):
        return ['user_partner_id']

    def _sms_get_number_fields(self):
        return ['mobile_phone']
Example #27
0
class PurchaseOrder(models.Model):
    _inherit = 'purchase.order'

    name = fields.Char('Order Reference', required=True, index=True, copy=False, default='Nueva orden')

    @api.multi
    def action_rfq_send(self):
        """
        Le cambiamos el estado a SDP enviada
        :return:
        """
        res = super(PurchaseOrder, self).action_rfq_send()
        self.write({
            'state': 'sent'
        })
        return res

    @api.multi
    def to_approve(self):
        """
        Solicitar aprobación de orden de compra
        """
        self.update({'state': 'to approve'})
        # Enviar correo a usuarios para aprobación
        self.env['eliterp.managerial.helps'].send_mail(self.id, self._name, 'eliterp_approve_purchase_order_mail')

    @api.multi
    def button_confirm(self):
        """
        Agregamos confirmación de OC
        :return: object
        """
        res = super(PurchaseOrder, self).button_confirm()
        self.write({
            'state': 'purchase',
            'approval_user': self._uid
        })
        return res

    @api.multi
    def approve(self):
        """
        Aprobar orden de compra
        """
        self.update({
            'state': 'approve',
        })

    reference = fields.Char('Referencia', track_visibility='onchange')
    attach_order = fields.Binary('Adjuntar documento', attachment=True, track_visibility='onchange')
    # CM
    invoice_status = fields.Selection([
        ('no', 'Pendiente'),
        ('to invoice', 'Para facturar'),
        ('invoiced', 'Facturado'),
    ], string='Estado de facturación', compute='_get_invoiced', store=True, readonly=True, copy=False, default='no', track_visibility='onchange')
    approval_user = fields.Many2one('res.users', 'Aprobado por')
    state = fields.Selection([
        ('draft', 'SDP Borrador'),
        ('sent', 'SDP Enviada'),
        ('approve', 'Orden de compra'),
        ('to approve', 'OCS por aprobar'),
        ('purchase', 'OCS Aprobado'),
        ('done', 'Bloqueado'),
        ('cancel', 'Negado')
    ], string='Status', readonly=True, index=True, copy=False, default='draft', track_visibility='onchange')

    # Campos modifcada la trazabilidad
    READONLY_STATES = {
        'purchase': [('readonly', True)],
        'done': [('readonly', True)],
        'cancel': [('readonly', True)],
    }

    partner_id = fields.Many2one('res.partner', string='Vendor', required=True, states=READONLY_STATES, change_default=True, track_visibility='onchange')
    amount_untaxed = fields.Monetary(string='Untaxed Amount', store=True, readonly=True, compute='_amount_all', track_visibility='onchange')
Example #28
0
class ProductProduct(models.Model):
    _name = "product.product"
    _description = "Product"
    _inherits = {'product.template': 'product_tmpl_id'}
    _inherit = ['mail.thread']
    _order = 'default_code, name, id'

    price = fields.Float('Price',
                         compute='_compute_product_price',
                         digits=dp.get_precision('Product Price'),
                         inverse='_set_product_price')
    price_extra = fields.Float(
        'Variant Price Extra',
        compute='_compute_product_price_extra',
        digits=dp.get_precision('Product Price'),
        help="This is the sum of the extra price of all attributes")
    lst_price = fields.Float(
        'Sale Price',
        compute='_compute_product_lst_price',
        digits=dp.get_precision('Product Price'),
        inverse='_set_product_lst_price',
        help=
        "The sale price is managed from the product template. Click on the 'Variant Prices' button to set the extra attribute prices."
    )

    default_code = fields.Char('Internal Reference', index=True)
    code = fields.Char('Internal Reference', compute='_compute_product_code')
    partner_ref = fields.Char('Customer Ref', compute='_compute_partner_ref')

    active = fields.Boolean(
        'Active',
        default=True,
        help=
        "If unchecked, it will allow you to hide the product without removing it."
    )
    product_tmpl_id = fields.Many2one('product.template',
                                      'Product Template',
                                      auto_join=True,
                                      index=True,
                                      ondelete="cascade",
                                      required=True)
    barcode = fields.Char(
        'Barcode',
        copy=False,
        oldname='ean13',
        help="International Article Number used for product identification.")
    attribute_value_ids = fields.Many2many('product.attribute.value',
                                           string='Attributes',
                                           ondelete='restrict')
    # image: all image fields are base64 encoded and PIL-supported
    image_variant = fields.Binary(
        "Variant Image",
        attachment=True,
        help=
        "This field holds the image used as image for the product variant, limited to 1024x1024px."
    )
    image = fields.Binary(
        "Big-sized image",
        compute='_compute_images',
        inverse='_set_image',
        help=
        "Image of the product variant (Big-sized image of product template if false). It is automatically "
        "resized as a 1024x1024px image, with aspect ratio preserved.")
    image_small = fields.Binary(
        "Small-sized image",
        compute='_compute_images',
        inverse='_set_image_small',
        help=
        "Image of the product variant (Small-sized image of product template if false)."
    )
    image_medium = fields.Binary(
        "Medium-sized image",
        compute='_compute_images',
        inverse='_set_image_medium',
        help=
        "Image of the product variant (Medium-sized image of product template if false)."
    )

    standard_price = fields.Float(
        'Cost',
        company_dependent=True,
        digits=dp.get_precision('Product Price'),
        groups="base.group_user",
        help=
        "Cost of the product template used for standard stock valuation in accounting and used as a base price on purchase orders. "
        "Expressed in the default unit of measure of the product.")
    volume = fields.Float('Volume', help="The volume in m3.")
    weight = fields.Float(
        'Weight',
        digits=dp.get_precision('Stock Weight'),
        help=
        "The weight of the contents in Kg, not including any packaging, etc.")

    pricelist_item_ids = fields.Many2many('product.pricelist.item',
                                          'Pricelist Items',
                                          compute='_get_pricelist_items')

    _sql_constraints = [
        ('barcode_uniq', 'unique(barcode)',
         _("A barcode can only be assigned to one product !")),
    ]

    def _compute_product_price(self):
        prices = {}
        pricelist_id_or_name = self._context.get('pricelist')
        if pricelist_id_or_name:
            pricelist = None
            partner = self._context.get('partner', False)
            quantity = self._context.get('quantity', 1.0)

            # Support context pricelists specified as display_name or ID for compatibility
            if isinstance(pricelist_id_or_name, basestring):
                pricelist_name_search = self.env[
                    'product.pricelist'].name_search(pricelist_id_or_name,
                                                     operator='=',
                                                     limit=1)
                if pricelist_name_search:
                    pricelist = self.env['product.pricelist'].browse(
                        [pricelist_name_search[0][0]])
            elif isinstance(pricelist_id_or_name, (int, long)):
                pricelist = self.env['product.pricelist'].browse(
                    pricelist_id_or_name)

            if pricelist:
                quantities = [quantity] * len(self)
                partners = [partner] * len(self)
                prices = pricelist.get_products_price(self, quantities,
                                                      partners)

        for product in self:
            product.price = prices.get(product.id, 0.0)

    def _set_product_price(self):
        for product in self:
            if self._context.get('uom'):
                value = self.env['product.uom'].browse(
                    self._context['uom'])._compute_price(
                        product.price, product.uom_id)
            else:
                value = product.price
            value -= product.price_extra
            product.write({'list_price': value})

    def _set_product_lst_price(self):
        for product in self:
            if self._context.get('uom'):
                value = self.env['product.uom'].browse(
                    self._context['uom'])._compute_price(
                        product.lst_price, product.uom_id)
            else:
                value = product.lst_price
            value -= product.price_extra
            product.write({'list_price': value})

    @api.depends('attribute_value_ids.price_ids.price_extra',
                 'attribute_value_ids.price_ids.product_tmpl_id')
    def _compute_product_price_extra(self):
        # TDE FIXME: do a real multi and optimize a bit ?
        for product in self:
            price_extra = 0.0
            for attribute_price in product.mapped(
                    'attribute_value_ids.price_ids'):
                if attribute_price.product_tmpl_id == product.product_tmpl_id:
                    price_extra += attribute_price.price_extra
            product.price_extra = price_extra

    @api.depends('list_price', 'price_extra')
    def _compute_product_lst_price(self):
        to_uom = None
        if 'uom' in self._context:
            to_uom = self.env['product.uom'].browse([self._context['uom']])

        for product in self:
            if to_uom:
                list_price = product.uom_id._compute_price(
                    product.list_price, to_uom)
            else:
                list_price = product.list_price
            product.lst_price = list_price + product.price_extra

    @api.one
    def _compute_product_code(self):
        for supplier_info in self.seller_ids:
            if supplier_info.name.id == self._context.get('partner_id'):
                self.code = supplier_info.product_code or self.default_code
                break
        else:
            self.code = self.default_code

    @api.one
    def _compute_partner_ref(self):
        for supplier_info in self.seller_ids:
            if supplier_info.name.id == self._context.get('partner_id'):
                product_name = supplier_info.product_name or self.default_code
                break
        else:
            product_name = self.name
        self.partner_ref = '%s%s' % (self.code and '[%s] ' % self.code
                                     or '', product_name)

    @api.one
    @api.depends('image_variant', 'product_tmpl_id.image')
    def _compute_images(self):
        if self._context.get('bin_size'):
            self.image_medium = self.image_variant
            self.image_small = self.image_variant
            self.image = self.image_variant
        else:
            resized_images = tools.image_get_resized_images(
                self.image_variant, return_big=True, avoid_resize_medium=True)
            self.image_medium = resized_images['image_medium']
            self.image_small = resized_images['image_small']
            self.image = resized_images['image']
        if not self.image_medium:
            self.image_medium = self.product_tmpl_id.image_medium
        if not self.image_small:
            self.image_small = self.product_tmpl_id.image_small
        if not self.image:
            self.image = self.product_tmpl_id.image

    @api.one
    def _set_image(self):
        self._set_image_value(self.image)

    @api.one
    def _set_image_medium(self):
        self._set_image_value(self.image_medium)

    @api.one
    def _set_image_small(self):
        self._set_image_value(self.image_small)

    @api.one
    def _set_image_value(self, value):
        image = tools.image_resize_image_big(value)
        if self.product_tmpl_id.image:
            self.image_variant = image
        else:
            self.product_tmpl_id.image = image

    @api.one
    def _get_pricelist_items(self):
        self.pricelist_item_ids = self.env['product.pricelist.item'].search([
            '|', ('product_id', '=', self.id),
            ('product_tmpl_id', '=', self.product_tmpl_id.id)
        ]).ids

    @api.constrains('attribute_value_ids')
    def _check_attribute_value_ids(self):
        for product in self:
            attributes = self.env['product.attribute']
            for value in product.attribute_value_ids:
                if value.attribute_id in attributes:
                    raise ValidationError(
                        _('Error! It is not allowed to choose more than one value for a given attribute.'
                          ))
                if value.attribute_id.create_variant:
                    attributes |= value.attribute_id
        return True

    @api.onchange('uom_id', 'uom_po_id')
    def _onchange_uom(self):
        if self.uom_id and self.uom_po_id and self.uom_id.category_id != self.uom_po_id.category_id:
            self.uom_po_id = self.uom_id

    @api.model
    def create(self, vals):
        product = super(
            ProductProduct,
            self.with_context(create_product_product=True)).create(vals)
        # When a unique variant is created from tmpl then the standard price is set by _set_standard_price
        if not (self.env.context.get('create_from_tmpl')
                and len(product.product_tmpl_id.product_variant_ids) == 1):
            product._set_standard_price(vals.get('standard_price') or 0.0)
        return product

    @api.multi
    def write(self, values):
        ''' Store the standard price change in order to be able to retrieve the cost of a product for a given date'''
        res = super(ProductProduct, self).write(values)
        if 'standard_price' in values:
            self._set_standard_price(values['standard_price'])
        return res

    @api.multi
    def unlink(self):
        unlink_products = self.env['product.product']
        unlink_templates = self.env['product.template']
        for product in self:
            # Check if product still exists, in case it has been unlinked by unlinking its template
            if not product.exists():
                continue
            # Check if the product is last product of this template
            other_products = self.search([('product_tmpl_id', '=',
                                           product.product_tmpl_id.id),
                                          ('id', '!=', product.id)])
            if not other_products:
                unlink_templates |= product.product_tmpl_id
            unlink_products |= product
        res = super(ProductProduct, unlink_products).unlink()
        # delete templates after calling super, as deleting template could lead to deleting
        # products due to ondelete='cascade'
        unlink_templates.unlink()
        return res

    @api.multi
    def copy(self, default=None):
        # TDE FIXME: clean context / variant brol
        if default is None:
            default = {}
        if self._context.get('variant'):
            # if we copy a variant or create one, we keep the same template
            default['product_tmpl_id'] = self.product_tmpl_id.id
        elif 'name' not in default:
            default['name'] = self.name

        return super(ProductProduct, self).copy(default=default)

    @api.model
    def search(self, args, offset=0, limit=None, order=None, count=False):
        # TDE FIXME: strange
        if self._context.get('search_default_categ_id'):
            args.append((('categ_id', 'child_of',
                          self._context['search_default_categ_id'])))
        return super(ProductProduct, self).search(args,
                                                  offset=offset,
                                                  limit=limit,
                                                  order=order,
                                                  count=count)

    @api.multi
    def name_get(self):
        # TDE: this could be cleaned a bit I think

        def _name_get(d):
            name = d.get('name', '')
            code = self._context.get('display_default_code', True) and d.get(
                'default_code', False) or False
            if code:
                name = '[%s] %s' % (code, name)
            return (d['id'], name)

        partner_id = self._context.get('partner_id')
        if partner_id:
            partner_ids = [
                partner_id, self.env['res.partner'].browse(
                    partner_id).commercial_partner_id.id
            ]
        else:
            partner_ids = []

        # all user don't have access to seller and partner
        # check access and use superuser
        self.check_access_rights("read")
        self.check_access_rule("read")

        result = []
        for product in self.sudo():
            # display only the attributes with multiple possible values on the template
            variable_attributes = product.attribute_line_ids.filtered(
                lambda l: len(l.value_ids) > 1).mapped('attribute_id')
            variant = product.attribute_value_ids._variant_name(
                variable_attributes)

            name = variant and "%s (%s)" % (product.name,
                                            variant) or product.name
            sellers = []
            if partner_ids:
                sellers = [
                    x for x in product.seller_ids
                    if (x.name.id in partner_ids) and (x.product_id == product)
                ]
                if not sellers:
                    sellers = [
                        x for x in product.seller_ids
                        if (x.name.id in partner_ids) and not x.product_id
                    ]
            if sellers:
                for s in sellers:
                    seller_variant = s.product_name and (
                        variant and "%s (%s)" %
                        (s.product_name, variant) or s.product_name) or False
                    mydict = {
                        'id': product.id,
                        'name': seller_variant or name,
                        'default_code': s.product_code or product.default_code,
                    }
                    temp = _name_get(mydict)
                    if temp not in result:
                        result.append(temp)
            else:
                mydict = {
                    'id': product.id,
                    'name': name,
                    'default_code': product.default_code,
                }
                result.append(_name_get(mydict))
        return result

    @api.model
    def name_search(self, name='', args=None, operator='ilike', limit=100):
        if not args:
            args = []
        if name:
            positive_operators = ['=', 'ilike', '=ilike', 'like', '=like']
            products = self.env['product.product']
            if operator in positive_operators:
                products = self.search([('default_code', '=', name)] + args,
                                       limit=limit)
                if not products:
                    products = self.search([('barcode', '=', name)] + args,
                                           limit=limit)
            if not products and operator not in expression.NEGATIVE_TERM_OPERATORS:
                # Do not merge the 2 next lines into one single search, SQL search performance would be abysmal
                # on a database with thousands of matching products, due to the huge merge+unique needed for the
                # OR operator (and given the fact that the 'name' lookup results come from the ir.translation table
                # Performing a quick memory merge of ids in Python will give much better performance
                products = self.search(args +
                                       [('default_code', operator, name)],
                                       limit=limit)
                if not limit or len(products) < limit:
                    # we may underrun the limit because of dupes in the results, that's fine
                    limit2 = (limit - len(products)) if limit else False
                    products += self.search(args +
                                            [('name', operator, name),
                                             ('id', 'not in', products.ids)],
                                            limit=limit2)
            elif not products and operator in expression.NEGATIVE_TERM_OPERATORS:
                domain = expression.OR([
                    [
                        '&', ('default_code', operator, name),
                        ('name', operator, name)
                    ],
                    [
                        '&', ('default_code', '=', False),
                        ('name', operator, name)
                    ],
                ])
                domain = expression.AND([args, domain])
                products = self.search(domain, limit=limit)
            if not products and operator in positive_operators:
                ptrn = re.compile('(\[(.*?)\])')
                res = ptrn.search(name)
                if res:
                    products = self.search(
                        [('default_code', '=', res.group(2))] + args,
                        limit=limit)
            # still no results, partner in context: search on supplier info as last hope to find something
            if not products and self._context.get('partner_id'):
                suppliers = self.env['product.supplierinfo'].search([
                    ('name', '=', self._context.get('partner_id')), '|',
                    ('product_code', operator, name),
                    ('product_name', operator, name)
                ])
                if suppliers:
                    products = self.search(
                        [('product_tmpl_id.seller_ids', 'in', suppliers.ids)],
                        limit=limit)
        else:
            products = self.search(args, limit=limit)
        return products.name_get()

    @api.model
    def view_header_get(self, view_id, view_type):
        res = super(ProductProduct, self).view_header_get(view_id, view_type)
        if self._context.get('categ_id'):
            return _('Products: ') + self.env['product.category'].browse(
                self._context['categ_id']).name
        return res

    @api.multi
    def open_product_template(self):
        """ Utility method used to add an "Open Template" button in product views """
        self.ensure_one()
        return {
            'type': 'ir.actions.act_window',
            'res_model': 'product.template',
            'view_mode': 'form',
            'res_id': self.product_tmpl_id.id,
            'target': 'new'
        }

    @api.multi
    def _select_seller(self,
                       partner_id=False,
                       quantity=0.0,
                       date=None,
                       uom_id=False):
        self.ensure_one()
        if date is None:
            date = fields.Date.today()
        res = self.env['product.supplierinfo']
        for seller in self.seller_ids:
            # Set quantity in UoM of seller
            quantity_uom_seller = quantity
            if quantity_uom_seller and uom_id and uom_id != seller.product_uom:
                quantity_uom_seller = uom_id._compute_quantity(
                    quantity_uom_seller, seller.product_uom)

            if seller.date_start and seller.date_start > date:
                continue
            if seller.date_end and seller.date_end < date:
                continue
            if partner_id and seller.name not in [
                    partner_id, partner_id.parent_id
            ]:
                continue
            if quantity_uom_seller < seller.min_qty:
                continue
            if seller.product_id and seller.product_id != self:
                continue

            res |= seller
            break
        return res

    @api.multi
    def price_compute(self,
                      price_type,
                      uom=False,
                      currency=False,
                      company=False):
        # TDE FIXME: delegate to template or not ? fields are reencoded here ...
        # compatibility about context keys used a bit everywhere in the code
        if not uom and self._context.get('uom'):
            uom = self.env['product.uom'].browse(self._context['uom'])
        if not currency and self._context.get('currency'):
            currency = self.env['res.currency'].browse(
                self._context['currency'])

        products = self
        if price_type == 'standard_price':
            # standard_price field can only be seen by users in base.group_user
            # Thus, in order to compute the sale price from the cost for users not in this group
            # We fetch the standard price as the superuser
            products = self.with_context(
                force_company=company and company.id or self._context.get(
                    'force_company', self.env.user.company_id.id)).sudo()

        prices = dict.fromkeys(self.ids, 0.0)
        for product in products:
            prices[product.id] = product[price_type] or 0.0
            if price_type == 'list_price':
                prices[product.id] += product.price_extra

            if uom:
                prices[product.id] = product.uom_id._compute_price(
                    prices[product.id], uom)

            # Convert from current user company currency to asked one
            # This is right cause a field cannot be in more than one currency
            if currency:
                prices[product.id] = product.currency_id.compute(
                    prices[product.id], currency)

        return prices

    # compatibility to remove after v10 - DEPRECATED
    @api.multi
    def price_get(self, ptype='list_price'):
        return self.price_compute(ptype)

    @api.multi
    def _set_standard_price(self, value):
        ''' Store the standard price change in order to be able to retrieve the cost of a product for a given date'''
        PriceHistory = self.env['product.price.history']
        for product in self:
            PriceHistory.create({
                'product_id':
                product.id,
                'cost':
                value,
                'company_id':
                self._context.get('force_company',
                                  self.env.user.company_id.id),
            })

    @api.multi
    def get_history_price(self, company_id, date=None):
        history = self.env['product.price.history'].search(
            [('company_id', '=', company_id), ('product_id', 'in', self.ids),
             ('datetime', '<=', date or fields.Datetime.now())],
            order='datetime desc,id desc',
            limit=1)
        return history.cost or 0.0

    def _need_procurement(self):
        # When sale/product is installed alone, there is no need to create procurements. Only
        # sale_stock and sale_service need procurements
        return False
class MuskathlonRegistration(models.Model):
    _name = 'muskathlon.registration'
    _inherit = ['website.published.mixin']
    _description = 'Muskathlon registration'
    _rec_name = 'partner_preferred_name'
    _order = 'id desc'

    event_id = fields.Many2one(
        'crm.event.compassion', 'Muskathlon event', required=True,
        domain="[('type', '=', 'sport')]"
    )
    partner_id = fields.Many2one(
        'res.partner', 'Muskathlon participant'
    )
    lead_id = fields.Many2one(
        'crm.lead', 'Lead'
    )
    ambassador_details_id = fields.Many2one(
        'ambassador.details', related='partner_id.ambassador_details_id')

    # The 4 following fields avoid giving read access to the public on the
    # res.partner participating in the muskathlon.
    partner_id_id = fields.Integer(related="partner_id.id", readonly=True)
    partner_display_name = fields.Char(related="partner_id.display_name",
                                       readonly=True)
    partner_preferred_name = fields.Char(related="partner_id.preferred_name",
                                         readonly=True)
    partner_name = fields.Char(related="partner_id.name", readonly=True)
    ambassador_picture_1 = fields.Binary(
        related='partner_id.image', readonly=True)
    ambassador_picture_2 = fields.Binary(
        related='ambassador_details_id.picture_large', readonly=True)
    ambassador_description = fields.Text(
        related='ambassador_details_id.description', readonly=True)
    ambassador_quote = fields.Text(
        related='ambassador_details_id.quote', readonly=True)
    partner_firstname = fields.Char(
        related='partner_id.firstname', readonly=True
    )
    partner_lastname = fields.Char(
        related='partner_id.lastname', readonly=True
    )
    partner_gender = fields.Selection(related='partner_id.title.gender',
                                      readonly=True)

    sport_discipline_id = fields.Many2one(
        'sport.discipline', 'Sport discipline', required=True
    )
    sport_level = fields.Selection([
        ('beginner', 'Beginner'),
        ('average', 'Average'),
        ('advanced', 'Advanced')
    ])
    sport_level_description = fields.Text('Describe your sport experience')
    amount_objective = fields.Integer('Raise objective', default=10000,
                                      required=True)
    amount_raised = fields.Integer(readonly=True,
                                   compute='_compute_amount_raised')
    amount_raised_percents = fields.Integer(readonly=True,
                                            compute='_compute_amount_raised')

    muskathlon_participant_id = fields.Char(
        related='partner_id.muskathlon_participant_id')

    muskathlon_event_id = fields.Char(
        related='event_id.muskathlon_event_id')

    website_published = fields.Boolean(
        compute='_compute_website_published', store=True)
    website_url = fields.Char(
        compute='_compute_website_url'
    )

    reg_id = fields.Char(string='Muskathlon registration ID', size=128)
    host = fields.Char(compute='_compute_host', readonly=True)

    _sql_constraints = [
        ('reg_unique', 'unique(event_id,partner_id)',
         'Only one registration per participant/event is allowed!')
    ]

    @api.multi
    def _compute_website_url(self):
        for registration in self:
            registration.website_url = "/event/{}/{}".format(
                slug(registration.event_id), slug(registration)
            )

    def _compute_amount_raised(self):
        muskathlon_report = self.env['muskathlon.report']

        for registration in self:
            amount_raised = int(sum(
                item.amount for item in muskathlon_report.search([
                    ('user_id', '=', registration.partner_id.id)
                ])
            ))

            registration.amount_raised = amount_raised
            registration.amount_raised_percents = int(
                amount_raised * 100 / (registration.amount_objective or 10000))

    def _compute_host(self):
        host = config.get('wordpress_host')
        if not host:
            raise MissingError(_('Missing wordpress_host in odoo config file'))
        for registration in self:
            registration.host = host

    @api.multi
    @api.depends(
        'partner_id', 'partner_id.image', 'ambassador_details_id',
        'ambassador_details_id.quote', 'ambassador_details_id.description')
    def _compute_website_published(self):
        required_fields = [
            'partner_preferred_name', 'ambassador_quote',
            'ambassador_description', 'ambassador_picture_1',
        ]
        for registration in self:
            published = True
            for field in required_fields:
                if not getattr(registration, field):
                    published = False
                    break
            registration.website_published = published

    def get_sport_discipline_name(self):
        return self.sport_discipline_id.get_label()

    @api.onchange('event_id')
    def onchange_event_id(self):
        return {
            'domain': {'sport_discipline_id': [
                ('id', 'in', self.event_id.sport_discipline_ids.ids)]}
        }

    @api.onchange('sport_discipline_id')
    def onchange_sport_discipline(self):
        if self.sport_discipline_id and self.sport_discipline_id not in \
                self.event_id.sport_discipline_ids:
            self.sport_discipline_id = False
            return {
                'warning': {
                    'title': _('Invalid sport'),
                    'message': _('This sport is not in muskathlon')
                }
            }
Example #30
0
class ResCompany(models.Model):
    _inherit = "res.company"

    signature = fields.Binary('Signature')
    accreditation = fields.Text('Accreditation')
    approval_authority = fields.Text('Approval Authority')