product.py 17.2 KB
import base64
import io
import os
from random import randrange

from odoo import models, fields, api, tools, _
from odoo.exceptions import UserError, ValidationError
from odoo.modules import get_resource_path

from PIL import Image

import logging

_logger = logging.getLogger(__name__)

TYPE_LIST = [('hotel', 'Hotel'),
             ('resto', 'Restaurant'),
             ('hibur', 'Hiburan'),
             ('ppj', 'Penerangan Jalan'),
             ('golc', 'Mineral dan Batuan'),
             ('reklame', 'Reklame'),
             ('walet', 'Sarang Burung Walet'),
             ('abt', 'Air Bawah Tanah'),
             ('parkir', 'Parkir'),
             ]


class Product(models.Model):
    _inherit = "product.template"
    name = fields.Char(related='partner_id.name', string='Company Name',
                       required=True, store=True, readonly=False)
    type = fields.Selection(
        selection_add=TYPE_LIST, string='Product Type',
        help='Pilih type objek pajak', default='hotel', required=True,
        ondelete={
            'hotel': 'set default',
            'resto': 'set default',
            'hibur': 'set default',
            'ppj': 'set default',
            'golc': 'set default',
            'reklame': 'set default',
            'walet': 'set default',
            'abt': 'set default',
            'parkir': 'set default'
        })
    partner_id = fields.Many2one('res.partner', string="Wajib Pajak")

    district_id = fields.Many2one(
        'res.district', compute='_compute_address', inverse='_inverse_district',
        string="Kab/Kota", domain="[('state_id', '=?', state_id)]"
    )

    sub_district_id = fields.Many2one(
        'res.district.sub', compute='_compute_address', inverse='_inverse_sub_district',
        string="Kecamatan", domain="[('district_id', '=?', district_id)]"
    )
    village_id = fields.Many2one(
        'res.district.village', compute='_compute_address', inverse='_inverse_village',
        string="Desa/Kelurahan", domain="[('sub_district_id', '=?', sub_district_id)]"
    )
    company_id = fields.Many2one('res.company', related='partner_id.company_id',
                                 string='Lembaga/Organisasi', required=True,
                                 store=True, readonly=False,
                                 default=lambda self: self.env.company.id
                                 if not self.company_id else False
                                 )

    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())

    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 add a colored bar on the bottom
            # 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)
            height = original.size[1]
            width = original.size[0]
            bar_size = 1
            for y in range(height):
                for x in range(width):
                    pixel = original.getpixel((x, y))
                    if height - bar_size <= y + 1 <= height:
                        new_image.putpixel((x, y), (color[0], color[1], color[2], 255))
                    else:
                        new_image.putpixel((x, y), (pixel[0], pixel[1], pixel[2], pixel[3]))
            stream = io.BytesIO()
            new_image.save(stream, format="ICO")
            return base64.b64encode(stream.getvalue())

    @tools.ormcache()
    def _get_default_category_id(self):
        # Deletion forbidden (at least through unlink)
        return self.env.ref('product.product_category_all')

    logo = fields.Binary(related='partner_id.image_1920', 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.product_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._default_currency_id())
    # user_ids = fields.Many2many('res.users', 'res_product_users_rel', 'cid', 'user_id', string='Accepted Users')
    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", domain="[('country_id', '=?', country_id)]"
    )
    country_id = fields.Many2one('res.country', compute='_compute_address', inverse='_inverse_country',
                                 string="Country")
    email = fields.Char(compute='_compute_address', inverse='_inverse_email')
    phone = fields.Char(compute='_compute_address', inverse='_inverse_phone')
    website = fields.Char(compute='_compute_address', inverse='_inverse_website')
    vat = fields.Char(related='partner_id.vat', string="Tax ID", readonly=False)
    categ_id = fields.Many2one(
        'product.category', 'Product Category',
        change_default=True, default=_get_default_category_id, group_expand='_read_group_categ_id',
        required=True, help="Select category for the current product",
        domain="[('type', '=?', type)]",
    )

    def _inverse_email(self):
        for product in self:
            product.partner_id.email = product.email

    def _inverse_phone(self):
        for product in self:
            product.partner_id.phone = product.phone

    def _inverse_website(self):
        for product in self:
            product.partner_id.website = product.website

    def _inverse_company(self):
        for product in self:
            product.partner_id.company_id = product.company_id

    def _inverse_district(self):
        for product in self:
            product.partner_id.district_id = product.district_id

    def _inverse_sub_district(self):
        for product in self:
            product.partner_id.sub_district_id = product.sub_district_id

    def _inverse_village(self):
        for product in self:
            product.partner_id.village_id = product.village_id

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

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

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

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

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

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

    def _get_product_address_field_names(self):
        """ Return a list of fields coming from the address partner to match
        on product address fields. Fields are labeled same on both models. """
        return ['street', 'street2', 'city', 'zip', 'state_id', 'country_id',
                'district_id', 'sub_district_id', 'village_id']

    @api.onchange('village_id')
    def _onchange_village_id(self):
        if self.village_id and self.village_id.sub_district_id != self.sub_district_id:
            self.sub_district_id = self.village_id.sub_district_id
        if self.village_id and self.village_id.zip and self.village_id.zip != self.zip:
            self.zip = self.village_id.zip

    @api.onchange('sub_district_id')
    def _onchange_sub_district_id(self):
        if self.sub_district_id and self.sub_district_id.district_id != self.district_id:
            self.district_id = self.sub_district_id.district_id
        if not self.sub_district_id or self.sub_district_id != self.village_id.sub_district_id:
            self.village_id = False

    @api.onchange('district_id')
    def _onchange_district_id(self):
        if self.district_id and self.district_id.state_id != self.state_id:
            self.state_id = self.district_id.state_id
        if not self.district_id or self.district_id != self.sub_district_id.district_id:
            self.sub_district_id = False

    @api.onchange('state_id')
    def _onchange_state_id(self):
        if self.state_id and self.state_id.country_id != self.country_id:
            self.country_id = self.state_id.country_id
        if not self.state_id or self.state_id != self.district_id.state_id:
            self.district_id = False

    @api.onchange('country_id')
    def _onchange_country_id(self):
        if not self.country_id or self.country_id != self.state_id.country_id:
            self.state_id = False

    def _get_product_address_update(self, partner):
        return dict((fname, partner[fname])
                    for fname in self._get_product_address_field_names())

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

    @api.depends('partner_id.image_1920')
    def _compute_logo_web(self):
        for product in self:
            product.logo_web = tools.image_process(product.partner_id.image_1920, size=(180, 0))

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

    # @api.onchange('country_id')
    # def _onchange_country_id(self):
    #     if self.country_id:
    #         self.currency_id = self.country_id.currency_id

    # @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.product_ids
    #         args = (args or []) + [('id', 'in', companies.ids)]
    #         newself = newself.sudo()
    #     return super(Product, 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 _product_default_get(self, object=False, field=False):
    #     """ Returns the user's product
    #         - Deprecated
    #     """
    #     _logger.warning("The method '_product_default_get' on res.product is deprecated and shouldn't be used
    #     anymore")
    #     return self.env.product

    # 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(Product, self).create(vals)
        partner = self.env['res.partner'].create({
            'name': vals['name'],
            'is_company': False,
            # 'image_1920': vals.get('logo'),
            # 'email': vals.get('email'),
            # 'phone': vals.get('phone'),
            # 'website': vals.get('website'),
            # 'vat': vals.get('vat'),
            # 'country_id': vals.get('country_id'),
        })
        # compute stored fields, for example address dependent fields
        partner.flush()
        vals['partner_id'] = partner.id
        self.clear_caches()
        product = super(Product, self).create(vals)
        # The write is made on the user to set it automatically in the multi product group.
        # self.env.user.write({'product_ids': [(4, product.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 product

    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})
        #
        if not self.partner_id:
            if not values.get('name'):
                values['name'] = self.name
            _logger.info("=================")
            _logger.info(values)
            partner = self.env['res.partner'].create({
                'name': values['name'],
                'is_company': False,
                # 'image_1920': vals.get('logo'),
                # 'email': vals.get('email'),
                # 'phone': vals.get('phone'),
                # 'website': vals.get('website'),
                # 'vat': vals.get('vat'),
                # 'country_id': vals.get('country_id'),
            })
            # compute stored fields, for example address dependent fields
            partner.flush()
            values['partner_id'] = partner.id

        res = super(Product, self).write(values)

        # invalidate product cache to recompute address based on updated partner
        product_address_fields = self._get_product_address_field_names()
        product_address_fields_upd = set(product_address_fields) & set(values.keys())
        if product_address_fields_upd:
            self.invalidate_cache(fnames=product_address_fields)
        return res


class ProductCategory(models.Model):
    _inherit = "product.category"
    type = fields.Selection(
        selection=TYPE_LIST, default="hotel", string='Product Type',
        help='Pilih type objek pajak', required=True,
        company_id=fields.Many2one(
            'res.company', string="Lembaga Organisasi",
            default=lambda self: self.env.company.id
            if not self.company_id else False)
    )

    rate = fields.Float(string="Tarif")
    rate_date = fields.Date(string="Tgl Perubahan")
    min_omzet = fields.Float(string="NJOP/Min Omset")
    report_type = fields.Selection([('insidentil', 'Insidentil'),
                                    ('bulan', 'Bulanan'),
                                    ('triwulan', 'Triwulanan'),
                                    ('tahun', 'Tahunan'),
                                    ], string="Jns Pelaporan")
    is_self = fields.Boolean(string="Self Assesment")
    end_of_report = fields.Integer(string="Tanggal Akhir Lapor")
    end_of_pay = fields.Integer(string="Tanggal Akhir Setor")
    end_of_pay_type = fields.Selection([("calendar", "Hari Kalender"),
                                        ('work', 'Hari Kerja')],
                                       default="calendar",
                                       string="Jenis Jatuh Tempo")
    company_id = fields.Many2one('res.company', string="Lembaga/Organisasi")
    category_type = fields.Selection(
        string='Category Type',
        selection=[('self', 'Self Assessment'), ('official', 'Official Assessment')],
        compute='_compute_category_type', inverse='_write_category_type')

    @api.depends('is_self')
    def _compute_category_type(self):
        for categ in self:
            categ.category_type = 'self' if categ.is_self else 'official'

    def _write_category_type(self):
        for categ in self:
            categ.is_self = categ.category_type == 'self'

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