Exemplo n.º 1
0
class res_partner(models.Model):
    _inherit = 'res.partner'
    
    @api.model
    def name_search(self, name, args=None, operator='ilike', limit=100):
        """ name_search(name='', args=None, operator='ilike', limit=100) -> records

        Search for records that have a display name matching the given
        ``name`` pattern when compared with the given ``operator``, while also
        matching the optional search domain (``args``).

        This is used for example to provide suggestions based on a partial
        value for a relational field. Sometimes be seen as the inverse
        function of :meth:`~.name_get`, but it is not guaranteed to be.

        This method is equivalent to calling :meth:`~.search` with a search
        domain based on ``display_name`` and then :meth:`~.name_get` on the
        result of the search.
        
        Extends the original method!!!
        If eq_filter_prod_sup in the context is true, then only the suppliers
        which sell all of the products from the purchase order lines 
        (eq_order_line in context) are returned.

        :param str name: the name pattern to match
        :param list args: optional search domain (see :meth:`~.search` for
                          syntax), specifying further restrictions
        :param str operator: domain operator for matching ``name``, such as
                             ``'like'`` or ``'='``.
        :param int limit: optional max number of records to return
        :rtype: list
        :return: list of pairs ``(id, text_repr)`` for all matching records.
        """
        if self._context.get('eq_filter_prod_sup') and self._context.get('eq_order_line'):
            product_ids = []
            purchase_line_obj = self.env['purchase.order.line']
            for line_data in self._context.get('eq_order_line'):
                if line_data[0] == 6:
                    product_ids.append(line_data[2]['product_id'])
                elif line_data[0] == 4:
                    purchase_order_line = purchase_line_obj.browse(line_data[1])
                    product_ids.append(purchase_order_line.product_id.id)
                elif line_data[0] == 1:
                    if 'product_id' in line_data[2]:
                        product_ids.append(line_data[2]['product_id'])
                    else:
                        purchase_order_line = purchase_line_obj.browse(line_data[1])
                        product_ids.append(purchase_order_line.product_id.id)
            
            if product_ids:
                sql_query = """select product_tmpl_id, name from product_supplierinfo where product_tmpl_id in %s""" % (str(tuple(product_ids)))
                self._cr.execute(sql_query)
                supplierinfo = self._cr.fetchall()
                
                prod_sup_mapping = {}
                
                for product_id, sup_id in supplierinfo:
                    if sup_id in prod_sup_mapping:
                        prod_sup_mapping[sup_id].append(product_id)
                    else:
                        prod_sup_mapping[sup_id] = [product_id]
                        
                suppliers = []
                for supplier_id, prod_list in prod_sup_mapping.iteritems():
                    if set(product_ids) <= set(prod_list):
                        suppliers.append(supplier_id)
                    
                if args == None:
                    args = []
                args.append(['id', 'in', suppliers])
        res = super(res_partner, self).name_search(name, args=args, operator=operator, limit=limit)
        return res
    
    eq_delivery_date_type_purchase = fields.Selection([('cw', 'Calendar week'), ('date', 'Date')], string="Delivery Date Purchase", help="If nothing is selected, the default from the settings will be used.")
    eq_delivery_date_type_sale = fields.Selection([('cw', 'Calendar week'), ('date', 'Date')], string="Delivery Date Sale", help="If nothing is selected, the default from the settings will be used.")    
    eq_complete_description = fields.Char(compute='_generate_complete_description', store=True)    
    
    eq_prospective_customer = fields.Boolean(string="Prospective user",required=False, default=False)
    eq_unlocked_for_webshop = fields.Boolean(string="Unlocked for webshop",required=False, default=False)
    
    
    @api.one
    @api.depends('name', 'eq_firstname')
    def _generate_complete_description(self):
        for record in self:
            if record.is_company is False:
                result = ""
                if record.name is not False:
                    result = record.name
                    
                if record.eq_firstname is not False:
                    if len(result) > 0:
                        result += ", " + record.eq_firstname 
                    else:
                        result = record.eq_firstname

                record.eq_complete_description = result  
            else:
                record.eq_complete_description = record.name
Exemplo n.º 2
0
class ingram_config(models.Model):
    _name = "ingram_config"
    _description = "Configuration Management Produces Ingram"

    name = fields.Char(string="Name",
                       help="Name associated with the configuration",
                       required=True)
    xml_address = fields.Char(string='Server Xml address',
                              help="server Xml address")
    xml_login = fields.Char(string='Login', help="Login for Xml request ")
    xml_passwd = fields.Char(string='Password',
                             help="Password for Xml Request")
    xml_active = fields.Boolean(string='XMl Request',
                                help="Active the Xml Request")
    server_address = fields.Char(string='Server address',
                                 help="server ip address",
                                 required=True)
    file_cat = fields.Char(string='Products Categories file name',
                           default='PCAT_GENERIC.TXT',
                           help="Name of the file for the products categories",
                           required=True)
    file_prod = fields.Char(
        string='Products File name',
        default='Price2.txt',
        help=
        "Name of the file for the products. Must be based on this header: 'Ingram Part Number,Vendor Part Number,EANUPC Code,Plant,Vendor Number,Vendor Name,Weight,Category ID,Customer Price,Retail Price,Availability Flag,BOM Flag,Warranty Flag,Material Long Description,Material Creation Reason code,Material Language Code,Music Copyright Fees,Recycling Fees,Document Copyright Fees,Battery Fees,Availability (Local Stock),Availability (Central Stock),Creation Reason Type,Creation Reason Value,Local Stock Backlog Quantity,Local Stock Backlog ETA,Central Stock Backlog Quantity,Central Stock Backlog ETA'",
        required=True)
    server_login = fields.Char(string='Login', help="Login database")
    server_passwd = fields.Char(string='Password', help="Password database")
    date_synchro = fields.Datetime(
        string='Date of last manually synchronization', readonly=True)
    date_import = fields.Datetime(string='Date of last manually importation',
                                  readonly=True)
    date_cron = fields.Datetime(string='Date of last cronjob synchronization',
                                readonly=True)
    chemin = fields.Char(string="Path",
                         help="Path where the files is stored",
                         required=True)
    mailto = fields.Char(
        string="Warning Mail",
        help=
        "Encode the adresses e-mail separated by ';'.\nThose e-mail will receive the warnings",
        required=True)
    pricelist = fields.Many2one(
        'product.pricelist',
        string='Pricelist for the sales price',
    )
    id_synchro = fields.Many2one(
        'ir.cron',
        string='Cronjob',
        required=True,
        help=
        "Cronjob in OpenERP for automatic synchronization. To bind the Cronjob with the configuration, click the button"
    )
    categorie_name = fields.Char(string='Category',
                                 help="Name of the product categorie")
    location_id = fields.Many2one('stock.location',
                                  string='Location',
                                  required=True,
                                  domain=[('usage', '=', 'internal')],
                                  help="Location of new product")
    country_id = fields.Many2one('res.country',
                                 string='Country',
                                 required=True,
                                 help=" Country of Ingram supplier")
    categorie_id = fields.Many2one(
        'product.category',
        string='Category',
        required=True,
        change_default=True,
        domain=[('type', '=', 'normal')],
        help="Select category for the current product")
    supplier_id = fields.Many2one('res.partner',
                                  string='Supplier',
                                  required=True,
                                  domain=[('supplier', '=', True)],
                                  ondelete='cascade',
                                  help="Supplier of this product")
    taxes_ventes = fields.Many2many('account.tax',
                                    "ingram_taxe_sales",
                                    'ingram_config',
                                    "taxe_id",
                                    string='Sales Taxes',
                                    domain=[('parent_id', '=', False),
                                            ('type_tax_use', 'in',
                                             ['sale', 'all'])])
    taxes_achats = fields.Many2many('account.tax',
                                    "ingram_taxe_achat",
                                    'ingram_config2',
                                    "taxe_id2",
                                    string='Purchases Taxes',
                                    domain=[('parent_id', '=', False),
                                            ('type_tax_use', 'in',
                                             ['purchase', 'all'])])

    @api.onchange('supplier_id')
    def onchange_supplier_id(self):
        if self.supplier_id:
            self.country_id = self.supplier_id.country_id.id

    @api.one
    def check_ftp(self):
        ip = self.server_address
        login = self.server_login
        passwd = self.server_passwd
        try:
            ftp = ftplib.FTP()
        except:
            raise Warning(_('FTP was not started!'))
            return False
        ip = ip.split('/')
        txt = ""
        for i in range(len(ip)):
            if i > 0:
                txt += "/" + ip[i]
        try:
            ftp.connect(ip[0])
            if login:
                ftp.login(login, passwd)
            else:
                ftp.login()
        except:
            raise Warning(
                _('Username/password FTP connection was not successfully!'))
            return False
        ftp.close()
        raise Warning(_('FTP connection was successfully!'))
        return True

    @api.model
    def create(self, vals):
        config = self.env['ingram_config'].search([])
        if config:
            raise Warning(_('You can have only one configuration!'))
        res = super(ingram_config, self).create(vals)
        return res

    @api.one
    def sendTextMail(self, ids, title, mess):
        _from = 'Ingram Error <*****@*****.**>'
        to = ids.mailto
        to = to.replace(';', ',')
        txt = ''
        if mess:
            txt += "\r\n"
            txt += mess
            txt += "\r\n"
        mail_obj = self.env['mail.mail']
        res = mail_obj.create({
            'subject': title,
            'email_from': '*****@*****.**',
            'email_to': to,
            'body_html': txt
        })

    @api.multi
    def write(self, values):
        idtaxevente = self.taxes_ventes
        idtaxeAchat = self.taxes_achats
        super(ingram_config, self).write(values)
        prod_tmpl = self.env['product.template']
        prod_prod = self.env['product.product']
        if ('taxes_ventes' in values):
            tab = []
            tab2 = []
            tab3 = []
            for i in idtaxevente:
                tab.append(i.id)
            for j in values['taxes_ventes'][0][2]:
                if not (j in tab):
                    tab2.append(j)
            for i in idtaxevente:
                if not (i.id in values['taxes_ventes'][0][2]):
                    tab3.append(i.id)
            if tab2:
                idProd = prod_tmpl.search([('ingram', '=', True)])
                for j in idProd:
                    empl = prod_prod.search([('product_tmpl_id', '=', j.id)])
                    for k in values['taxes_ventes'][0][2]:
                        empl.taxes_id = [(4, k)]
            if tab3:
                idProd = prod_tmpl.search([('ingram', '=', True)])
                for j in idProd:
                    empl = prod_prod.search([('product_tmpl_id', '=', j.id)])
                    for i in tab3:
                        empl.taxes_id = [(3, i)]
        if ('taxes_achats' in values):
            tab = []
            tab2 = []
            tab3 = []
            for i in idtaxeAchat:
                tab.append(i.id)
            for j in values['taxes_achats'][0][2]:
                if not (j in tab):
                    tab2.append(j)
            for i in idtaxeAchat:
                if not (i.id in values['taxes_achats'][0][2]):
                    tab3.append(i.id)
            if (tab2):
                idProd = prod_tmpl.search([('ingram', '=', True)])
                for j in idProd:
                    empl = prod_prod.search([('product_tmpl_id', '=', j.id)])
                    for i in values['taxes_achats'][0][2]:
                        empl.supplier_taxes_id = [(4, i)]
            if tab3:
                idProd = prod_tmpl.search([('ingram', '=', True)])
                for j in idProd:
                    empl = prod_prod.search([('product_tmpl_id', '=', j.id)])
                    for i in tab3:
                        empl.taxes_id = [(3, i)]

    @api.model
    def cron_function(self):
        id_config = self.search([('xml_active', '=', True)])
        if not id_config:
            _logger.info('No config!')
            return False
        _logger.info(_('Download started'))
        result = id_config.import_data(id_config)
        if result[0] == True:
            _logger.info(_('Download ended'))
        else:
            _logger.info(_('Download error'))
            return False
        _logger.info(_('Synchronization started'))
        result2 = id_config.synchro_categ(id_config)
        if result2[0] == True:
            _logger.info(_('products categories synchronization ended'))
        else:
            _logger.info(_('products categories synchronization error'))
            return False
        _logger.info(_('Products synchronization started'))
        result3 = id_config.synchronisation(id_config)
        _logger.info(result3)
        if result3[0] == True:
            _logger.info(_('Products synchronization ended'))
        else:
            _logger.info(_('Products synchronization error'))
            return False
        _logger.info(_('Clean product started'))
        result4 = id_config.clean_data()
        if result4[0] == True:
            _logger.info(_('Clean product ended'))
        else:
            _logger.info(_('Clean product error'))
            return False
        id_config.clean_categ()
        _logger.info(_('end synchronization'))
        try:
            id_config.date_cron = time.strftime("%Y-%m-%d %H:%M:%S")
        except:
            try:
                id_config.date_cron = time.strftime("%Y-%m-%d %H:%M:%S")
            except:
                pass
        _logger.info('Done')
        return True

    @api.one
    def button_import_data(self):
        _logger.info(_('Download started'))
        config_id = self
        result = self.import_data(config_id)
        if result[0] == True:
            _logger.info(_('Download ended'))
            try:
                self.date_import = time.strftime("%Y-%m-%d %H:%M:%S")
            except:
                try:
                    self.date_import = time.strftime("%Y-%m-%d %H:%M:%S")
                except:
                    pass
        else:
            _logger.info(_('Download error'))
            return False

        return True

    @api.one
    def import_data(self, config_id):
        config = config_id
        ip = config.server_address
        login = config.server_login
        passwd = config.server_passwd
        chm = str(config.chemin)
        try:
            ftp = ftplib.FTP()
        except:
            _logger.error(_('connexion error'))
            self.sendTextMail(
                config_id, "Connecion error",
                "An error occured during the connection to the server.\n\nDetails: \n\t %s"
                % (sys.exc_info()[0]))
            return False
        ip = ip.split('/')
        txt = ""
        for i in range(len(ip)):
            if i > 0:
                txt += "/" + ip[i]
        try:
            ftp.connect(ip[0])
            if login:
                ftp.login(login, passwd)
            else:
                ftp.login()
            ftp.retrlines('LIST')
            ftp.cwd(txt)
            ftp.retrlines('LIST')
            self.download(config_id, '.', chm, ftp)
            ftp.close()
            return True
        except:
            _logger.error(_('Download error'))
            self.sendTextMail(
                config_id, "Import error",
                "An error occured during the importation.\n\nDetails: \n\t %s"
                % (sys.exc_info()[0]))
            return False

    @api.one
    def download(self, config_id, pathsrc, pathdst, ftp):
        idss = config_id
        try:
            lenpathsrc = len(pathsrc)
            l = ftp.nlst(pathsrc)
            for i in l:
                tailleinit = ftp.size(i)
                if ((str(i) == str(idss.file_cat))
                        or str(i) == str(idss.file_prod)):
                    try:
                        ftp.size(i)
                        ftp.retrbinary('RETR ' + i,
                                       open(pathdst + os.sep + i, 'wb').write)
                    except:
                        try:
                            os.makedirs(pathdst + os.sep +
                                        os.path.dirname(i[lenpathsrc:]))
                        except:
                            pass
                        return False
                    if os.path.isfile(pathdst + '/' + i):
                        taille = os.path.getsize(pathdst + '/' + i)
                        if (tailleinit != taille):
                            os.remove(pathdst + '/' + i)
            return True
        except:
            return False

    @api.one
    def clean_data(self):
        _logger.info("clean_data")
        try:
            product_product = self.env['product.product']
            sale_order_line = self.env['sale.order.line']
            purchase_order_line = self.env['purchase.order.line']
            procurement_order = self.env['procurement.order']
            stock_move = self.env['stock.move']
            account_invoice_line = self.env['account.invoice.line']
            aujourdhui = datetime.today()
            semaine = timedelta(weeks=1)
            date = aujourdhui - semaine
            idProd = product_product.search([
                '|', ('active', '=', True), ('active', '=', False),
                ('product_tmpl_id.ingram', '=', True),
                ('product_tmpl_id.last_synchro_ingram', '<', date)
            ],
                                            order='id')
            delete = 0
            undelete = 0
            use = 0
            for i in idProd:
                ids1 = sale_order_line.search([('product_id', '=', i.id)])
                ids2 = purchase_order_line.search([('product_id', '=', i.id)])
                ids3 = procurement_order.search([('product_id', '=', i.id)])
                ids4 = stock_move.search([('product_id', '=', i.id)])
                ids5 = account_invoice_line.search([('product_id', '=', i.id)])
                if not ids1 and not ids2 and not ids3 and not ids4 and not ids5:
                    try:
                        i.unlink()
                        delete += 1
                    except:
                        _logger.info('Delete impossible')
                        undelete += 1
                else:
                    i.active = False
                    use += 1
            _logger.info('Products deleted : %s' % (delete))
            _logger.info('Products non deleted : %s' % (use))
            _logger.info('product cleaned')
            return True
        except:
            _logger.error("Erreur Clean_data")
            self.sendTextMail(
                self, "Error products cleaning",
                "An error occured during the cleaning.\n\nDetails: \n\t %s" %
                (sys.exc_info()[0]))
            return False

    @api.one
    def delete_data(self):
        _logger.info("delete_data")
        try:
            product_product = self.env['product.product']
            sale_order_line = self.env['sale.order.line']
            purchase_order_line = self.env['purchase.order.line']
            procurement_order = self.env['procurement.order']
            stock_move = self.env['stock.move']
            account_invoice_line = self.env['account.invoice.line']
            idProd = product_product.search([
                '|', ('active', '=', True), ('active', '=', False),
                ('product_tmpl_id.ingram', '=', True)
            ],
                                            order='id')
            delete = 0
            undelete = 0
            use = 0
            cpt = 0
            for i in idProd:
                cpt += 1
                ids1 = sale_order_line.search([('product_id', '=', i.id)])
                ids2 = purchase_order_line.search([('product_id', '=', i.id)])
                ids3 = procurement_order.search([('product_id', '=', i.id)])
                ids4 = stock_move.search([('product_id', '=', i.id)])
                ids5 = account_invoice_line.search([('product_id', '=', i.id)])
                if not ids1 and not ids2 and not ids3 and not ids4 and not ids5:
                    try:
                        i.unlink()
                        delete += 1
                    except:
                        _logger.info('Delete impossible')
                        undelete += 1
                else:
                    i.active = False
                    use += 1
            _logger.info('Products deleted : %s' % (delete))
            _logger.info('Products non deleted : %s' % (use))
            _logger.info('product cleaned')
            return True
        except:
            _logger.error("Erreur delete_data")
            self.sendTextMail(
                self, "Error products deleting",
                "An error occured during the cleaning.\n\nDetails: \n\t %s" %
                (sys.exc_info()[0]))
            return False

    @api.one
    def synchro_data(self):
        config_id = self
        _logger.info(_('Products categories synchronization started'))
        result = self.synchro_categ(config_id)
        if result[0] == True:
            _logger.info(_('products categories synchronization ended'))
        else:
            _logger.info(('products categories synchronization error'))
            return False
        _logger.info(_('products synchronization started'))
        result3 = self.synchronisation(config_id)
        _logger.info(result3)
        if result3[0] == True:
            _logger.info(_('products synchronization ended'))
        else:
            _logger.info(_('products synchronization error'))
            return False
        self.clean_categ()
        try:
            self.date_synchro = time.strftime("%Y-%m-%d %H:%M:%S")
        except:
            try:
                self.date_synchro = time.strftime("%Y-%m-%d %H:%M:%S")
            except:
                pass
        return True

    @api.one
    def clean_categ(self):
        _logger.info("Clean_categ")
        product_categ = self.env['product.category']
        product_tmpl = self.env['product.template']
        tab = []
        idss_cat = product_categ.search([('code_categ_ingram', '=', '-1')])
        for i in idss_cat:
            id_child = product_categ.search([('parent_id', '=', i.id)])
            for j in id_child:
                if j in idss_cat and j.id not in tab:
                    id_child2 = product_categ.search([('parent_id', '=', j.id)
                                                      ])
                    for z in id_child2:
                        if z in idss_cat and z.id not in tab:
                            tab.append(z.id)
                    tab.append(j.id)
            tab.append(i.id)
        if 1 == 1:
            for j in tab:
                id_prod = product_tmpl.search([('categ_id', '=', j)])
                id_cat = product_categ.search([('id', 'in', tab),
                                               ('parent_id', '=', j)])
                if not id_prod and not id_cat:
                    try:
                        k = product_categ.browse(j)
                        k.unlink()
                    except:
                        pass
        _logger.info("End Clean Categ")
        return True

    @api.one
    def synchronisation(self, config_id):
        categ = config_id.categorie_id
        supplier = config_id.supplier_id
        chm = str(config_id.chemin)
        file_prod = config_id.file_prod
        listefich = os.listdir(chm + '/')
        product_product = self.env['product.product']
        product_categ = self.env['product.category']
        product_tmpl = self.env['product.template']
        pricelist_supplier = self.env['pricelist.partnerinfo']
        product_supplier = self.env['product.supplierinfo']
        product_routes = self.env['stock.location.route']
        taxes_a = []
        taxes_v = []
        for a in config_id.taxes_achats:
            taxes_a.append(a.id)
        for a in config_id.taxes_ventes:
            taxes_v.append(a.id)
        try:
            compteur = 0
            for i in listefich:
                if str(i) == str(file_prod):
                    fichier = open(chm + i, 'rb')
                    fichiercsv = csv.reader(fichier,
                                            delimiter=',',
                                            quotechar='|')
                    for ligne in fichiercsv:
                        if ligne[0] != "Ingram Part Number":
                            i = 0
                            nom = ligne[13]
                            name = ''
                            lgt = len(nom)
                            while (i < lgt):
                                try:
                                    nom[i].decode('latin-1')
                                    name += nom[i]
                                    i += 1
                                except:
                                    i += 1
                            nom = name[0:127]
                            desc = name
                            print "n:", compteur
                            _logger.info(compteur)
                            empl = product_product.search([('default_code',
                                                            '=', ligne[0])])
                            if empl:
                                idprod = empl.product_tmpl_id
                                categ_ingram = product_categ.search([
                                    ('code_categ_ingram', '=', ligne[7])
                                ])
                                if not categ_ingram:
                                    categ_ingram = categ
                                elif len(categ_ingram) > 1:
                                    categ_ingram = categ_ingram[0]
                                if ligne[8] == 'X' or not ligne[8]:
                                    ligne[8] = '0.0'
                                idprod.name = nom
                                idprod.standard_price = float(ligne[8])
                                idprod.weight_net = float(ligne[6])
                                idprod.description = desc
                                idprod.valuation = 'real_time'
                                idprod.description_sale = desc
                                idprod.categ_id = categ_ingram.id
                                idprod.last_synchro_ingram = time.strftime(
                                    "%Y-%m-%d")
                                suppinfo_id = idprod.seller_ids
                                exist_line = False
                                exist = False
                                for b in suppinfo_id:
                                    if b.name.id == supplier.id:
                                        exist = b.id
                                        if not b.product_name or not b.product_code:
                                            b.product_name = nom
                                            b.product_code = ligne[0]
                                        for c in b.pricelist_ids:
                                            exist_line = True
                                            if c.name == 'INGRAM' and c.min_quantity == 1:
                                                c.price = float(ligne[8])
                                if exist and exist_line == False:
                                    pricelist_supplier.create({
                                        'min_quantity':
                                        '1',
                                        'price':
                                        float(ligne[8]),
                                        'suppinfo_id':
                                        exist,
                                        'name':
                                        'INGRAM'
                                    })
                                if (len(ligne[2]) == 12):
                                    ligne[2] = "0" + ligne[2]
                                if len(ligne[2]) == 13:
                                    empl.name_template = nom
                                    empl.active = True
                                    empl.ean13 = ligne[2]
                                    empl.vpn = ligne[1]
                                    empl.manufacturer = ligne[5]
                                else:
                                    empl.name_template = nom
                                    empl.active = True
                                    empl.vpn = ligne[1]
                                    empl.manufacturer = ligne[5]
                            else:
                                categ_ingram = product_categ.search([
                                    ('code_categ_ingram', '=', ligne[7])
                                ])
                                if not categ_ingram:
                                    categ_ingram = categ
                                elif len(categ_ingram) > 1:
                                    categ_ingram = categ_ingram[0]
                                if ligne[8] == 'X' or not ligne[8]:
                                    ligne[8] = '0.0'
                                mto_ids = product_routes.search([
                                    '|', ('name', '=', 'Make To Order'),
                                    ('name', '=', 'Buy')
                                ])
                                route = []
                                for d in mto_ids:
                                    route.append(d.id)
                                tmpl_id = product_tmpl.create({
                                    'valuation':
                                    'real_time',
                                    'name':
                                    nom,
                                    'standard_price':
                                    float(ligne[8]),
                                    'weight_net':
                                    float(ligne[6]),
                                    'description':
                                    desc,
                                    'description_sale':
                                    desc,
                                    'categ_id':
                                    categ_ingram.id,
                                    'route_ids': [(6, 0, route)],
                                    'ingram':
                                    True,
                                    'type':
                                    'product',
                                    'last_synchro_ingram':
                                    time.strftime("%Y-%m-%d")
                                })
                                id_prod = product_product.search([
                                    ('product_tmpl_id', '=', tmpl_id.id)
                                ])
                                if (len(ligne[2]) == 12):
                                    ligne[2] = "0" + ligne[2]
                                if len(ligne[2]) == 13:
                                    id_prod.default_code = ligne[0]
                                    id_prod.name_template = nom
                                    id_prod.taxes_id = [(6, 0, taxes_v)]
                                    id_prod.supplier_taxes_id = [(6, 0,
                                                                  taxes_a)]
                                    id_prod.price_extra = 0.00
                                    id_prod.active = True
                                    id_prod.product_tmpl_id = tmpl_id.id
                                    id_prod.ean13 = ligne[2]
                                    id_prod.vpn = ligne[1]
                                    id_prod.manufacturer = ligne[5]
                                else:
                                    id_prod.default_code = ligne[0]
                                    id_prod.name_template = nom
                                    id_prod.taxes_id = [(6, 0, taxes_v)]
                                    id_prod.supplier_taxes_id = [(6, 0,
                                                                  taxes_a)]
                                    id_prod.price_extra = 0.00
                                    id_prod.active = True
                                    id_prod.product_tmpl_id = tmpl_id.id
                                    id_prod.vpn = ligne[1]
                                    id_prod.manufacturer = ligne[5]
                                supp_info = product_supplier.create({
                                    'name':
                                    supplier.id,
                                    'min_qty':
                                    0,
                                    'product_tmpl_id':
                                    tmpl_id.id,
                                    'product_name':
                                    nom,
                                    'product_code':
                                    ligne[0]
                                })
                                pricelist_supplier.create({
                                    'min_quantity':
                                    '1',
                                    'price':
                                    float(ligne[8]),
                                    'suppinfo_id':
                                    supp_info.id,
                                    'name':
                                    'INGRAM'
                                })
                        compteur += 1
                    fichier.close()
            return True
        except:
            _logger.error("Erreur Synchro_produit")
            self.sendTextMail(
                config_id, "Error Synchronization",
                "An error occured during the synchronization.\n \nDetails: \n\t %s"
                % (sys.exc_info()[0]))
            return False

    @api.one
    def synchro_categ(self, config_id):
        categ = config_id.categorie_id
        file_cat = config_id.file_cat
        chm = str(config_id.chemin)
        listefich = os.listdir(chm + '/')
        product_categ = self.env['product.category']
        compteur = 0
        for i in listefich:
            if str(i) == str(file_cat):
                fichier = open(chm + '/' + i, 'rb')
                fichiercsv = csv.reader(fichier, delimiter=';')
                cat = []
                for ligne in fichiercsv:
                    ligne_un = product_categ.search([('code_categ_ingram', '=',
                                                      ligne[1]),
                                                     ('name', '=', ligne[2])])
                    if ligne_un:
                        ligne_trois = product_categ.search([
                            ('code_categ_ingram', '=', ligne[3]),
                            ('name', '=', ligne[4]),
                            ('parent_id', '=', ligne_un.id)
                        ])
                    else:
                        ligne_trois = product_categ.search([
                            ('code_categ_ingram', '=', ligne[3]),
                            ('name', '=', ligne[4])
                        ])
                    if ligne_trois:
                        ligne_cinq = product_categ.search([
                            ('code_categ_ingram', '=', ligne[5]),
                            ('name', '=', ligne[6]),
                            ('parent_id', '=', ligne_trois.id)
                        ])
                    else:
                        ligne_cinq = product_categ.search([
                            ('code_categ_ingram', '=', ligne[5]),
                            ('name', '=', ligne[6])
                        ])
                    _logger.info(compteur)
                    if not ligne_un:
                        ligne_un = product_categ.create({
                            'name':
                            ligne[2],
                            'parent_id':
                            categ.id,
                            'code_categ_ingram':
                            ligne[1],
                            'type':
                            'view'
                        })
                        if ligne_un not in cat:
                            cat.append(ligne_un)
                    else:
                        ligne_un.code_categ_ingram = ligne[1]
                        if ligne_un not in cat:
                            cat.append(ligne_un)
                    if not ligne_trois:
                        ligne_trois = product_categ.create({
                            'name':
                            ligne[4],
                            'parent_id':
                            ligne_un.id,
                            'code_categ_ingram':
                            ligne[3],
                            'type':
                            'view'
                        })
                        if ligne_trois not in cat:
                            cat.append(ligne_trois)
                    else:
                        if ligne_trois.parent_id != ligne_un:
                            ligne_trois.parent_id = ligne_un.id
                            ligne_trois.code_categ_ingram = ligne[3]
                        else:
                            ligne_trois.code_categ_ingram = ligne[3]
                        if ligne_trois not in cat:
                            cat.append(ligne_trois)
                    if not ligne_cinq:
                        ligne_cinq = product_categ.create({
                            'name':
                            ligne[6],
                            'parent_id':
                            ligne_trois.id,
                            'code_categ_ingram':
                            ligne[5]
                        })
                        if ligne_cinq not in cat:
                            cat.append(ligne_cinq)
                    else:
                        if ligne_cinq.parent_id != ligne_trois:
                            ligne_cinq.parent_id = ligne_trois.id
                            ligne_cinq.code_categ_ingram = ligne[5]
                        else:
                            ligne_cinq.code_categ_ingram = ligne[5]
                        if ligne_cinq not in cat:
                            cat.append(ligne_cinq[0])
                    compteur += 1
                fichier.close()
                idss = product_categ.search([('code_categ_ingram', '!=', False)
                                             ])
                tab = []
                for i in idss:
                    if i not in cat:
                        tab.append(i)
                        i.code_categ_ingram = '-1'
        return True
Exemplo n.º 3
0
class EventTrackPresenceReport(models.Model):
    _name = "event.track.presence.report"
    _description = "Event track presence report"
    _auto = False

    employee_id = fields.Many2one(comodel_name='hr.employee',
                                  string='Employee',
                                  readonly=True)
    customer_id = fields.Many2one(comodel_name='res.partner',
                                  string='Customer',
                                  readonly=True)
    city = fields.Char(string='City', readonly=True)
    street = fields.Char(string='Street', readonly=True)
    event_id = fields.Many2one(comodel_name='event.event',
                               string='Event',
                               readonly=True)
    start_hour = fields.Char(string='Start hour', readonly=True)
    end_hour = fields.Char(string='End hour', readonly=True)
    session_duration = fields.Float(string='Session duration', readonly=True)
    days = fields.Char(string='Days', readonly=True)
    session_name = fields.Char(string='Job', readonly=True)

    def _select(self):
        select_str = """
        select p.employee_id as employee_id, p.customer_id as customer_id,
               COALESCE(r.street2,r.street) as street, r.city as city,
               p.event as event_id, p.start_hour as start_hour,
               p.end_hour as end_hour,
               (p.session_duration * (select count(distinct(p2.session_day))
                                      from   event_track_presence p2
                                      where  p2.event = p.event
                                        and  p2.partner = p.partner
                                        and  p2.start_hour = p.start_hour
                                        and  p2.end_hour = p.end_hour))
                as session_duration,
                array_to_string(array_agg(distinct(replace(replace(replace(
                replace(replace(replace(replace(p.session_day,'0','L'),'1','M')
                ,'2','X'),'3','J'),'4','V'),'5','S'),'6','D'))),',') as days,
               COALESCE(e.notes,
               (select t.name
                from   event_track t
                where  t.event_id = p.event
                  and  t.id = (select min(t2.id)
                                from  event_track t2
                                where t2.event_id = p.event))) as session_name,
               min(p.id) as id
        """
        return select_str

    def _from(self):
        from_str = """
        from   event_track_presence p
               inner join res_partner r on r.id = p.customer_id
               inner join event_event e on e.id = p.event
        """
        return from_str

    def _where(self):
        where_str = """
        where  p.state != 'canceled'
          and  p.analytic_account_state not in ('canceled','close')
          and  p.customer_id is not null
          and  p.employee_id is not null
        """
        return where_str

    def _where2(self):
        res = "{} and p.employee_id = {}".format(
            self._where(), self.env.context.get('employee_id'))
        res = "{} and p.session_date_without_hour >= '{}'".format(
            res, self.env.context.get('from_date'))
        res = "{} and p.session_date_without_hour <= '{}'".format(
            res, self.env.context.get('to_date'))
        return res

    def _group_by(self):
        group_by_str = """
        group  by 1, 2, 3, 4, 5, 6, 7, 8, 10
        """
        return group_by_str

    def _order_by(self):
        order_by_str = """
        order  by 1, 2, 5, 9
        """
        return order_by_str

    def init(self, cr):
        tools.drop_view_if_exists(cr, self._table)
        cr.execute("""CREATE or REPLACE VIEW %s as (%s %s %s %s %s)
        """ % (self._table, self._select(), self._from(), self._where(),
               self._group_by(), self._order_by()))

    @api.multi
    def presence_analysis_from_employee(self):
        tools.drop_view_if_exists(self.env.cr, self._table)
        self.env.cr.execute("""CREATE or REPLACE VIEW %s as (%s %s %s %s %s)
        """ % (self._table, self._select(), self._from(), self._where2(),
               self._group_by(), self._order_by()))
Exemplo n.º 4
0
class AccountJournal(models.Model):
    _name = "account.journal"
    _description = "Journal"
    _order = 'sequence, type, code'

    def _default_inbound_payment_methods(self):
        return self.env.ref('account.account_payment_method_manual_in')

    def _default_outbound_payment_methods(self):
        return self.env.ref('account.account_payment_method_manual_out')

    name = fields.Char(string='Journal Name', required=True)
    code = fields.Char(
        string='Short Code',
        size=5,
        required=True,
        help=
        "The journal entries of this journal will be named using this prefix.")
    type = fields.Selection([
            ('sale', 'Sale'),
            ('purchase', 'Purchase'),
            ('cash', 'Cash'),
            ('bank', 'Bank'),
            ('general', 'Miscellaneous'),
        ], required=True,
        help="Select 'Sale' for customer invoices journals."\
        " Select 'Purchase' for vendor bills journals."\
        " Select 'Cash' or 'Bank' for journals that are used in customer or vendor payments."\
        " Select 'General' for miscellaneous operations journals."\
        " Select 'Opening/Closing Situation' for entries generated for new fiscal years.")
    type_control_ids = fields.Many2many('account.account.type',
                                        'account_journal_type_rel',
                                        'journal_id',
                                        'type_id',
                                        string='Account Types Allowed')
    account_control_ids = fields.Many2many('account.account',
                                           'account_account_type_rel',
                                           'journal_id',
                                           'account_id',
                                           string='Accounts Allowed',
                                           domain=[('deprecated', '=', False)])
    default_credit_account_id = fields.Many2one(
        'account.account',
        string='Default Credit Account',
        domain=[('deprecated', '=', False)],
        help="It acts as a default account for credit amount")
    default_debit_account_id = fields.Many2one(
        'account.account',
        string='Default Debit Account',
        domain=[('deprecated', '=', False)],
        help="It acts as a default account for debit amount")
    update_posted = fields.Boolean(
        string='Allow Cancelling Entries',
        help=
        "Check this box if you want to allow the cancellation the entries related to this journal or of the invoice related to this journal"
    )
    group_invoice_lines = fields.Boolean(
        string='Group Invoice Lines',
        help=
        "If this box is checked, the system will try to group the accounting lines when generating them from invoices."
    )
    sequence_id = fields.Many2one(
        'ir.sequence',
        string='Entry Sequence',
        help=
        "This field contains the information related to the numbering of the journal entries of this journal.",
        required=True,
        copy=False)
    refund_sequence_id = fields.Many2one(
        'ir.sequence',
        string='Refund Entry Sequence',
        help=
        "This field contains the information related to the numbering of the refund entries of this journal.",
        copy=False)
    sequence = fields.Integer(
        help='Used to order Journals in the dashboard view')

    #groups_id = fields.Many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', string='Groups')
    currency_id = fields.Many2one('res.currency',
                                  help='The currency used to enter statement',
                                  string="Currency",
                                  oldname='currency')
    company_id = fields.Many2one('res.company',
                                 string='Company',
                                 required=True,
                                 index=True,
                                 default=lambda self: self.env.user.company_id,
                                 help="Company related to this journal")

    refund_sequence = fields.Boolean(
        string='Dedicated Refund Sequence',
        help=
        "Check this box if you don't want to share the same sequence for invoices and refunds made from this journal",
        default=False)

    inbound_payment_method_ids = fields.Many2many(
        'account.payment.method',
        'account_journal_inbound_payment_method_rel',
        'journal_id',
        'inbound_payment_method',
        domain=[('payment_type', '=', 'inbound')],
        string='Debit Methods',
        default=lambda self: self._default_inbound_payment_methods(),
        help=
        "Means of payment for collecting money. Odoo modules offer various payments handling facilities, "
        "but you can always use the 'Manual' payment method in order to manage payments outside of the software."
    )
    outbound_payment_method_ids = fields.Many2many(
        'account.payment.method',
        'account_journal_outbound_payment_method_rel',
        'journal_id',
        'outbound_payment_method',
        domain=[('payment_type', '=', 'outbound')],
        string='Payment Methods',
        default=lambda self: self._default_outbound_payment_methods(),
        help=
        "Means of payment for sending money. Odoo modules offer various payments handling facilities, "
        "but you can always use the 'Manual' payment method in order to manage payments outside of the software."
    )
    at_least_one_inbound = fields.Boolean(compute='_methods_compute',
                                          store=True)
    at_least_one_outbound = fields.Boolean(compute='_methods_compute',
                                           store=True)
    profit_account_id = fields.Many2one(
        'account.account',
        string='Profit Account',
        domain=[('deprecated', '=', False)],
        help=
        "Used to register a profit when the ending balance of a cash register differs from what the system computes"
    )
    loss_account_id = fields.Many2one(
        'account.account',
        string='Loss Account',
        domain=[('deprecated', '=', False)],
        help=
        "Used to register a loss when the ending balance of a cash register differs from what the system computes"
    )

    # Bank journals fields
    bank_account_id = fields.Many2one('res.partner.bank',
                                      string="Bank Account",
                                      ondelete='restrict',
                                      copy=False)
    display_on_footer = fields.Boolean(
        "Show in Invoices Footer",
        help=
        "Display this bank account on the footer of printed documents like invoices and sales orders."
    )
    bank_statements_source = fields.Selection([('manual', 'Record Manually')],
                                              string='Bank Feeds')
    bank_acc_number = fields.Char(related='bank_account_id.acc_number')
    bank_id = fields.Many2one('res.bank', related='bank_account_id.bank_id')

    _sql_constraints = [
        ('code_company_uniq', 'unique (code, name, company_id)',
         'The code and name of the journal must be unique per company !'),
    ]

    @api.one
    @api.constrains('currency_id', 'default_credit_account_id',
                    'default_debit_account_id')
    def _check_currency(self):
        if self.currency_id:
            if self.default_credit_account_id and not self.default_credit_account_id.currency_id.id == self.currency_id.id:
                raise UserError(
                    _('Configuration error!\nThe currency of the journal should be the same than the default credit account.'
                      ))
            if self.default_debit_account_id and not self.default_debit_account_id.currency_id.id == self.currency_id.id:
                raise UserError(
                    _('Configuration error!\nThe currency of the journal should be the same than the default debit account.'
                      ))

    @api.one
    @api.constrains('type', 'bank_account_id')
    def _check_bank_account(self):
        if self.type == 'bank' and self.bank_account_id:
            if self.bank_account_id.company_id != self.company_id:
                raise ValidationError(
                    _('The bank account of a bank journal must belong to the same company (%s).'
                      ) % self.company_id.name)
            # A bank account can belong to a customer/supplier, in which case their partner_id is the customer/supplier.
            # Or they are part of a bank journal and their partner_id must be the company's partner_id.
            if self.bank_account_id.partner_id != self.company_id.partner_id:
                raise ValidationError(
                    _('The holder of a journal\'s bank account must be the company (%s).'
                      ) % self.company_id.name)

    @api.onchange('default_debit_account_id')
    def onchange_debit_account_id(self):
        if not self.default_credit_account_id:
            self.default_credit_account_id = self.default_debit_account_id

    @api.onchange('default_credit_account_id')
    def onchange_credit_account_id(self):
        if not self.default_debit_account_id:
            self.default_debit_account_id = self.default_credit_account_id

    @api.multi
    def unlink(self):
        bank_accounts = self.env['res.partner.bank'].browse()
        for bank_account in self.mapped('bank_account_id'):
            accounts = self.search([('bank_account_id', '=', bank_account.id)])
            if accounts <= self:
                bank_accounts += bank_account
        ret = super(AccountJournal, self).unlink()
        bank_accounts.unlink()
        return ret

    @api.one
    def copy(self, default=None):
        default = dict(default or {})
        default.update(code=_("%s (copy)") % (self.code or ''),
                       name=_("%s (copy)") % (self.name or ''))
        return super(AccountJournal, self).copy(default)

    @api.multi
    def write(self, vals):
        for journal in self:
            if ('company_id' in vals
                    and journal.company_id.id != vals['company_id']):
                if self.env['account.move'].search(
                    [('journal_id', 'in', self.ids)], limit=1):
                    raise UserError(
                        _('This journal already contains items, therefore you cannot modify its company.'
                          ))
            if ('code' in vals and journal.code != vals['code']):
                if self.env['account.move'].search(
                    [('journal_id', 'in', self.ids)], limit=1):
                    raise UserError(
                        _('This journal already contains items, therefore you cannot modify its short name.'
                          ))
                new_prefix = self._get_sequence_prefix(vals['code'],
                                                       refund=False)
                journal.sequence_id.write({'prefix': new_prefix})
                if journal.refund_sequence_id:
                    new_prefix = self._get_sequence_prefix(vals['code'],
                                                           refund=True)
                    journal.refund_sequence_id.write({'prefix': new_prefix})
            if 'currency_id' in vals:
                if not 'default_debit_account_id' in vals and self.default_debit_account_id:
                    self.default_debit_account_id.currency_id = vals[
                        'currency_id']
                if not 'default_credit_account_id' in vals and self.default_credit_account_id:
                    self.default_credit_account_id.currency_id = vals[
                        'currency_id']
            if 'bank_acc_number' in vals and not vals.get(
                    'bank_acc_number') and journal.bank_account_id:
                raise UserError(
                    _('You cannot empty the account number once set.\nIf you would like to delete the account number, you can do it from the Bank Accounts list.'
                      ))
        result = super(AccountJournal, self).write(vals)

        # Create the bank_account_id if necessary
        if 'bank_acc_number' in vals:
            for journal in self.filtered(
                    lambda r: r.type == 'bank' and not r.bank_account_id):
                journal.set_bank_account(vals.get('bank_acc_number'),
                                         vals.get('bank_id'))

        return result

    @api.model
    def _get_sequence_prefix(self, code, refund=False):
        prefix = code.upper()
        if refund:
            prefix = 'R' + prefix
        return prefix + '/%(range_year)s/'

    @api.model
    def _create_sequence(self, vals, refund=False):
        """ Create new no_gap entry sequence for every new Journal"""
        prefix = self._get_sequence_prefix(vals['code'], refund)
        seq = {
            'name': vals['name'],
            'implementation': 'no_gap',
            'prefix': prefix,
            'padding': 4,
            'number_increment': 1,
            'use_date_range': True,
        }
        if 'company_id' in vals:
            seq['company_id'] = vals['company_id']
        return self.env['ir.sequence'].create(seq)

    @api.model
    def _prepare_liquidity_account(self, name, company, currency_id, type):
        '''
        This function prepares the value to use for the creation of the default debit and credit accounts of a
        liquidity journal (created through the wizard of generating COA from templates for example).

        :param name: name of the bank account
        :param company: company for which the wizard is running
        :param currency_id: ID of the currency in wich is the bank account
        :param type: either 'cash' or 'bank'
        :return: mapping of field names and values
        :rtype: dict
        '''

        # Seek the next available number for the account code
        code_digits = company.accounts_code_digits or 0
        if type == 'bank':
            account_code_prefix = company.bank_account_code_prefix or ''
        else:
            account_code_prefix = company.cash_account_code_prefix or company.bank_account_code_prefix or ''
        for num in xrange(1, 100):
            new_code = str(account_code_prefix.ljust(code_digits - 1,
                                                     '0')) + str(num)
            rec = self.env['account.account'].search(
                [('code', '=', new_code), ('company_id', '=', company.id)],
                limit=1)
            if not rec:
                break
        else:
            raise UserError(_('Cannot generate an unused account code.'))

        liquidity_type = self.env.ref('account.data_account_type_liquidity')
        return {
            'name': name,
            'currency_id': currency_id or False,
            'code': new_code,
            'user_type_id': liquidity_type and liquidity_type.id or False,
            'company_id': company.id,
        }

    @api.model
    def create(self, vals):
        company_id = vals.get('company_id', self.env.user.company_id.id)
        if vals.get('type') in ('bank', 'cash'):
            # For convenience, the name can be inferred from account number
            if not vals.get('name') and 'bank_acc_number' in vals:
                vals['name'] = vals['bank_acc_number']

            # If no code provided, loop to find next available journal code
            if not vals.get('code'):
                for num in xrange(1, 100):
                    # journal_code has a maximal size of 5, hence we can enforce the boundary num < 100
                    journal_code = (vals['type'] == 'cash' and 'CSH'
                                    or 'BNK') + str(num)
                    journal = self.env['account.journal'].search(
                        [('code', '=', journal_code),
                         ('company_id', '=', company_id)],
                        limit=1)
                    if not journal:
                        vals['code'] = journal_code
                        break
                else:
                    raise UserError(
                        _("Cannot generate an unused journal code. Please fill the 'Shortcode' field."
                          ))

            # Create a default debit/credit account if not given
            default_account = vals.get('default_debit_account_id') or vals.get(
                'default_credit_account_id')
            if not default_account:
                company = self.env['res.company'].browse(company_id)
                account_vals = self._prepare_liquidity_account(
                    vals.get('name'), company, vals.get('currency_id'),
                    vals.get('type'))
                default_account = self.env['account.account'].create(
                    account_vals)
                vals['default_debit_account_id'] = default_account.id
                vals['default_credit_account_id'] = default_account.id

        # We just need to create the relevant sequences according to the chosen options
        if not vals.get('sequence_id'):
            vals.update({'sequence_id': self.sudo()._create_sequence(vals).id})
        if vals.get('type') in ('sale', 'purchase') and vals.get(
                'refund_sequence') and not vals.get('refund_sequence_id'):
            vals.update({
                'refund_sequence_id':
                self.sudo()._create_sequence(vals, refund=True).id
            })

        journal = super(AccountJournal, self).create(vals)

        # Create the bank_account_id if necessary
        if journal.type == 'bank' and not journal.bank_account_id and vals.get(
                'bank_acc_number'):
            journal.set_bank_account(vals.get('bank_acc_number'),
                                     vals.get('bank_id'))

        return journal

    def set_bank_account(self, acc_number, bank_id=None):
        """ Create a res.partner.bank and set it as value of the  field bank_account_id """
        self.ensure_one()
        self.bank_account_id = self.env['res.partner.bank'].create({
            'acc_number':
            acc_number,
            'bank_id':
            bank_id,
            'company_id':
            self.company_id.id,
            'currency_id':
            self.currency_id.id,
            'partner_id':
            self.company_id.partner_id.id,
        }).id

    @api.multi
    @api.depends('name', 'currency_id', 'company_id', 'company_id.currency_id')
    def name_get(self):
        res = []
        for journal in self:
            currency = journal.currency_id or journal.company_id.currency_id
            name = "%s (%s)" % (journal.name, currency.name)
            res += [(journal.id, name)]
        return res

    @api.multi
    @api.depends('inbound_payment_method_ids', 'outbound_payment_method_ids')
    def _methods_compute(self):
        for journal in self:
            journal.at_least_one_inbound = bool(
                len(journal.inbound_payment_method_ids))
            journal.at_least_one_outbound = bool(
                len(journal.outbound_payment_method_ids))
Exemplo n.º 5
0
class AccountTaxGroup(models.Model):
    _name = 'account.tax.group'
    _order = 'sequence asc'

    name = fields.Char(required=True, translate=True)
    sequence = fields.Integer(default=10)
Exemplo n.º 6
0
class case_type(models.Model):
    _name = 'case.type'

    name = fields.Char(string='Name',required=True)
Exemplo n.º 7
0
class TicketGroup(models.Model):

    _name = "ticket.group"

    name = fields.Char('Name')
Exemplo n.º 8
0
class ProductProduct(models.Model):
    _inherit = ['product.product', 'product.configurator']
    _name = "product.product"

    # This is needed as the AbstractModel removes the delegated related field
    name = fields.Char(related="product_tmpl_id.name")

    @api.multi
    def _get_product_attributes_values_dict(self):
        # Retrieve first the attributes from template to preserve order
        res = self.product_tmpl_id._get_product_attributes_dict()
        for val in res:
            value = self.attribute_value_ids.filtered(
                lambda x: x.attribute_id.id == val['attribute_id'])
            val['value_id'] = value.id
        return res

    @api.multi
    def _get_product_attributes_values_text(self):
        description = self.attribute_value_ids.mapped(
            lambda x: "%s: %s" % (x.attribute_id.name, x.name))
        return "%s\n%s" % (self.product_tmpl_id.name, "\n".join(description))

    @api.model
    def _build_attributes_domain(self, product_template, product_attributes):
        domain = []
        cont = 0
        if product_template:
            domain.append(('product_tmpl_id', '=', product_template.id))
            for attr_line in product_attributes:
                if isinstance(attr_line, dict):
                    value_id = attr_line.get('value_id')
                else:
                    value_id = attr_line.value_id.id
                if value_id:
                    domain.append(('attribute_value_ids', '=', value_id))
                    cont += 1
        return domain, cont

    @api.model
    def _product_find(self, product_template, product_attributes):
        if product_template:
            domain, cont = self._build_attributes_domain(
                product_template, product_attributes)
            products = self.search(domain)
            # Filter the product with the exact number of attributes values
            for product in products:
                if len(product.attribute_value_ids) == cont:
                    return product
        return False

    @api.constrains('product_tmpl_id', 'attribute_value_ids')
    def _check_duplicity(self):
        for product in self:
            domain = [('product_tmpl_id', '=', product.product_tmpl_id.id)]
            for value in product.attribute_value_ids:
                domain.append(('attribute_value_ids', '=', value.id))
            other_products = self.search(domain)
            # Filter the product with the exact number of attributes values
            cont = len(product.attribute_value_ids)
            for other_product in other_products:
                if (len(other_product.attribute_value_ids) == cont and
                        other_product != product):
                    raise exceptions.ValidationError(
                        _("There's another product with the same attributes."))

    @api.constrains('product_tmpl_id', 'attribute_value_ids')
    def _check_configuration_validity(self):
        """This method checks that the current selection values are correct
        according rules. As default, the validity means that all the attributes
        values are set. This can be overridden to set another rules.

        :raises: exceptions.ValidationError: If the check is not valid.
        """
        for product in self:
            if bool(product.product_tmpl_id.attribute_line_ids.mapped(
                    'attribute_id') -
                    product.attribute_line_ids.mapped('attribute_id')):
                raise exceptions.ValidationError(
                    _("You have to fill all the attributes values."))

    @api.model
    def create(self, vals):
        if (not vals.get('attribute_value_ids') and
                vals.get('product_attribute_ids')):
            vals['attribute_value_ids'] = (
                (4, x[2]['value_id'])
                for x in vals.pop('product_attribute_ids')
                if x[2].get('value_id'))
        obj = self.with_context(product_name=vals.get('name', ''))
        return super(ProductProduct, obj).create(vals)
Exemplo n.º 9
0
class IfrsLines(models.Model):

    _name = 'ifrs.lines'
    _order = 'ifrs_id, sequence'

    def _get_ifrs_query(self, cr, uid, brw, context=None):
        """ Fetches a semi-query to be provided as context into aml"""
        context = dict(context or {})
        query = ''
        if not brw.filter_id:
            return query
        args = eval(brw.filter_id.domain)
        query = self.pool['account.move.line']._where_calc(
            cr, uid, args, context=context)
        where_clause, where_clause_params = query.get_sql()[1:]
        where_clause = where_clause.replace('account_move_line', 'l')
        query = cr.mogrify(where_clause, where_clause_params)
        return query

    def _get_sum_total(
            self, cr, uid, brw, operand, number_month=None,
            one_per=False, bag=None, context=None):
        """ Calculates the sum of the line total_ids & operand_ids the current
        ifrs.line
        @param number_month: period to compute
        """
        context = context and dict(context) or {}
        res = 0

        # If the report is two or twelve columns, will choose the field needed
        # to make the sum
        if context.get('whole_fy', False) or one_per:
            field_name = 'ytd'
        else:
            field_name = 'period_%s' % str(number_month)

        # It takes the sum of the total_ids & operand_ids
        for ttt in getattr(brw, operand):
            res += bag[ttt.id].get(field_name, 0.0)
        return res

    def _get_sum_detail(self, cr, uid, ids=None, number_month=None,
                        context=None):
        """ Calculates the amount sum of the line type == 'detail'
        @param number_month: periodo a calcular
        """
        fy_obj = self.pool.get('account.fiscalyear')
        period_obj = self.pool.get('account.period')
        context = context and dict(context) or {}
        cx = context.copy()
        res = 0.0

        if not cx.get('fiscalyear'):
            cx['fiscalyear'] = fy_obj.find(cr, uid)

        fy_id = cx['fiscalyear']

        brw = self.browse(cr, uid, ids)
        
        # custom
        date_domain = []
        if brw.acc_val == 'init':
            if cx.get('whole_fy', False):
                cx['periods'] = period_obj.search(cr, uid, [
                    ('fiscalyear_id', '=', fy_id), ('special', '=', True)])
                # custom
                date_domain += [('date_maturity', '<', fy_obj.browse(cr, uid, fy_id).date_start)]
            else:
                period_from = period_obj.search(cr, uid, [
                    ('fiscalyear_id', '=', fy_id), ('special', '=', True)])
                # Case when the period_from is the first non-special period
                # of the fiscalyear
                if period_obj.browse(cr, uid, cx['period_from']).date_start == \
                        fy_obj.browse(cr, uid, fy_id).date_start:
                    cx['period_to'] = period_from[0]
                else:
                    cx['period_to'] = period_obj.previous(
                        cr, uid, cx['period_from'])
                cx['period_from'] = period_from[0]
                # custom
                date_domain += [('date_maturity', '<', period_obj.browse(cr, uid, cx['period_from']).date_start)]
        elif brw.acc_val == 'var':
            # it is going to be the one sent by the previous cx
            if cx.get('whole_fy', False):
                cx['periods'] = period_obj.search(cr, uid, [
                ('fiscalyear_id', '=', fy_id), ('special', '=', False)])
            # custom
                date_domain += [('date_maturity', '>=', fy_obj.browse(cr, uid, fy_id).date_start)]
                date_domain += [('date_maturity', '<=', fy_obj.browse(cr, uid, fy_id).date_stop)]
            else:
                date_domain += [('date_maturity', '>=', period_obj.browse(cr, uid, cx['period_from']).date_start)]
                date_domain += [('date_maturity', '<=', period_obj.browse(cr, uid, cx['period_to']).date_stop)]
        else:
            # it is going to be from the fiscalyear's beginning
            if cx.get('whole_fy', False):
                cx['periods'] = period_obj.search(cr, uid, [
                    ('fiscalyear_id', '=', fy_id)])
                # custom
                date_domain += [('date_maturity', '<=', fy_obj.browse(cr, uid, fy_id).date_stop)]
            else:
                period_from = period_obj.search(cr, uid, [
                    ('fiscalyear_id', '=', fy_id), ('special', '=', True)])
                cx['period_from'] = period_from[0]
                cx['periods'] = \
                    period_obj.build_ctx_periods(cr, uid, cx['period_from'],
                                                 cx['period_to'])
                # custom
                date_domain += [('date_maturity', '<=', fy_obj.browse(cr, uid, fy_id).date_stop)]

        if brw.type == 'detail':
            # Si es de tipo detail
            # If we have to only take into account a set of Journals
            cx['journal_ids'] = [aj_brw.id for aj_brw in brw.journal_ids]
            cx['analytic'] = [an.id for an in brw.analytic_ids]
            cx['ifrs_tax'] = [tx.id for tx in brw.tax_code_ids]
            cx['ifrs_partner'] = [p_brw.id for p_brw in brw.partner_ids]
            cx['ifrs_query'] = self._get_ifrs_query(cr, uid, brw, context)

            # NOTE: This feature is not yet been implemented
            # cx['partner_detail'] = cx.get('partner_detail')

            # Refreshing record with new context
            brw = self.browse(cr, uid, ids, context=cx)

            for aa in brw.cons_ids:
                # Se hace la sumatoria de la columna balance, credito o debito.
                # Dependiendo de lo que se escoja en el wizard
                if brw.value == 'debit':
                    res += aa.debit
                elif brw.value == 'credit':
                    res += aa.credit
                else:
                    res += aa.balance
                    
                # custom 
                if brw.ifrs_id.arap_account_ids and aa.id in brw.ifrs_id.arap_account_ids._get_children_and_consol():
                    domain = [('account_id', 'in', aa._get_children_and_consol()), 
                              ('date_maturity', '!=', False)] + date_domain
                    aml_ids = self.pool.get('account.move.line').search(cr, uid, domain)
                    if brw.value in ('debit', 'balance'):
                        res += sum([aml.amount_residual if aml.amount_residual > 0 else 0 for aml in self.pool.get('account.move.line').browse(cr, uid, aml_ids)])
                    else:
                        res += sum([-aml.amount_residual if aml.amount_residual < 0 else 0 for aml in self.pool.get('account.move.line').browse(cr, uid, aml_ids)])

                if brw.ifrs_id.forecast_ids:
                    domain = [('account_id', 'in', aa._get_children_and_consol()),
                              ('forecast_id', 'in', [f.id for f in brw.ifrs_id.forecast_ids])] + date_domain
                    afl_ids = self.pool.get('account.forecast.line').search(cr, uid, domain)
                    if brw.value in ('debit', 'balance'):
                        res += sum([afl.amount if afl.amount > 0 else 0 for afl in self.pool.get('account.forecast.line').browse(cr, uid, afl_ids)])
                    else:
                        res += sum([-afl.amount if afl.amount < 0 else 0 for afl in self.pool.get('account.forecast.line').browse(cr, uid, afl_ids)])
        return res

    def _get_logical_operation(self, cr, uid, brw, ilf, irg, context=None):
        def result(brw, ifn, ilf, irg):
            if getattr(brw, ifn) == 'subtract':
                res = ilf - irg
            elif getattr(brw, ifn) == 'addition':
                res = ilf + irg
            elif getattr(brw, ifn) == 'lf':
                res = ilf
            elif getattr(brw, ifn) == 'rg':
                res = irg
            elif getattr(brw, ifn) == 'zr':
                res = 0.0
            return res

        context = dict(context or {})
        fnc = getattr(op, brw.logical_operation)

        if fnc(ilf, irg):
            res = result(brw, 'logical_true', ilf, irg)
        else:
            res = result(brw, 'logical_false', ilf, irg)
        return res

    def _get_grand_total(
            self, cr, uid, ids, number_month=None, one_per=False, bag=None,
            context=None):
        """ Calculates the amount sum of the line type == 'total'
        @param number_month: periodo a calcular
        """
        fy_obj = self.pool.get('account.fiscalyear')
        context = context and dict(context) or {}
        cx = context.copy()
        res = 0.0

        if not cx.get('fiscalyear'):
            cx['fiscalyear'] = fy_obj.find(cr, uid)

        brw = self.browse(cr, uid, ids)
        res = self._get_sum_total(
            cr, uid, brw, 'total_ids', number_month, one_per=one_per, bag=bag,
            context=cx)

        if brw.operator in ('subtract', 'condition', 'percent', 'ratio',
                            'product'):
            so = self._get_sum_total(
                cr, uid, brw, 'operand_ids', number_month, one_per=one_per,
                bag=bag, context=cx)
            if brw.operator == 'subtract':
                res -= so
            elif brw.operator == 'condition':
                res = self._get_logical_operation(cr, uid, brw, res, so,
                                                  context=cx)
            elif brw.operator == 'percent':
                res = so != 0 and (100 * res / so) or 0.0
            elif brw.operator == 'ratio':
                res = so != 0 and (res / so) or 0.0
            elif brw.operator == 'product':
                res = res * so
        return res

    def _get_constant(self, cr, uid, ids=None, number_month=None,
                      context=None):
        """ Calculates the amount sum of the line of constant
        @param number_month: periodo a calcular
        """
        cx = context or {}
        brw = self.browse(cr, uid, ids, context=cx)
        if brw.constant_type == 'constant':
            return brw.constant
        fy_obj = self.pool.get('account.fiscalyear')
        period_obj = self.pool.get('account.period')

        if not cx.get('fiscalyear'):
            cx['fiscalyear'] = fy_obj.find(cr, uid, dt=None, context=cx)

        if not cx.get('period_from', False) and not cx.get('period_to', False):
            if context.get('whole_fy', False):
                cx['period_from'] = period_obj.find_special_period(
                    cr, uid, cx['fiscalyear'])
            cx['period_to'] = period_obj.search(
                cr, uid, [('fiscalyear_id', '=', cx['fiscalyear'])])[-1]

        if brw.constant_type == 'period_days':
            res = period_obj._get_period_days(
                cr, uid, cx['period_from'], cx['period_to'])
        elif brw.constant_type == 'fy_periods':
            res = fy_obj._get_fy_periods(cr, uid, cx['fiscalyear'])
        elif brw.constant_type == 'fy_month':
            res = fy_obj._get_fy_month(cr, uid, cx[
                                       'fiscalyear'], cx['period_to'])
        elif brw.constant_type == 'number_customer':
            res = self._get_number_customer_portfolio(cr, uid, ids, cx[
                'fiscalyear'], cx['period_to'], cx)
        return res

    def exchange(self, cr, uid, ids, from_amount, to_currency_id,
                 from_currency_id, exchange_date, context=None):
        context = context and dict(context) or {}
        if from_currency_id == to_currency_id:
            return from_amount
        curr_obj = self.pool.get('res.currency')
        context['date'] = exchange_date
        return curr_obj.compute(cr, uid, from_currency_id, to_currency_id,
                                from_amount, context=context)

    def _get_amount_value(
            self, cr, uid, ids, ifrs_line=None, period_info=None,
            fiscalyear=None, exchange_date=None, currency_wizard=None,
            number_month=None, target_move=None, pdx=None, undefined=None,
            two=None, one_per=False, bag=None, context=None):
        """ Returns the amount corresponding to the period of fiscal year
        @param ifrs_line: linea a calcular monto
        @param period_info: informacion de los periodos del fiscal year
        @param fiscalyear: selected fiscal year
        @param exchange_date: date of change currency
        @param currency_wizard: currency in the report
        @param number_month: period number
        @param target_move: target move to consider
        """

        context = context and dict(context) or {}
        # TODO: Current Company's Currency shall be used: the one on wizard
        from_currency_id = ifrs_line.ifrs_id.company_id.currency_id.id
        to_currency_id = currency_wizard

        if number_month:
            if two:
                context = {
                    'period_from': number_month, 'period_to': number_month}
            else:
                period_id = period_info[number_month][1]
                context = {'period_from': period_id, 'period_to': period_id}
        else:
            context = {'whole_fy': True}

        # NOTE: This feature is not yet been implemented
        # context['partner_detail'] = pdx
        context['fiscalyear'] = fiscalyear
        context['state'] = target_move

        if ifrs_line.type == 'detail':
            res = self._get_sum_detail(
                cr, uid, ifrs_line.id, number_month,
                context=context)
        elif ifrs_line.type == 'total':
            res = self._get_grand_total(
                cr, uid, ifrs_line.id, number_month,
                one_per=one_per, bag=bag, context=context)
        elif ifrs_line.type == 'constant':
            res = self._get_constant(cr, uid, ifrs_line.id, number_month,
                                     context=context)
        else:
            res = 0.0

        if ifrs_line.type == 'detail':
            res = self.exchange(
                cr, uid, ids, res, to_currency_id, from_currency_id,
                exchange_date, context=context)
        return res

    def _get_dict_amount_with_operands(
            self, cr, uid, ids, ifrs_line, period_info=None, fiscalyear=None,
            exchange_date=None, currency_wizard=None, month_number=None,
            target_move=None, pdx=None, undefined=None, two=None,
            one_per=False, bag=None, context=None):
        """ Integrate operand_ids field in the calculation of the amounts for
        each line
        @param ifrs_line: linea a calcular monto
        @param period_info: informacion de los periodos del fiscal year
        @param fiscalyear: selected fiscal year
        @param exchange_date: date of change currency
        @param currency_wizard: currency in the report
        @param month_number: period number
        @param target_move: target move to consider
        """

        context = dict(context or {})

        direction = ifrs_line.inv_sign and -1.0 or 1.0

        res = {}
        for number_month in range(1, 13):
            field_name = 'period_%(month)s' % dict(month=number_month)
            bag[ifrs_line.id][field_name] = self._get_amount_value(
                cr, uid, ids, ifrs_line, period_info, fiscalyear,
                exchange_date, currency_wizard, number_month, target_move, pdx,
                undefined, two, one_per=one_per, bag=bag,
                context=context) * direction
            res[number_month] = bag[ifrs_line.id][field_name]

        return res

    def _get_amount_with_operands(
            self, cr, uid, ids, ifrs_l, period_info=None, fiscalyear=None,
            exchange_date=None, currency_wizard=None, number_month=None,
            target_move=None, pdx=None, undefined=None, two=None,
            one_per=False, bag=None, context=None):
        """ Integrate operand_ids field in the calculation of the amounts for
        each line
        @param ifrs_line: linea a calcular monto
        @param period_info: informacion de los periodos del fiscal year
        @param fiscalyear: selected fiscal year
        @param exchange_date: date of change currency
        @param currency_wizard: currency in the report
        @param number_month: period number
        @param target_move: target move to consider
        """

        context = context and dict(context) or {}

        if not number_month:
            context = {'whole_fy': True}

        res = self._get_amount_value(
            cr, uid, ids, ifrs_l, period_info, fiscalyear, exchange_date,
            currency_wizard, number_month, target_move, pdx, undefined, two,
            one_per=one_per, bag=bag, context=context)

        res = ifrs_l.inv_sign and (-1.0 * res) or res
        bag[ifrs_l.id]['ytd'] = res

        return res

    def _get_number_customer_portfolio(self, cr, uid, ids, fyr, period,
                                       context=None):
        ifrs_brw = self.browse(cr, uid, ids, context=context)
        company_id = ifrs_brw.ifrs_id.company_id.id
        if context.get('whole_fy', False):
            period_fy = [('period_id.fiscalyear_id', '=', fyr),
                         ('period_id.special', '=', False)]
        else:
            period_fy = [('period_id', '=', period)]
        invoice_obj = self.pool.get('account.invoice')
        invoice_ids = invoice_obj.search(cr, uid, [
            ('type', '=', 'out_invoice'),
            ('state', 'in', ('open', 'paid',)),
            ('company_id', '=', company_id)] + period_fy)
        partner_number = \
            set([inv.partner_id.id for inv in
                 invoice_obj.browse(cr, uid, invoice_ids, context=context)])
        return len(list(partner_number))

    def onchange_sequence(self, cr, uid, ids, sequence, context=None):
        context = context and dict(context) or {}
        return {'value': {'priority': sequence}}

    @api.returns('self')
    def _get_default_help_bool(self):
        ctx = dict(self._context)
        return ctx.get('ifrs_help', True)

    @api.returns('self')
    def _get_default_sequence(self):
        ctx = dict(self._context)
        res = 0
        if not ctx.get('ifrs_id'):
            return res + 10
        ifrs_lines_ids = self.search([('ifrs_id', '=', ctx['ifrs_id'])])
        if ifrs_lines_ids:
            res = max(line.sequence for line in ifrs_lines_ids)
        return res + 10

    def onchange_type_without(self, cr, uid, ids, ttype, operator,
                              context=None):
        context = context and dict(context) or {}
        res = {}
        if ttype == 'total' and operator == 'without':
            res = {'value': {'operand_ids': []}}
        return res

    def write(self, cr, uid, ids, vals, context=None):
        ids = isinstance(ids, (int, long)) and [ids] or ids
        res = super(IfrsLines, self).write(cr, uid, ids, vals)
        for ifrs_line in self.pool.get('ifrs.lines').browse(cr, uid, ids):
            if ifrs_line.type == 'total' and ifrs_line.operator == 'without':
                vals['operand_ids'] = [(6, 0, [])]
                super(IfrsLines, self).write(cr, uid, ifrs_line.id, vals)
        return res

    help = fields.Boolean(
        string='Show Help', copy=False, related='ifrs_id.help',
        default=_get_default_help_bool,
        help='Allows you to show the help in the form')
    # Really!!! A repeated field with same functionality! This was done due
    # to the fact that web view everytime that sees sequence tries to allow
    # you to change the values and this feature here is undesirable.
    sequence = fields.Integer(
        string='Sequence', default=_get_default_sequence,
        help=('Indicates the order of the line in the report. The sequence '
              'must be unique and unrepeatable'))
    priority = fields.Integer(
        string='Sequence', default=_get_default_sequence, related='sequence',
        help=('Indicates the order of the line in the report. The sequence '
              'must be unique and unrepeatable'))
    name = fields.Char(
        string='Name', size=128, required=True, translate=True,
        help=('Line name in the report. This name can be translatable, if '
              'there are multiple languages loaded it can be translated'))
    type = fields.Selection(
        [('abstract', 'Abstract'),
         ('detail', 'Detail'),
         ('constant', 'Constant'),
         ('total', 'Total')],
        string='Type', required=True, default='abstract',
        help=('Line type of report:  \n-Abstract(A),\n-Detail(D), '
              '\n-Constant(C),\n-Total(T)'))
    constant = fields.Float(
        string='Constant',
        help=('Fill this field with your own constant that will be used '
              'to compute in your other lines'),
        readonly=False)
    constant_type = fields.Selection(
        [('constant', 'My Own Constant'),
         ('period_days', 'Days of Period'),
         ('fy_periods', "FY's Periods"),
         ('fy_month', "FY's Month"),
         ('number_customer', "Number of customers* in portfolio")],
        string='Constant Type',
        required=False,
        help='Constant Type')
    ifrs_id = fields.Many2one(
        'ifrs.ifrs', string='IFRS',
        ondelete='cascade',
        required=True)
    company_id = fields.Many2one(
        'res.company', string='Company', related='ifrs_id.company_id',
        store=True)
    amount = fields.Float(
        string='Amount', readonly=True,
        help=('This field will update when you click the compute button in '
              'the IFRS doc form'))
    cons_ids = fields.Many2many(
        'account.account', 'ifrs_account_rel', 'ifrs_lines_id', 'account_id',
        string='Consolidated Accounts')
    journal_ids = fields.Many2many(
        'account.journal', 'ifrs_journal_rel', 'ifrs_lines_id', 'journal_id',
        string='Journals', required=False)
    analytic_ids = fields.Many2many(
        'account.analytic.account', 'ifrs_analytic_rel', 'ifrs_lines_id',
        'analytic_id', string='Consolidated Analytic Accounts')
    partner_ids = fields.Many2many(
        'res.partner', 'ifrs_partner_rel', 'ifrs_lines_id',
        'partner_id', string='Partners')
    tax_code_ids = fields.Many2many(
        'account.tax.code', 'ifrs_tax_rel', 'ifrs_lines_id',
        'tax_code_id', string='Tax Codes')
    filter_id = fields.Many2one(
        'ir.filters', string='Custom Filter',
        ondelete='set null',
        domain=("[('model_id','=','account.move.line')]"))
    parent_id = fields.Many2one(
        'ifrs.lines', string='Parent',
        ondelete='set null',
        domain=("[('ifrs_id','=',parent.id),"
                "('type','=','total'),('id','!=',id)]"))
    operand_ids = fields.Many2many(
        'ifrs.lines', 'ifrs_operand_rel', 'ifrs_parent_id', 'ifrs_child_id',
        string='Second Operand')
    operator = fields.Selection(
        [('subtract', 'Subtraction'),
         ('condition', 'Conditional'),
         ('percent', 'Percentage'),
         ('ratio', 'Ratio'),
         ('product', 'Product'),
         ('without', 'First Operand Only')],
        string='Operator', required=False,
        default='without',
        help='Leaving blank will not take into account Operands')
    logical_operation = fields.Selection(
        LOGICAL_OPERATIONS,
        string='Logical Operations', required=False,
        help=('Select type of Logical Operation to perform with First '
              '(Left) and Second (Right) Operand'))
    logical_true = fields.Selection(
        LOGICAL_RESULT,
        string='Logical True', required=False,
        help=('Value to return in case Comparison is True'))
    logical_false = fields.Selection(
        LOGICAL_RESULT,
        string='Logical False', required=False,
        help=('Value to return in case Comparison is False'))
    comparison = fields.Selection(
        [('subtract', 'Subtraction'),
         ('percent', 'Percentage'),
         ('ratio', 'Ratio'),
         ('without', 'No Comparison')],
        string='Make Comparison', required=False,
        default='without',
        help=('Make a Comparison against the previous period.\nThat is, '
              'period X(n) minus period X(n-1)\nLeaving blank will not '
              'make any effects'))
    acc_val = fields.Selection(
        [('init', 'Initial Values'),
         ('var', 'Variation in Periods'),
         ('fy', ('Ending Values'))],
        string='Accounting Span', required=False,
        default='fy',
        help='Leaving blank means YTD')
    value = fields.Selection(
        [('debit', 'Debit'),
         ('credit', 'Credit'),
         ('balance', 'Balance')],
        string='Accounting Value', required=False,
        default='balance',
        help='Leaving blank means Balance')
    total_ids = fields.Many2many(
        'ifrs.lines', 'ifrs_lines_rel', 'parent_id', 'child_id',
        string='First Operand')
    inv_sign = fields.Boolean(
        string='Change Sign to Amount', default=False, copy=True,
        help='Allows you to show the help in the form')
    invisible = fields.Boolean(
        string='Invisible', default=False, copy=True,
        help='Allows whether the line of the report is printed or not')
    comment = fields.Text(
        string='Comments/Question',
        help='Comments or questions about this ifrs line')

#     _sql_constraints = [
#         ('sequence_ifrs_id_unique', 'unique(ifrs_id)',
#          'The sequence already have been set in another IFRS line')]

    def _get_level(self, cr, uid, lll, tree, level=1, context=None):
        """ Calcula los niveles de los ifrs.lines, tomando en cuenta que sera
        un mismo arbol para los campos total_ids y operand_ids.
        @param lll: objeto a un ifrs.lines
        @param level: Nivel actual de la recursion
        @param tree: Arbol de dependencias entre lineas construyendose
        """
        context = context and dict(context) or {}
        if not tree.get(level):
            tree[level] = {}
        # The search through level should be backwards from the deepest level
        # to the outmost level
        levels = tree.keys()
        levels.sort()
        levels.reverse()
        xlevel = False
        for nnn in levels:
            xlevel = isinstance(tree[nnn].get(lll.id), (set)) and nnn or xlevel
        if not xlevel:
            tree[level][lll.id] = set()
        elif xlevel < level:
            tree[level][lll.id] = tree[xlevel][lll.id]
            del tree[xlevel][lll.id]
        else:  # xlevel >= level
            return True
        for jjj in set(lll.total_ids + lll.operand_ids):
            tree[level][lll.id].add(jjj.id)
            self._get_level(cr, uid, jjj, tree, level + 1, context=context)
        return True
Exemplo n.º 10
0
class ResPartnerCategory(osv.osv):
    _name = "res.partner.category"

    name = fields.Char("Name", size=50, required=True)
Exemplo n.º 11
0
class SaleOrder(models.Model):
    _inherit = 'sale.order'

    notify_approval = fields.Char(
        string=_(u'Notify approval'),
        size=100,
    )

    date_delivery = fields.Date(
        string=_(u'Date delivery'),
        default=fields.Date.today,
    )

    date_reception = fields.Date(string=_(u'Date reception'),
                                 #default=fields.Date.today,
                                 )

    total_net_sale = fields.Float(string=_(u'Total net sale'),
                                  digits_compute=dp.get_precision('Account'),
                                  compute='_compute_profit_margin',
                                  store=True)

    perc_freight = fields.Float(
        string=_(u'Freight percentage'),
        digits_compute=dp.get_precision('Account'),
    )

    total_freight = fields.Float(string=_(u'Total Freight'),
                                 digits_compute=dp.get_precision('Account'),
                                 compute='_compute_profit_margin',
                                 store=True)

    perc_installation = fields.Float(
        string=_(u'installation percentage'),
        digits_compute=dp.get_precision('Account'),
    )

    total_installation = fields.Float(
        string=_(u'Total installation'),
        digits_compute=dp.get_precision('Account'),
        compute='_compute_profit_margin',
        store=True)

    profit_margin = fields.Float(string=_(u'Profit margin'),
                                 digits_compute=dp.get_precision('Account'),
                                 compute='_compute_profit_margin',
                                 store=True)

    not_be_billed = fields.Boolean(string=_(u'not be billed'), )

    no_facturar = fields.Boolean(string=_(u'No Facturar'), )

    manufacture = fields.Selection(
        [('special', _(u'Special')), ('line', _(u'Line')),
         ('replenishment', _(u'Replenishment')),
         ('semi_special', _(u'Semi special'))],
        string=_(u"Manufacture"),
    )

    executive = fields.Char(
        string=_(u'Executive'),
        size=100,
    )

    respo_reple = fields.Char(
        string=_(u'Responsible of replenishment'),
        size=200,
    )

    priority = fields.Selection(
        [('high', _(u'High')), ('replenishment', _(u'Replenishment')),
         ('express', _(u'Express')), ('sample', _(u'Sample'))],
        _(u'Manufacturing priority'),
    )

    complement_saleorder_id = fields.Many2one(
        'sale.order',
        string=_(u'In complement:'),
        help=_(u'Displays a list of sales orders'),
    )

    manufacturing_observations = fields.Text(
        string=_(u'Observations Manufacturing'), )

    replenishing_motif = fields.Text(
        string=_(u'Reason for the replenishment'), )

    credit_status = fields.Selection(
        [('normal', _(u'Normal')),
         ('suspended', _(u'Suspended for Collection')),
         ('conditioned', _(u'Conditioned'))],
        _(u'Credit status'),
    )

    credit_note = fields.Text(string=_(u'Note Credit and Collections'), )

    date_production = fields.Date(string=_('Date of Production Termination'), )

    approve = fields.Selection(
        [('approved', _('Approved')),
         ('suggested', _('Suggested for Approval')),
         ('not_approved', _('Not Approved'))],
        default='not_approved',
        string=_('Approve Status'),
        store=True,
        copy=False,
    )

    total_cost = fields.Float(string=_('Total cost'),
                              compute='_compute_profit_margin',
                              store=True)

    sale_picking_adm = fields.Boolean(string=_(u'Admin Sale Picking'), )

    webiste_operator = fields.Boolean(string=_('Captured by Operator'), )

    date_suggested = fields.Datetime(string=_('Suggestion Date Approval'),
                                     copy=False,
                                     help=_('Suggestion Date Approval.'))

    date_approved = fields.Datetime(string=_('Credit Release Date'),
                                    copy=False,
                                    help=_('Credit Release Date.'))

    _sql_constraints = [
        ('name_unique', 'UNIQUE(name)', "The order name must be unique"),
    ]

    @api.onchange('partner_id')
    def _onchange_partner_id(self):
        self.webiste_operator = False
        self.notify_approval = False
        if self.partner_id:
            self.webiste_operator = True
        if self.partner_id.notify_approval:
            self.notify_approval = self.partner_id.notify_approval

    @api.depends('order_line.net_sale')
    def _compute_profit_margin(self):
        for order in self:
            global_cost = 0.0
            global_net_sale = 0.0
            global_freight = 0.0
            global_installa = 0.0
            global_profit_margin = 0.0
            currency = order.company_id.currency_id
            for line in order.order_line:
                global_cost += line.standard_cost
                global_net_sale += line.net_sale
                global_freight += line.freight_amount
                global_installa += line.installation_amount
            if global_net_sale > 0.000000:
                global_total_pm = currency.compute(
                    global_cost, order.pricelist_id.currency_id)
                global_profit_margin = (1 -
                                        (global_total_pm) / global_net_sale)
                global_profit_margin = global_profit_margin * 100

            order.total_cost = global_cost
            order.total_net_sale = global_net_sale
            order.total_freight = global_freight
            order.total_installation = global_installa
            order.profit_margin = global_profit_margin

    @api.multi
    @api.onchange('project_id')
    def onchange_project_id(self):
        """
        Trigger the change of warehouse when the analytic account is modified.
        """
        if self.project_id and self.project_id.warehouse_id:
            self.warehouse_id = self.project_id.warehouse_id
        return {}

    @api.multi
    def action_confirm(self):
        for order in self:
            if order.company_id.is_manufacturer:
                order.validate_manufacturing()
                if not order.notify_approval:
                    raise UserError(
                        _('The following field is not invalid:\nNotify approval'
                          ))
                if not order.manufacture:
                    raise UserError(
                        _('The following field is not invalid:\nManufacture'))
                # if not order.executive:
                #     raise UserError(
                #         _('The following field is not invalid:\nExecutive'))
                if not order.priority:
                    raise UserError(
                        _('The following field is not invalid:\nManufacturing \
                          priority'))
                if not order.project_id:
                    raise UserError(
                        _('The following field is not invalid:\nAnalytic Account'
                          ))
                if not order.client_order_ref:
                    raise UserError(_('This Sale Order not has OC captured'))
                if not order.date_reception:
                    raise UserError(
                        _('This Sale Order not has Date Reception'))
                for line in order.order_line:
                    if not line.route_id:
                        raise UserError(
                            _('Product line %s does not have a route assigned'
                              % (line.product_id.default_code)))
                    if line.standard_cost == 0.00:
                        raise UserError(
                            "No se puede validar un producto con costo 0 (%s)"
                            % (line.product_id.default_code))
                # Comented toda vez que ya hay un modulo de
                # PLM que considera productos cotizacion:
                # for line in order.order_line:
                #     if line.product_id.quotation_product:
                #         raise UserError(_('The Product contains Quotation'))
            if order.warehouse_id != order.project_id.warehouse_id:
                raise UserError(
                    ('No coincide la Analítica con el almacen seleccionado'))
        return super(SaleOrder, self).action_confirm()

    @api.multi
    def validate_manufacturing(self):
        for order in self:

            # pending = self.env['sale.order'].search(
            # [('state', '=', 'draft')])
            # dife = 0.0
            # dife = order.amount_total - order.total_nste
            # if order.total_nste > 0.0000000:
            #     if abs(dife) > 0.6000:
            #         raise UserError(
            #             _('The amount are differents:\nAnalytic Account'))

            for line in order.order_line:
                if line.product_id:
                    routes = line.product_id.route_ids + \
                        line.product_id.categ_id.total_route_ids
                    if line.product_id.type == 'service':
                        continue
                    if len(routes) < 2:
                        raise UserError(
                            _('%s %s %s' %
                              (_("The next product has no a valid Route"),
                               line.product_id.default_code,
                               line.product_id.name)))
                    product_bom = False
                    for bom in line.product_id.product_tmpl_id.bom_ids:
                        if bom.product_id.id == line.product_id.id:
                            product_bom = bom or False
                    if not product_bom:
                        raise UserError(
                            _('%s %s %s' %
                              (_("The next product has no a Bill of Materials"
                                 ), line.product_id.default_code,
                               line.product_id.name)))

                    # if not line.product_id.product_service_id:
                    #     raise UserError(
                    #         _('%s %s %s' % (
                    #             _("The next product has not a SAT Code: "),
                    #             line.product_id.default_code, line.product_id.name)))

        return True

    @api.multi
    def approve_action(self):
        for order in self:
            if order.approve == 'approved':
                raise UserError(_('This Sale Order is already approved'))
            if order.create_uid.id == self.env.uid:
                if order.manufacture != 'replenishment' or \
                   order.priority != 'replenishment':
                    raise UserError(
                        _('Este no es un Pedido de Reposición solicita \
                    la aprobación de Credito y Cobranza.'))
                else:
                    order.write({'approve': 'approved'})
                    order.date_approved = fields.Datetime.now()
                    return
            if not self.env.user.has_group('account.group_account_manager'):
                raise ValidationError(
                    _('Este no es un Pedido de Reposición solicita \
                    la aprobación de Credito y Cobranza.'))
            order.write({'approve': 'approved'})
            order.date_approved = fields.Datetime.now()

        # resws = super(SaleOrder, self)._product_data_validation()

        return True

    @api.multi
    def suggested_action(self):
        for order in self:
            if order.approve == 'suggested':
                raise UserError(
                    _('This Sale Order is already Suggested for Approval'))
            if not order.order_line:
                raise UserError(_('This Sale Order not has Products Captured'))
            if not order.client_order_ref:
                raise UserError(_('This Sale Order not has OC captured'))
            if order.partner_id.parent_id:
                order.partner_id = order.partner_id.parent_id
            order.write({'approve': 'suggested'})
            order.date_suggested = fields.Datetime.now()

            if order.company_id.is_manufacturer:
                # resws = order._product_data_validation()
                resws2 = order.product_data_validation2()
        # if resws[0] != 'OK':
        #     raise ValidationError('Este pedido no podra ser aprobado  \
        #         debido a errores de configuracion \
        #         en los productos que ocasionarian \
        #         excepciones, se ha enviado un correo detallado a los \
        #         interesados.')

        return True

    @api.multi
    def _prepare_invoice(self):
        invoice_vals = super(SaleOrder, self)._prepare_invoice()
        invoice_vals['perc_freight'] = self.perc_freight
        invoice_vals['perc_installation'] = self.perc_installation
        # invoice_vals['executive'] = self.executive
        invoice_vals['journal_id'] = self.project_id.journal_sale_id.id
        invoice_vals['manufacture'] = self.manufacture

        return invoice_vals

    @api.multi
    def action_done(self):
        super(SaleOrder, self).action_done()

        # commented temporary til implementatio of CRM
        self.force_quotation_send()

    @api.multi
    def force_quotation_send(self):
        for order in self:
            email_act = order.action_quotation_send()
            if email_act and email_act.get('context'):
                email_ctx = email_act['context']
                notify = order.notify_approval
                email_ctx.update(default_email_to=notify)
                email_ctx.update(default_email_from=order.company_id.email)
                order.with_context(email_ctx).message_post_with_template(
                    email_ctx.get('default_template_id'))
        return True
Exemplo n.º 12
0
class AccountInvoice(models.Model):
    _inherit = "account.invoice"

    main_id_number = fields.Char(
        related='commercial_partner_id.main_id_number',
        readonly=True,
    )
    state_id = fields.Many2one(
        related='commercial_partner_id.state_id',
        store=True,
        readonly=True,
    )
    currency_rate = fields.Float(
        string='Currency Rate',
        copy=False,
        digits=(16, 4),
        # TODO make it editable, we have to change move create method
        readonly=True,
    )
    document_letter_id = fields.Many2one(
        related='document_type_id.document_letter_id',
        readonly=True,
    )
    document_letter_name = fields.Char(
        related='document_letter_id.name',
        readonly=True,
    )
    taxes_included = fields.Boolean(
        related='document_letter_id.taxes_included',
        readonly=True,
    )
    afip_responsability_type_id = fields.Many2one(
        'afip.responsability.type',
        string='AFIP Responsability Type',
        readonly=True,
        copy=False,
    )
    invoice_number = fields.Integer(
        compute='_get_invoice_number',
        string="Invoice Number",
    )
    point_of_sale_number = fields.Integer(
        compute='_get_invoice_number',
        string="Point Of Sale",
    )
# impuestos e importes de impuestos
    # todos los impuestos tipo iva (es un concepto mas bien interno)
    vat_tax_ids = fields.One2many(
        compute="_get_argentina_amounts",
        comodel_name='account.invoice.tax',
        string='VAT Taxes'
    )
    # todos los impuestos iva que componene base imponible (no se incluyen 0,
    # 1, 2 que no son impuesto en si)
    vat_taxable_ids = fields.One2many(
        compute="_get_argentina_amounts",
        comodel_name='account.invoice.tax',
        string='VAT Taxes'
    )
    # todos los impuestos menos los tipo iva vat_tax_ids
    not_vat_tax_ids = fields.One2many(
        compute="_get_argentina_amounts",
        comodel_name='account.invoice.tax',
        string='Not VAT Taxes'
    )
    # suma de base para todos los impuestos tipo iva
    vat_base_amount = fields.Monetary(
        compute="_get_argentina_amounts",
        string='VAT Base Amount'
    )
    # base imponible (no se incluyen 0, exento y no gravado)
    vat_taxable_amount = fields.Monetary(
        compute="_get_argentina_amounts",
        string='VAT Taxable Amount'
    )
    # base iva exento
    vat_exempt_base_amount = fields.Monetary(
        compute="_get_argentina_amounts",
        string='VAT Exempt Base Amount'
    )
    # base iva no gravado
    vat_untaxed_base_amount = fields.Monetary(
        compute="_get_argentina_amounts",
        string='VAT Untaxed Base Amount'
    )
    # importe de iva
    vat_amount = fields.Monetary(
        compute="_get_argentina_amounts",
        string='VAT Amount'
    )
    # importe de otros impuestos
    other_taxes_amount = fields.Monetary(
        compute="_get_argentina_amounts",
        string='Other Taxes Amount'
    )
    afip_incoterm_id = fields.Many2one(
        'afip.incoterm',
        'Incoterm',
        readonly=True,
        states={'draft': [('readonly', False)]}
    )
    point_of_sale_type = fields.Selection(
        related='journal_id.point_of_sale_type',
        readonly=True,
    )
    # estos campos los agregamos en este modulo pero en realidad los usa FE
    # pero entendemos que podrian ser necesarios para otros tipos, por ahora
    # solo lo vamos a hacer requerido si el punto de venta es del tipo
    # electronico
    # TODO mejorar, este concepto deberia quedar fijo y no poder modificarse
    # una vez validada, cosa que pasaria por ej si cambias el producto
    afip_concept = fields.Selection(
        compute='_get_concept',
        # store=True,
        selection=[('1', 'Producto / Exportación definitiva de bienes'),
                   ('2', 'Servicios'),
                   ('3', 'Productos y Servicios'),
                   ('4', '4-Otros (exportación)'),
                   ],
        string="AFIP concept",
    )
    afip_service_start = fields.Date(
        string='Service Start Date',
        readonly=True,
        states={'draft': [('readonly', False)]},
    )
    afip_service_end = fields.Date(
        string='Service End Date',
        readonly=True,
        states={'draft': [('readonly', False)]},
    )

    @api.one
    def _get_argentina_amounts(self):
        """
        """
        vat_taxes = self.tax_line_ids.filtered(
            lambda r: (
                r.tax_id.tax_group_id.type == 'tax' and
                r.tax_id.tax_group_id.tax == 'vat'))
        # we add and "r.base" because only if a there is a base amount it is
        # considered taxable, this is used for eg to validate invoices on afip
        vat_taxables = vat_taxes.filtered(
            lambda r: (
                r.tax_id.tax_group_id.afip_code not in [0, 1, 2]) and r.base)

        vat_amount = sum(vat_taxes.mapped('amount'))
        self.vat_tax_ids = vat_taxes
        self.vat_taxable_ids = vat_taxables
        self.vat_amount = vat_amount
        # self.vat_taxable_amount = sum(vat_taxables.mapped('base_amount'))
        self.vat_taxable_amount = sum(vat_taxables.mapped('base'))
        # self.vat_base_amount = sum(vat_taxes.mapped('base_amount'))
        self.vat_base_amount = sum(vat_taxes.mapped('base'))

        # vat exempt values
        # exempt taxes are the ones with code 2
        vat_exempt_taxes = self.tax_line_ids.filtered(
            lambda r: (
                r.tax_id.tax_group_id.type == 'tax' and
                r.tax_id.tax_group_id.tax == 'vat' and
                r.tax_id.tax_group_id.afip_code == 2))
        self.vat_exempt_base_amount = sum(
            vat_exempt_taxes.mapped('base'))
        # self.vat_exempt_base_amount = sum(
        #     vat_exempt_taxes.mapped('base_amount'))

        # vat_untaxed_base_amount values (no gravado)
        # vat exempt taxes are the ones with code 1
        vat_untaxed_taxes = self.tax_line_ids.filtered(
            lambda r: (
                r.tax_id.tax_group_id.type == 'tax' and
                r.tax_id.tax_group_id.tax == 'vat' and
                r.tax_id.tax_group_id.afip_code == 1))
        self.vat_untaxed_base_amount = sum(
            vat_untaxed_taxes.mapped('base'))
        # self.vat_untaxed_base_amount = sum(
        #     vat_untaxed_taxes.mapped('base_amount'))

        # other taxes values
        not_vat_taxes = self.tax_line_ids - vat_taxes
        other_taxes_amount = sum(not_vat_taxes.mapped('amount'))
        self.not_vat_tax_ids = not_vat_taxes
        self.other_taxes_amount = other_taxes_amount

    @api.multi
    @api.depends('document_number', 'number')
    def _get_invoice_number(self):
        """ Funcion que calcula numero de punto de venta y numero de factura
        a partir del document number. Es utilizado principalmente por el modulo
        de vat ledger citi
        """
        # TODO mejorar estp y almacenar punto de venta y numero de factura por
        # separado, de hecho con esto hacer mas facil la carga de los
        # comprobantes de compra

        # decidimos obtener esto solamente para comprobantes con doc number
        for rec in self:
            str_number = rec.document_number or False
            if str_number:
                if rec.document_type_id.code in ['33', '99', '331', '332']:
                    point_of_sale = '0'
                    # leave only numbers and convert to integer
                    invoice_number = str_number
                # despachos de importacion
                elif rec.document_type_id.code == '66':
                    point_of_sale = '0'
                    invoice_number = '0'
                elif "-" in str_number:
                    splited_number = str_number.split('-')
                    invoice_number = splited_number.pop()
                    point_of_sale = splited_number.pop()
                elif "-" not in str_number and len(str_number) == 12:
                    point_of_sale = str_number[:4]
                    invoice_number = str_number[-8:]
                else:
                    raise ValidationError(_(
                        'Could not get invoice number and point of sale for '
                        'invoice id %i') % (rec.id))
                rec.invoice_number = int(
                    re.sub("[^0-9]", "", invoice_number))
                rec.point_of_sale_number = int(
                    re.sub("[^0-9]", "", point_of_sale))

    @api.one
    @api.depends(
        'invoice_line_ids',
        'invoice_line_ids.product_id',
        'invoice_line_ids.product_id.type',
        'localization',
    )
    def _get_concept(self):
        afip_concept = False
        if self.point_of_sale_type in ['online', 'electronic']:
            # exportaciones
            invoice_lines = self.invoice_line_ids
            product_types = set(
                [x.product_id.type for x in invoice_lines if x.product_id])
            consumible = set(['consu', 'product'])
            service = set(['service'])
            mixed = set(['consu', 'service', 'product'])
            # default value "product"
            afip_concept = '1'
            if product_types.issubset(mixed):
                afip_concept = '3'
            if product_types.issubset(service):
                afip_concept = '2'
            if product_types.issubset(consumible):
                afip_concept = '1'
            if self.document_type_id.code in ['19', '20', '21']:
                # TODO verificar esto, como par expo no existe 3 y existe 4
                # (otros), considermaos que un mixto seria el otros
                if afip_concept == '3':
                    afip_concept = '4'
        self.afip_concept = afip_concept

    @api.multi
    def get_localization_invoice_vals(self):
        self.ensure_one()
        if self.localization == 'argentina':
            commercial_partner = self.partner_id.commercial_partner_id
            currency = self.currency_id.with_context(
                date=self.date_invoice or fields.Date.context_today(self))
            if self.company_id.currency_id == currency:
                currency_rate = 1.0
            else:
                currency_rate = currency.compute(
                    1., self.company_id.currency_id, round=False)
            return {
                'afip_responsability_type_id': (
                    commercial_partner.afip_responsability_type_id.id),
                'currency_rate': currency_rate,
            }
        else:
            return super(
                AccountInvoice, self).get_localization_invoice_vals()

    @api.model
    def _get_available_journal_document_types(
            self, journal, invoice_type, partner):
        """
        This function search for available document types regarding:
        * Journal
        * Partner
        * Company
        * Documents configuration
        If needed, we can make this funcion inheritable and customizable per
        localization
        """
        if journal.localization != 'argentina':
            return super(
                AccountInvoice, self)._get_available_journal_document_types(
                    journal, invoice_type, partner)

        commercial_partner = partner.commercial_partner_id

        journal_document_types = journal_document_type = self.env[
            'account.journal.document.type']

        if invoice_type in [
                'out_invoice', 'in_invoice', 'out_refund', 'in_refund']:

            if journal.use_documents:
                letters = journal.get_journal_letter(
                    counterpart_partner=commercial_partner)

                domain = [
                    ('journal_id', '=', journal.id),
                    '|',
                    ('document_type_id.document_letter_id', 'in', letters.ids),
                    ('document_type_id.document_letter_id', '=', False),
                ]

                # if invoice_type is refund, only credit notes
                if invoice_type in ['out_refund', 'in_refund']:
                    domain += [
                        ('document_type_id.internal_type',
                            # '=', 'credit_note')]
                            # TODO, check if we need to add tickets and others
                            # also
                            'in', ['credit_note', 'in_document'])]
                # else, none credit notes
                else:
                    domain += [
                        ('document_type_id.internal_type',
                            '!=', 'credit_note')]

                # If internal_type in context we try to serch specific document
                # for eg used on debit notes
                internal_type = self._context.get('internal_type', False)
                if internal_type:
                    journal_document_type = journal_document_type.search(
                        domain + [
                            ('document_type_id.internal_type',
                                '=', internal_type)], limit=1)
                # For domain, we search all documents
                journal_document_types = journal_document_types.search(domain)

                # If not specific document type found, we choose another one
                if not journal_document_type and journal_document_types:
                    journal_document_type = journal_document_types[0]

        if invoice_type == 'in_invoice':
            other_document_types = (commercial_partner.other_document_type_ids)

            domain = [
                ('journal_id', '=', journal.id),
                ('document_type_id',
                    'in', other_document_types.ids),
            ]
            other_journal_document_types = self.env[
                'account.journal.document.type'].search(domain)

            journal_document_types += other_journal_document_types
            # if we have some document sepecific for the partner, we choose it
            if other_journal_document_types:
                journal_document_type = other_journal_document_types[0]

        return {
            'available_journal_document_types': journal_document_types,
            'journal_document_type': journal_document_type,
        }

    @api.multi
    @api.constrains('document_number', 'partner_id', 'company_id')
    def _check_document_number_unique(self):
        for rec in self.filtered(lambda x: x.localization == 'argentina'):
            if rec.document_number:
                domain = [
                    ('type', '=', rec.type),
                    ('document_number', '=', rec.document_number),
                    ('document_type_id', '=', rec.document_type_id.id),
                    ('company_id', '=', rec.company_id.id),
                    ('id', '!=', rec.id)
                ]
                msg = (
                    'Error en factura con id %s: El numero de comprobante (%s)'
                    ' debe ser unico por tipo de documento')
                if rec.type in ['out_invoice', 'out_refund']:
                    # si es factura de cliente entonces tiene que ser numero
                    # unico por compania y tipo de documento
                    rec.search(domain)
                else:
                    # si es factura de proveedor debe ser unica por proveedor
                    domain += [
                        ('partner_id.commercial_partner_id', '=',
                            rec.commercial_partner_id.id)]
                    msg += ' y proveedor'
                if rec.search(domain):
                    raise ValidationError(msg % (rec.id, rec.document_number))

    @api.multi
    def action_move_create(self):
        """
        We add currency rate on move creation so it can be used by electronic
        invoice later on action_number
        """
        self.check_argentinian_invoice_taxes()
        return super(AccountInvoice, self).action_move_create()

    @api.multi
    def check_argentinian_invoice_taxes(self):
        """
        We make theis function to be used as a constraint but also to be called
        from other models like vat citi
        """
        # only check for argentinian localization companies
        _logger.info('Running checks related to argentinian documents')

        # we consider argentinian invoices the ones from companies with
        # localization localization and that belongs to a journal with
        # use_documents
        argentinian_invoices = self.filtered(
            lambda r: (
                r.localization == 'argentina' and r.use_documents))
        if not argentinian_invoices:
            return True

        # check partner has responsability so it will be assigned on invoice
        # validate
        without_responsability = argentinian_invoices.filtered(
            lambda x: not x.commercial_partner_id.afip_responsability_type_id)
        if without_responsability:
            raise ValidationError(_(
                'The following invoices has a partner without AFIP '
                'responsability: %s' % without_responsability.ids))

        # we check all invoice tax lines has tax_id related
        # we exclude exempt vats and untaxed (no gravados)
        wihtout_tax_id = argentinian_invoices.mapped('tax_line_ids').filtered(
            lambda r: not r.tax_id)
        if wihtout_tax_id:
            raise ValidationError(_(
                "Some Invoice Tax Lines don't have a tax_id asociated, please "
                "correct them or try to refresh invoice "))

        # check codes has argentinian tax attributes configured
        tax_groups = argentinian_invoices.mapped(
            'tax_line_ids.tax_id.tax_group_id')
        unconfigured_tax_groups = tax_groups.filtered(
            lambda r: not r.type or not r.tax or not r.application)
        if unconfigured_tax_groups:
            raise ValidationError(_(
                "You are using argentinian localization and there are some tax"
                " groups that are not configured. Tax Groups (id): %s" % (
                    ', '.join(unconfigured_tax_groups.mapped(
                        lambda x: '%s (%s)' % (x.name, x.id))))))

        vat_taxes = self.env['account.tax'].search([
            ('tax_group_id.type', '=', 'tax'),
            ('tax_group_id.tax', '=', 'vat')])
        lines_without_vat = self.env['account.invoice.line'].search([
            ('invoice_id', 'in', argentinian_invoices.ids),
            ('invoice_line_tax_ids', 'not in', vat_taxes.ids)])
        if lines_without_vat:
            raise ValidationError(_(
                "Invoice with ID %s has some lines without vat Tax ") % (
                    lines_without_vat.mapped('invoice_id').ids))
        # for invoice in argentinian_invoices:
        #     # TODO usar round
        #     # TODO tal vez debamos usar esto para un chequeo de suma de
        #     # importes y demas, tener en cuenta caso de importaciones
        #     # tal como esta este chequeo da error si se agregan impuestos
        #     # manuales
        #     if abs(invoice.vat_base_amount - invoice.amount_untaxed) > 0.1:
        #         raise ValidationError(_(
        #             "Invoice with ID %i has some lines without vat Tax ") % (
        #                 invoice.id))

        # Check except vat invoice
        afip_exempt_codes = ['Z', 'X', 'E', 'N', 'C']
        for invoice in argentinian_invoices:
            special_vat_taxes = invoice.tax_line_ids.filtered(
                lambda r: r.tax_id.tax_group_id.afip_code in [1, 2, 3])
            if (
                    special_vat_taxes and
                    invoice.fiscal_position_id.afip_code
                    not in afip_exempt_codes):
                raise ValidationError(_(
                    "If you have choose a 0, exempt or untaxed 'tax', "
                    "you must choose a fiscal position with afip code in %s.\n"
                    "* Invoice id %i" % (afip_exempt_codes, invoice.id))
                )

    # TODO sacamos esto porque no era muy lindo y daba algunos errores con
    # el account_fix, hicimos que los datos demo hagan el compute tax
    # habria que ver una mejor forma de hacerlo para que tambien ande bien si
    # se importa desde interfaz
    # If we import or get demo data
    # tax_id is not loaded on tax lines, we couldn't find the error
    # so we add this to fix it
    # @api.one
    # @api.constrains('invoice_line_ids')
    # def update_taxes_fix(self):
    #     context = dict(self._context)
    #     if context.get('constraint_update_taxes'):
    #         return True
    #     self.with_context(constraint_update_taxes=True).compute_taxes()

    # we add fiscal position with fp method instead of directly from partner
    # TODO. this should go in a PR to ODOO
    @api.onchange('partner_id', 'company_id')
    def _onchange_partner_id(self):
        res = super(AccountInvoice, self)._onchange_partner_id()
        fiscal_position = self.env[
            'account.fiscal.position'].with_context(
                force_company=self.company_id.id).get_fiscal_position(
                self.partner_id.id)
        if fiscal_position:
            self.fiscal_position_id = fiscal_position
        return res

    @api.one
    @api.constrains('date_invoice')
    def set_date_afip(self):
        if self.date_invoice:
            date_invoice = fields.Datetime.from_string(self.date_invoice)
            if not self.afip_service_start:
                self.afip_service_start = date_invoice + relativedelta(day=1)
            if not self.afip_service_end:
                self.afip_service_end = date_invoice + \
                    relativedelta(day=1, days=-1, months=+1)
Exemplo n.º 13
0
class HrExpense(models.Model):

    _name = "hr.expense"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _description = "Expense"
    _order = "date desc"

    name = fields.Char(string='Expense Description', readonly=True, required=True, states={'draft': [('readonly', False)]})
    date = fields.Date(readonly=True, states={'draft': [('readonly', False)]}, default=fields.Date.context_today, string="Date")
    employee_id = fields.Many2one('hr.employee', string="Employee", required=True, readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1))
    product_id = fields.Many2one('product.product', string='Product', readonly=True, states={'draft': [('readonly', False)]}, domain=[('can_be_expensed', '=', True)], required=True)
    product_uom_id = fields.Many2one('product.uom', string='Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env['product.uom'].search([], limit=1, order='id'))
    unit_amount = fields.Float(string='Unit Price', readonly=True, required=True, states={'draft': [('readonly', False)]}, digits=dp.get_precision('Product Price'))
    quantity = fields.Float(required=True, readonly=True, states={'draft': [('readonly', False)]}, digits=dp.get_precision('Product Unit of Measure'), default=1)
    tax_ids = fields.Many2many('account.tax', 'expense_tax', 'expense_id', 'tax_id', string='Taxes', states={'done': [('readonly', True)], 'post': [('readonly', True)]})
    untaxed_amount = fields.Float(string='Subtotal', store=True, compute='_compute_amount', digits=dp.get_precision('Account'))
    total_amount = fields.Float(string='Total', store=True, compute='_compute_amount', digits=dp.get_precision('Account'))
    company_id = fields.Many2one('res.company', string='Company', readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env.user.company_id)
    currency_id = fields.Many2one('res.currency', string='Currency', readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env.user.company_id.currency_id)
    analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, oldname='analytic_account', domain=[('account_type', '=', 'normal')])
    department_id = fields.Many2one('hr.department', string='Department', states={'post': [('readonly', True)], 'done': [('readonly', True)]})
    description = fields.Text()
    payment_mode = fields.Selection([("own_account", "Employee (to reimburse)"), ("company_account", "Company")], default='own_account', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, string="Payment By")
    journal_id = fields.Many2one('account.journal', string='Expense Journal', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, default=lambda self: self.env['account.journal'].search([('type', '=', 'purchase')], limit=1), help="The journal used when the expense is done.")
    bank_journal_id = fields.Many2one('account.journal', string='Bank Journal', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, default=lambda self: self.env['account.journal'].search([('type', 'in', ['case', 'bank'])], limit=1), help="The payment method used when the expense is paid by the company.")
    account_move_id = fields.Many2one('account.move', string='Journal Entry', copy=False, track_visibility="onchange")
    attachment_number = fields.Integer(compute='_compute_attachment_number', string='Number of Attachments')
    state = fields.Selection([('draft', 'To Submit'),
                              ('submit', 'Submitted'),
                              ('approve', 'Approved'),
                              ('post', 'Waiting Payment'),
                              ('done', 'Paid'),
                              ('cancel', 'Refused')
                              ], string='Status', index=True, readonly=True, track_visibility='onchange', copy=False, default='draft', required=True,
        help='When the expense request is created the status is \'To Submit\'.\n It is submitted by the employee and request is sent to manager, the status is \'Submitted\'.\
        \nIf the manager approve it, the status is \'Approved\'.\n If the accountant genrate the accounting entries for the expense request, the status is \'Waiting Payment\'.')

    @api.depends('quantity', 'unit_amount', 'tax_ids', 'currency_id')
    def _compute_amount(self):
        for expense in self:
            expense.untaxed_amount = expense.unit_amount * expense.quantity
            taxes = expense.tax_ids.compute_all(expense.unit_amount, expense.currency_id, expense.quantity, expense.product_id, expense.employee_id.user_id.partner_id)
            expense.total_amount = taxes.get('total_included')

    @api.multi
    def _compute_attachment_number(self):
        attachment_data = self.env['ir.attachment'].read_group([('res_model', '=', 'hr.expense'), ('res_id', 'in', self.ids)], ['res_id'], ['res_id'])
        attachment = dict((data['res_id'], data['res_id_count']) for data in attachment_data)
        for expense in self:
            expense.attachment_number = attachment.get(expense.id, 0)

    @api.onchange('product_id')
    def _onchange_product_id(self):
        if self.product_id:
            if not self.name:
                self.name = self.product_id.display_name or ''
            self.unit_amount = self.env['product.template']._price_get(self.product_id, 'standard_price')[self.product_id.id]
            self.product_uom_id = self.product_id.uom_id
            self.tax_ids = self.product_id.supplier_taxes_id

    @api.onchange('product_uom_id')
    def _onchange_product_uom_id(self):
        if self.product_id and self.product_uom_id.category_id != self.product_id.uom_id.category_id:
            raise UserError(_('Selected Unit of Measure does not belong to the same category as the product Unit of Measure'))

    @api.onchange('employee_id')
    def _onchange_employee_id(self):
        self.department_id = self.employee_id.department_id

    def _add_followers(self):
        user_ids = []
        employee = self.employee_id
        if employee.user_id:
            user_ids.append(employee.user_id.id)
        if employee.parent_id:
            user_ids.append(employee.parent_id.user_id.id)
        if employee.department_id and employee.department_id.manager_id and employee.parent_id != employee.department_id.manager_id:
            user_ids.append(employee.department_id.manager_id.user_id.id)
        self.sudo().message_subscribe_users(user_ids=user_ids)

    @api.model
    def create(self, vals):
        hr_expense = super(HrExpense, self).create(vals)
        if vals.get('employee_id'):
            hr_expense._add_followers()
        return hr_expense

    @api.multi
    def write(self, vals):
        res = super(HrExpense, self).write(vals)
        if vals.get('employee_id'):
            self._add_followers()
        return res

    @api.multi
    def unlink(self):
        if any(expense.state not in ['draft', 'cancel'] for expense in self):
            raise UserError(_('You can only delete draft or refused expenses!'))
        return super(HrExpense, self).unlink()

    @api.multi
    def submit_expenses(self):
        if any(expense.state != 'draft' for expense in self):
            raise UserError(_("You can only submit draft expenses!"))
        self.write({'state': 'submit'})

    @api.multi
    def approve_expenses(self):
        self.write({'state': 'approve'})

    @api.multi
    def refuse_expenses(self, reason):
        self.write({'state': 'cancel'})
        if self.employee_id.user_id:
            body = (_("Your Expense %s has been refused.<br/><ul class=o_timeline_tracking_value_list><li>Reason<span> : </span><span class=o_timeline_tracking_value>%s</span></li></ul>") % (self.name, reason))
            self.message_post(body=body, partner_ids=[self.employee_id.user_id.partner_id.id])

    @api.multi
    def paid_expenses(self):
        self.write({'state': 'done'})

    @api.multi
    def reset_expenses(self):
        return self.write({'state': 'draft'})

    @api.multi
    def _track_subtype(self, init_values):
        self.ensure_one()
        if 'state' in init_values and self.state == 'approve':
            return 'hr_expense.mt_expense_approved'
        elif 'state' in init_values and self.state == 'submit':
            return 'hr_expense.mt_expense_confirmed'
        elif 'state' in init_values and self.state == 'cancel':
            return 'hr_expense.mt_expense_refused'
        return super(HrExpense, self)._track_subtype(init_values)

    def _prepare_move_line(self, line):
        '''
        This function prepares move line of account.move related to an expense
        '''
        partner_id = self.employee_id.address_home_id.commercial_partner_id.id
        return {
            'date_maturity': line.get('date_maturity'),
            'partner_id': partner_id,
            'name': line['name'][:64],
            'debit': line['price'] > 0 and line['price'],
            'credit': line['price'] < 0 and -line['price'],
            'account_id': line['account_id'],
            'analytic_line_ids': line.get('analytic_line_ids'),
            'amount_currency': line['price'] > 0 and abs(line.get('amount_currency')) or -abs(line.get('amount_currency')),
            'currency_id': line.get('currency_id'),
            'tax_line_id': line.get('tax_line_id'),
            'tax_ids': line.get('tax_ids'),
            'ref': line.get('ref'),
            'quantity': line.get('quantity',1.00),
            'product_id': line.get('product_id'),
            'product_uom_id': line.get('uom_id'),
            'analytic_account_id': line.get('analytic_account_id'),
        }

    @api.multi
    def _compute_expense_totals(self, company_currency, account_move_lines, move_date):
        '''
        internal method used for computation of total amount of an expense in the company currency and
        in the expense currency, given the account_move_lines that will be created. It also do some small
        transformations at these account_move_lines (for multi-currency purposes)

        :param account_move_lines: list of dict
        :rtype: tuple of 3 elements (a, b ,c)
            a: total in company currency
            b: total in hr.expense currency
            c: account_move_lines potentially modified
        '''
        self.ensure_one()
        total = 0.0
        total_currency = 0.0
        for line in account_move_lines:
            line['currency_id'] = False
            line['amount_currency'] = False
            if self.currency_id != company_currency:
                line['currency_id'] = self.currency_id.id
                line['amount_currency'] = line['price']
                line['price'] = self.currency_id.with_context(date=move_date or fields.Date.context_today(self)).compute(line['price'], company_currency)
            total -= line['price']
            total_currency -= line['amount_currency'] or line['price']
        return total, total_currency, account_move_lines

    @api.multi
    def action_move_create(self):
        '''
        main function that is called when trying to create the accounting entries related to an expense
        '''
        if any(expense.state != 'approve' for expense in self):
            raise UserError(_("You can only generate accounting entry for approved expense(s)."))

        if any(expense.employee_id != self[0].employee_id for expense in self):
            raise UserError(_("Expenses must belong to the same Employee."))

        if any(not expense.journal_id for expense in self):
            raise UserError(_("Expenses must have an expense journal specified to generate accounting entries."))

        journal_dict = {}
        maxdate = False
        for expense in self:
            if expense.date > maxdate:
                maxdate = expense.date
            jrn = expense.bank_journal_id if expense.payment_mode == 'company_account' else expense.journal_id
            journal_dict.setdefault(jrn, [])
            journal_dict[jrn].append(expense)

        for journal, expense_list in journal_dict.items():
            #create the move that will contain the accounting entries
            move = self.env['account.move'].create({
                'journal_id': journal.id,
                'company_id': self.env.user.company_id.id,
                'date': maxdate,
            })
            for expense in expense_list:
                company_currency = expense.company_id.currency_id
                diff_currency_p = expense.currency_id != company_currency
                #one account.move.line per expense (+taxes..)
                move_lines = expense._move_line_get()

                #create one more move line, a counterline for the total on payable account
                total, total_currency, move_lines = expense._compute_expense_totals(company_currency, move_lines, maxdate)
                if expense.payment_mode == 'company_account':
                    if not expense.bank_journal_id.default_credit_account_id:
                        raise UserError(_("No credit account found for the %s journal, please configure one.") % (expense.bank_journal_id.name))
                    emp_account = expense.bank_journal_id.default_credit_account_id.id
                else:
                    if not expense.employee_id.address_home_id:
                        raise UserError(_("No Home Address found for the employee %s, please configure one.") % (expense.employee_id.name))
                    emp_account = expense.employee_id.address_home_id.property_account_payable_id.id

                move_lines.append({
                        'type': 'dest',
                        'name': expense.employee_id.name,
                        'price': total,
                        'account_id': emp_account,
                        'date_maturity': expense.date,
                        'amount_currency': diff_currency_p and total_currency or False,
                        'currency_id': diff_currency_p and expense.currency_id.id or False,
                        'ref': expense.employee_id.address_home_id.ref or False
                        })

                #convert eml into an osv-valid format
                lines = map(lambda x:(0, 0, expense._prepare_move_line(x)), move_lines)
                move.with_context(dont_create_taxes=True).write({'line_ids': lines})
                expense.write({'account_move_id': move.id, 'state': 'post'})
                if expense.payment_mode == 'company_account':
                    expense.paid_expenses()
            move.post()
        return True

    @api.multi
    def _move_line_get(self):
        account_move = []
        for expense in self:
            if expense.product_id:
                account = expense.product_id.product_tmpl_id._get_product_accounts()['expense']
                if not account:
                    raise UserError(_("No Expense account found for the product %s (or for it's category), please configure one.") % (expense.product_id.name))
            else:
                account = self.env['ir.property'].with_context(force_company=expense.company_id.id).get('property_account_expense_categ_id', 'product.category')
                if not account:
                    raise UserError(_('Please configure Default Expense account for Product expense: `property_account_expense_categ_id`.'))
            move_line = {
                    'type': 'src',
                    'name': expense.name.split('\n')[0][:64],
                    'price_unit': expense.unit_amount,
                    'quantity': expense.quantity,
                    'price': expense.total_amount,
                    'account_id': account.id,
                    'product_id': expense.product_id.id,
                    'uom_id': expense.product_uom_id.id,
                    'analytic_account_id': expense.analytic_account_id.id,
                }
            account_move.append(move_line)

            # Calculate tax lines and adjust base line
            taxes = expense.tax_ids.compute_all(expense.unit_amount, expense.currency_id, expense.quantity, expense.product_id)
            account_move[-1]['price'] = taxes['total_excluded']
            account_move[-1]['tax_ids'] = expense.tax_ids.ids
            for tax in taxes['taxes']:
                account_move.append({
                    'type': 'tax',
                    'name': tax['name'],
                    'price_unit': tax['amount'],
                    'quantity': 1,
                    'price': tax['amount'],
                    'account_id': tax['account_id'] or move_line['account_id'],
                    'tax_line_id': tax['id'],
                })
        return account_move

    @api.multi
    def action_get_attachment_view(self):
        self.ensure_one()
        res = self.env['ir.actions.act_window'].for_xml_id('base', 'action_attachment')
        res['domain'] = [('res_model', '=', 'hr.expense'), ('res_id', 'in', self.ids)]
        res['context'] = {'default_res_model': 'hr.expense', 'default_res_id': self.id}
        return res
class qdodoo_car_sale_contract(models.Model):
    """
        销售合同
    """
    _name = 'qdodoo.car.sale.contract'
    _rec_name = 'contract_num'
    _order = 'id desc'

    name = fields.Char(u'合同编号')
    contract_num = fields.Char(
        u'合同号',
        required=True,
        default=lambda self: str(datetime.now().year) + '-SDHSLGXHSC-')
    partner_id = fields.Many2one('res.partner', u'客户', required=True)
    date_order = fields.Date(u'合同日期',
                             required=True,
                             default=datetime.now().date())
    car_number = fields.Float(u'车辆数', compute="_get_car_number")
    amount_total = fields.Float(u'金额', compute="_get_car_number")
    deposit_rate = fields.Float(u'保证金比例(%)', required=True)
    state = fields.Selection([('draft', u'草稿'), ('doing', u'执行'),
                              ('done', u'完成'), ('cancel', u'取消'),
                              ('no_normal', u'异常')],
                             u'状态',
                             default='draft')
    order_line = fields.One2many('qdodoo.car.sale.contract.line', 'order_id',
                                 u'车辆明细')
    file_name = fields.Char(u'合同名称', copy=False)
    contract_file = fields.Binary(u'合同扫描件', copy=False)
    pledge_money = fields.Float(u'保证金已收金额', copy=False)
    currency_id = fields.Many2one('res.currency', u'外币币种', required=True)
    currency_raise = fields.Float(u'外币汇率', required=True, digits=(14, 6))
    is_issuing = fields.Boolean(u'已收保证金', copy=False)
    is_make_invoice = fields.Boolean(u'全部已开票', copy=False)
    is_payment = fields.Boolean(u'全部已收提车款', copy=False)
    is_settlement = fields.Boolean(u'已结算', copy=False)
    dalay_date = fields.Float(u'延期天数', default='90')
    dalay_rate = fields.Float(u'押汇利率(%)', default='4.5')
    dalay_china_rate = fields.Float(u'延期利率(%)人民币', default='10')

    _sql_constraints = [
        ('contract_num_uniq', 'unique(contract_num)', '销售合同号已存在!'),
    ]

    @api.onchange('currency_id')
    def onchange_currcency(self):
        if self.currency_id:
            self.currency_raise = 1 / self.currency_id.rate_silent

    # 只能删除草稿或取消的订单
    @api.multi
    def unlink(self):
        for ids in self:
            if ids.state not in ('draft', 'cancel'):
                raise osv.except_osv(_(u'错误'), _(u'只能删除草稿或取消的订单!'))
        return super(qdodoo_car_sale_contract, self).unlink()

    # 获取唯一的编号
    @api.model
    def create(self, vals):
        if not vals.get('name'):
            vals['name'] = self.env['ir.sequence'].get(
                'qdodoo.car.sale.contract')
        return super(qdodoo_car_sale_contract, self).create(vals)

    # 获取车辆数量、金额
    def _get_car_number(self):
        for ids in self:
            number = 0
            money = 0
            for line in ids.order_line:
                number += line.product_qty
                money += line.all_money
            ids.car_number = number
            ids.amount_total = money

    # 押汇申请(销售)
    @api.multi
    def btn_negotiation_sale(self):
        negotiation_obj = self.env['qdodoo.car.negotiation.manager.sale']
        # 判断是否存在押汇
        negotiation_id = negotiation_obj.search([('purchase_id.contract_id',
                                                  '=', self.id),
                                                 ('state', '!=', 'cancel')])
        result = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade',
            'tree_qdodoo_car_negotiation_manager_sale')
        view_id = result and result[1] or False
        result_form = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade',
            'form_qdodoo_car_negotiation_manager_sale')
        view_id_form = result_form and result_form[1] or False
        return {
            'name': _('押汇'),
            'view_type': 'form',
            "view_mode": 'tree,form',
            'res_model': 'qdodoo.car.negotiation.manager.sale',
            'type': 'ir.actions.act_window',
            'domain': [('id', '=', negotiation_id.ids)],
            'views': [(view_id, 'tree'), (view_id_form, 'form')],
            'view_id': [view_id],
        }

    @api.one
    # 确认销售合同(生成车辆档案)
    def btn_doing(self):
        information_obj = self.env['qdodoo.car.information']
        for ids in self:
            if not ids.order_line:
                raise osv.except_osv(_('错误!'), _('请输入车辆明细.'))
            for line in ids.order_line:
                # 判断如果存在车架号,则数量只能为1
                if line.product_num and line.product_qty != 1:
                    raise osv.except_osv(_('错误!'), _('填写车架号的明细数量只能为1.'))
                line.write({'rest_qty': line.product_qty})
                val = {}
                val['sale_contract'] = ids.id
                val['product_id'] = line.product_id.id
                val['price_unit'] = line.price_unit
                val['tax_money'] = line.tax_money
                val['product_num'] = line.product_num
                val['pledge_money'] = line.pledge_money / line.product_qty
                val['agent_money'] = line.agent_money
                val['currency_id'] = self.currency_id.id
                if line.product_qty > 1:
                    for k in range(line.product_qty):
                        information_obj.create(val)
                else:
                    information_obj.create(val)
        return self.write({'state': 'doing'})

    @api.multi
    # 保证金(收款通知)
    def btn_predict_money(self):
        payment_obj = self.env['qdodoo.car.payment.order']
        line_obj = self.env['qdodoo.car.payment.line']
        information_obj = self.env['qdodoo.car.information']
        domain = [('type', '=', 'collection'), ('contract_id', '=', self.id),
                  ('is_pledge', '=', True), ('state', '!=', 'cancel')]
        value = {
            'type': 'collection',
            'contract_id': self.id,
            'partner_id': self.partner_id.id,
            'is_pledge': True
        }
        # 是保证金
        model_id, pay_project = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'qdodoo_car_expense_1')
        value['pay_project'] = pay_project
        # 判断是否存在收款通知
        payment_id = payment_obj.search(domain)
        if not payment_id:
            # 创建对应的收款通知
            payment_id = payment_obj.create(value)
            # 创建对应的收款明细
            # 获取销售订单对应的车辆档案
            information_ids = information_obj.search([('sale_contract', '=',
                                                       self.id)])
            for information_id in information_ids:
                money = information_id.pledge_money
                line_obj.create({
                    'order_id': payment_id.id,
                    'product_num': information_id.id,
                    'money': money
                })
        result = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'tree_qdodoo_car_payment_order')
        view_id = result and result[1] or False
        result_form = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'form_qdodoo_car_payment_order')
        view_id_form = result_form and result_form[1] or False
        return {
            'name': _('收款通知'),
            'view_type': 'form',
            "view_mode": 'tree,form',
            'res_model': 'qdodoo.car.payment.order',
            'type': 'ir.actions.act_window',
            'domain': [('id', '=', payment_id.id)],
            'views': [(view_id, 'tree'), (view_id_form, 'form')],
            'view_id': [view_id],
        }

    @api.multi
    # 二次保证金
    def btn_predict_money_two(self):
        margin_obj = self.env['qdodoo.car.margin.money']
        margin_ids = margin_obj.search([('partner_id', '=', self.partner_id.id)
                                        ])
        result = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'tree_qdodoo_car_margin_money')
        view_id = result and result[1] or False
        result_form = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'form_qdodoo_car_margin_money')
        view_id_form = result_form and result_form[1] or False
        return {
            'name': _('二次保证金'),
            'view_type': 'form',
            "view_mode": 'tree,form',
            'res_model': 'qdodoo.car.margin.money',
            'type': 'ir.actions.act_window',
            'domain': [('id', '=', margin_ids.ids)],
            'context': {
                'partner_id': self.partner_id.id
            },
            'views': [(view_id, 'tree'), (view_id_form, 'form')],
            'view_id': [view_id],
        }

    @api.multi
    # 提车款
    def btn_bring_car(self):
        carry_obj = self.env['qdodoo.car.carry.money']
        carry_ids = carry_obj.search([('partner_id', '=', self.partner_id.id)])
        result = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'tree_qdodoo_car_carry_money')
        view_id = result and result[1] or False
        result_form = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'form_qdodoo_car_carry_money')
        view_id_form = result_form and result_form[1] or False
        return {
            'name': _('提车款'),
            'view_type': 'form',
            "view_mode": 'tree,form',
            'res_model': 'qdodoo.car.carry.money',
            'type': 'ir.actions.act_window',
            'domain': [('id', '=', carry_ids.ids)],
            'context': {
                'partner_id': self.partner_id.id
            },
            'views': [(view_id, 'tree'), (view_id_form, 'form')],
            'view_id': [view_id],
        }

    @api.multi
    # 结算
    def btn_squaring_up(self):
        settlement_obj = self.env['qdodoo.car.settlement']
        settlement_ids = settlement_obj.search([('sale_id', '=', self.id)])
        result = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'tree_qdodoo_car_settlement')
        view_id = result and result[1] or False
        result_form = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'form_qdodoo_car_settlement')
        view_id_form = result_form and result_form[1] or False
        return {
            'name': _('结算单'),
            'view_type': 'form',
            "view_mode": 'tree,form',
            'res_model': 'qdodoo.car.settlement',
            'type': 'ir.actions.act_window',
            'domain': [('id', '=', settlement_ids.ids)],
            'views': [(view_id, 'tree'), (view_id_form, 'form')],
            'view_id': [view_id],
        }

    @api.one
    # 取消
    def btn_cancel(self):
        return self.write({'state': 'cancel'})

    @api.one
    # 异常
    def btn_unusual(self):
        return self.write({'state': 'no_normal'})

    @api.one
    # 恢复执行
    def btn_perform(self):
        return self.write({'state': 'doing'})

    @api.multi
    # 查看车辆档案
    def btn_car_information(self):
        information_obj = self.env['qdodoo.car.information']
        information_ids = information_obj.search([('sale_contract', '=',
                                                   self.id)])
        result = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'tree_qdodoo_car_information')
        view_id = result and result[1] or False
        result_form = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'form_qdodoo_car_information')
        view_id_form = result_form and result_form[1] or False
        return {
            'name': _('车辆档案'),
            'view_type': 'form',
            "view_mode": 'tree,form',
            'res_model': 'qdodoo.car.information',
            'type': 'ir.actions.act_window',
            'domain': [('id', 'in', information_ids.ids)],
            'views': [(view_id, 'tree'), (view_id_form, 'form')],
            'view_id': [view_id],
        }

    @api.multi
    # 查看外贸合同
    def btn_purchase_contract(self):
        purchase_obj = self.env['qdodoo.car.purchase.contract']
        purchase_ids = purchase_obj.search([('contract_id', '=', self.id)])
        result = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'tree_qdodoo_car_purchase_contract')
        view_id = result and result[1] or False
        result_form = self.env['ir.model.data'].get_object_reference(
            'qdodoo_car_import_trade', 'form_qdodoo_car_purchase_contract')
        view_id_form = result_form and result_form[1] or False
        return {
            'name': _('外贸合同'),
            'view_type': 'form',
            "view_mode": 'tree,form',
            'res_model': 'qdodoo.car.purchase.contract',
            'type': 'ir.actions.act_window',
            'domain': [('id', 'in', purchase_ids.ids)],
            'views': [(view_id, 'tree'), (view_id_form, 'form')],
            'view_id': [view_id],
        }
Exemplo n.º 15
0
    def _check_attended_date(self):
        for obj in self:
            call_received = obj.call_received
            call_attended = obj.call_attended

            if call_received and call_attended:
                if call_attended < call_received:
                    return False
        return True

    _constraints = [
            (_check_attended_date, 'Call Attended should be greater than call received', ['call_received','call_attended']),
        ]
#
    id = fields.Integer(string="ID",readonly=True)
    name = fields.Char(string="Case",required=True)
    message_summary=fields.Char('Message Summary')
    case_name = fields.Char(string="Case",required=True)
    active = fields.Boolean(string="Active",required=False,default=True)
    create_date = fields.Datetime(string="Creation Date",readonly=True, index=True)
    write_date = fields.Datetime(string="Update Date",readonly=True)
    date_deadline = fields.Date(string="Deadline")
    partner_id = fields.Many2one('res.partner', 'Customer', required=True)
    contact_id = fields.Many2one('res.partner', 'Contact', required=False)
    company_id = fields.Many2one('res.company', 'Company')
    description = fields.Text(string="Private Note")
    action_taken = fields.Text(compute='_task_lines_cal', string='Action Taken')
    re_open = fields.Text(string="Reopen")
    kanban_state= fields.Selection([('normal','Normal'),('blocked','Blocked'),('done','Ready for next stage'),\
            ],string = 'Kanban State', track_visibility='onchange',help="A Case's kanban state indicates special situations affecting it:\n"
                                              " * Normal is the default situation\n"
class AccountTaxReport(models.Model):
    _name = 'account.tax.report'
    _description = 'Account Tax Report (pdf/xls)'
    _auto = False

    doc_type = fields.Selection(
        [('sale', 'Sales'), ('purchase', 'Purchase')],
        string='Type',
    )
    period_id = fields.Many2one(
        'account.period',
        string='Period',
    )
    move_number = fields.Char(string='JE Number', )
    move_ref = fields.Char(
        string='JE Ref.',
        help="Original Document",
    )
    year = fields.Char(string='Year', )
    month = fields.Char(string='Month', )
    report_period_id = fields.Many2one(
        'account.period',
        string='Reporting Period',
    )
    partner_id = fields.Many2one(
        'res.partner',
        string='Partner',
    )
    partner_name = fields.Char(string='Partner Name', )
    partner_title = fields.Char(string='Partner Title', )
    partner_vat = fields.Char(string='Partner VAT', )
    partner_taxbranch = fields.Char(string='Partner Taxbranch', )
    tax_id = fields.Many2one(
        'account.tax',
        string='Tax',
    )
    tax_sequence = fields.Integer(string='Sequence', )
    tax_sequence_display = fields.Char(string='Sequence Display', )
    invoice_date = fields.Date(string='Invoice Date', )
    invoice_number = fields.Char(string='Invoice Number', )
    base = fields.Float(string='Base', )
    amount = fields.Float(string='Tax', )

    def _select(self):
        res = """
            doc_type, atd.id, atd.period_id,
            am.name as move_number, am.ref as move_ref,
            to_char(ap.date_start, 'YYYY') as "year",
            to_char(ap.date_start, 'MM') as "month",
            report_period_id, tax_sequence, tax_id, tax_sequence_display,
            invoice_date, invoice_number, atd.partner_id,
            case when cancel is true then ''
                else rp.name end as partner_name,
            case when cancel is true then ''
                else rpt.name end as partner_title,
            case when cancel is true then ''
                else rp.vat end as vat,
            case when cancel is true then ''
                else rp.taxbranch end as taxbranch,
            case when cancel is true then 0.0
                else base_company end as base,
            case when cancel is true then 0.0
                else amount_company end as amount
        """
        return res

    def init(self, cr):
        tools.drop_view_if_exists(cr, self._table)
        cr.execute("""CREATE or REPLACE VIEW %s as (
            select %s
            from account_tax_detail atd
            left outer join res_partner rp on rp.id = atd.partner_id
            left outer join res_partner_title rpt on rp.title = rpt.id
            left outer join account_period ap on atd.period_id = ap.id
            left outer join account_move am on am.id = atd.ref_move_id
            where report_period_id is not null
            order by year, month, tax_sequence
        )""" % (
            self._table,
            self._select(),
        ))
Exemplo n.º 17
0
class project_case_version(models.Model):
    _name = "project.case.version"
    _order = "name desc"
    name = fields.Char(string="student Name")
    active = fields.Boolean(string="student Name",default=True)
Exemplo n.º 18
0
class TxtIva(models.Model):
    _name = "txt.iva"
    _inherit = ['mail.thread']

    @api.model
    def _default_period_id(self):
        """ Return current period
        """
        fecha = time.strftime('%m/%Y')
        periods = self.env['account.period'].search([('code', '=', fecha)])
        return periods and periods[0].id or False

    @api.multi
    def _get_amount_total(self):
        """ Return total amount withheld of each selected bill
        """
        res = {}
        for txt in self:
            res[txt.id] = 0.0
            for txt_line in txt.txt_ids:
                if txt_line.invoice_id.type in ['out_refund', 'in_refund']:
                    res[txt.id] -= txt_line.amount_withheld
                else:
                    res[txt.id] += txt_line.amount_withheld
        return res

    @api.multi
    def _get_amount_total_base(self):
        """ Return total amount base of each selected bill
        """
        res = {}
        for txt in self:
            res[txt.id] = 0.0
            for txt_line in txt.txt_ids:
                if txt_line.invoice_id.type in ['out_refund', 'in_refund']:
                    res[txt.id] -= txt_line.untaxed
                else:
                    res[txt.id] += txt_line.untaxed
        return res

    name = fields.Char(
        string='Description', size=128, required=True, select=True,
        default=lambda self: 'Withholding Vat ' + time.strftime('%m/%Y'),
        help="Description about statement of withholding income")
    company_id = fields.Many2one(
        'res.company', string='Company', required=True, readonly=True,
        states={'draft': [('readonly', False)]}, help='Company',
        default=lambda self: self.env['res.company']._company_default_get())
    state = fields.Selection([
        ('draft', 'Draft'),
        ('confirmed', 'Confirmed'),
        ('done', 'Done'),
        ('cancel', 'Cancelled')
        ], string='Estado', select=True, readonly=True, default='draft',
        help="proof status")
    period_id = fields.Many2one(
        'account.period', string='Period', required=True, readonly=True,
        default=_default_period_id,
        states={'draft': [('readonly', False)]}, help='fiscal period')
    type = fields.Boolean(
        string='Retention Suppliers?', required=True,
        states={'draft': [('readonly', False)]}, default=True,
        help="Select the type of retention to make")
    date_start = fields.Date(
        string='Begin Date', required=True,
        states={'draft': [('readonly', False)]},
        help="Begin date of period")
    date_end = fields.Date(
        string='End date', required=True,
        states={'draft': [('readonly', False)]},
        help="End date of period")
    txt_ids = fields.One2many(
        'txt.iva.line', 'txt_id', readonly=True,
        states={'draft': [('readonly', False)]},
        help='Txt field lines of ar required by SENIAT for'
        ' VAT withholding')
    amount_total_ret = fields.Float(
        string='Withholding total amount', digits=dp.get_precision('Account'),
        compute=_get_amount_total, readonly=True,
        help="Monto Total Retenido")
    amount_total_base = fields.Float(
        string='Taxable total amount', digits=dp.get_precision('Account'),
        compute=_get_amount_total_base, readonly=True,
        help="Total de la Base Imponible")

    @api.multi
    def name_get(self):
        """ Return a list with id and name of the current register
        """
        res = [(r.id, r.name) for r in self]
        return res

    @api.multi
    def action_anular(self):
        """ Return document state to draft
        """
        self.write({'state': 'draft'})
        return True

    @api.multi
    def check_txt_ids(self):
        """ Check that txt_iva has lines to process."""
        for awi in self:
            if not awi.txt_ids:
                raise exceptions.except_orm(
                    _("Missing Values !"),
                    _("Missing VAT TXT Lines!!!"))
        return True

    @api.multi
    def action_confirm(self):
        """ Transfers the document status to confirmed
        """
        self.check_txt_ids()
        self.write({'state': 'confirmed'})
        return True

    @api.multi
    def action_generate_lines_txt(self):
        """ Current lines are cleaned and rebuilt
        """
        rp_obj = self.env['res.partner']
        voucher_obj = self.env['account.wh.iva']
        txt_iva_obj = self.env['txt.iva.line']
        vouchers = []
        txt_brw = self.browse(self._ids)[0]
        txt_ids = txt_iva_obj.search([('txt_id', '=', txt_brw.id)])
        if txt_ids:
            txt_ids.unlink()

        if txt_brw.type:
            vouchers = voucher_obj.search([
                ('date_ret', '>=', txt_brw.date_start),
                ('date_ret', '<=', txt_brw.date_end),
                ('period_id', '=', txt_brw.period_id.id),
                ('state', '=', 'done'),
                ('type', 'in', ['in_invoice', 'in_refund'])])
        else:
            vouchers = voucher_obj.search([
                ('date_ret', '>=', txt_brw.date_start),
                ('date_ret', '<=', txt_brw.date_end),
                ('period_id', '=', txt_brw.period_id.id),
                ('state', '=', 'done'),
                ('type', 'in', ['out_invoice', 'out_refund'])])

        for voucher in vouchers:
            acc_part_id = rp_obj._find_accounting_partner(voucher.partner_id)
            for voucher_lines in voucher.wh_lines:
                if voucher_lines.invoice_id.state not in ['open', 'paid']:
                    continue
                for voucher_tax_line in voucher_lines.tax_line:
                    txt_iva_obj.create(
                        {'partner_id': acc_part_id.id,
                         'voucher_id': voucher.id,
                         'invoice_id': voucher_lines.invoice_id.id,
                         'txt_id': txt_brw.id,
                         'untaxed': voucher_tax_line.base,
                         'amount_withheld': voucher_tax_line.amount_ret,
                         'tax_wh_iva_id': voucher_tax_line.id,
                         })
        return True

    @api.model
    def get_buyer_vendor(self, txt, txt_line):
        """ Return the buyer and vendor of the sale or purchase invoice
        @param txt: current txt document
        @param txt_line: One line of the current txt document
        """
        rp_obj = self.env['res.partner']
        vat_company = rp_obj._find_accounting_partner(
            txt.company_id.partner_id).vat[2:]
        vat_partner = rp_obj._find_accounting_partner(
            txt_line.partner_id).vat[2:]
        if txt_line.invoice_id.type in ['out_invoice', 'out_refund']:
            vendor = vat_company
            buyer = vat_partner
        else:
            buyer = vat_company
            vendor = vat_partner
        return (vendor, buyer)

    @api.model
    def get_document_affected(self, txt_line):
        """ Return the reference or number depending of the case
        @param txt_line: line of the current document
        """
        number = '0'
        if txt_line.invoice_id.type in ['in_invoice', 'in_refund'] and \
                txt_line.invoice_id.parent_id:
            number = txt_line.invoice_id.parent_id.supplier_invoice_number
        elif txt_line.invoice_id.parent_id:
            number = txt_line.invoice_id.parent_id.number
        return number

    @api.model
    def get_number(self, number, inv_type, max_size):
        """ Return a list of number for document number
        @param number: list of characters from number or reference of the bill
        @param inv_type: invoice type
        @param long: max size oh the number
        """
        if not number:
            return '0'
        result = ''
        for i in number:
            if inv_type == 'vou_number' and i.isdigit():
                if len(result) < max_size:
                    result = i + result
            elif i.isalnum():
                if len(result) < max_size:
                    result = i + result
        return result[::-1].strip()

    @api.model
    def get_document_number(self, txt_line, inv_type):
        """ Return the number o reference of the invoice into txt line
        @param txt_line: One line of the current txt document
        @param inv_type: invoice type into txt line
        """
        number = 0
        if txt_line.invoice_id.type in ['in_invoice', 'in_refund']:
            if not txt_line.invoice_id.supplier_invoice_number:
                raise exceptions.except_orm(
                    _('Invalid action !'),
                    _("Unable to make txt file, because the bill has no"
                      " reference number free!"))
            else:
                number = self.get_number(
                    txt_line.invoice_id.supplier_invoice_number.strip(),
                    inv_type, 20)
        elif txt_line.invoice_id.number:
            number = self.get_number(
                txt_line.invoice_id.number.strip(), inv_type, 20)
        return number

    @api.model
    def get_type_document(self, txt_line):
        """ Return the document type
        @param txt_line: line of the current document
        """
        inv_type = '03'
        if txt_line.invoice_id.type in ['out_invoice', 'in_invoice']:
            inv_type = '01'
        elif txt_line.invoice_id.type in ['out_invoice', 'in_invoice'] and \
                txt_line.invoice_id.parent_id:
            inv_type = '02'
        return inv_type

    @api.model
    def get_max_aliquot(self, txt_line):
        """Get maximum aliquot per invoice"""
        res = []
        for tax_line in txt_line.invoice_id.tax_line:
            res.append(int(tax_line.tax_id.amount * 100))
        return max(res)

    @api.model
    def get_amount_line(self, txt_line, amount_exempt):
        """Method to compute total amount"""
        ali_max = self.get_max_aliquot(txt_line)
        exempt = 0

        if ali_max == int(txt_line.tax_wh_iva_id.tax_id.amount * 100):
            exempt = amount_exempt
        total = (txt_line.tax_wh_iva_id.base + txt_line.tax_wh_iva_id.amount +
                 exempt)
        return total, exempt

    @api.model
    def get_amount_exempt_document(self, txt_line):
        """ Return total amount not entitled to tax credit and the remaining
        amounts
        @param txt_line: One line of the current txt document
        """
        tax = 0
        amount_doc = 0
        for tax_line in txt_line.invoice_id.tax_line:
            if 'SDCF' in tax_line.name or \
                    (tax_line.base and not tax_line.amount):
                tax = tax_line.base + tax
            else:
                amount_doc = tax_line.base + amount_doc
        return (tax, amount_doc)

    @api.model
    def get_alicuota(self, txt_line):
        """ Return aliquot of the withholding into line
        @param txt_line: One line of the current txt document
        """
        return int(txt_line.tax_wh_iva_id.tax_id.amount * 100)

    @api.multi
    def generate_txt(self):
        """ Return string with data of the current document
        """
        txt_string = ''
        rp_obj = self.env['res.partner']
        for txt in self:
            vat = rp_obj._find_accounting_partner(
                txt.company_id.partner_id).vat[2:]
            vat = vat
            for txt_line in txt.txt_ids:
                vendor, buyer = self.get_buyer_vendor(txt, txt_line)
                period = txt.period_id.name.split('/')
                period2 = period[0] + period[1]
                # TODO: use the start date of the period to get the period2
                # with the 'YYYYmm'
                operation_type = ('V' if txt_line.invoice_id.type in
                                  ['out_invoice', 'out_refund'] else 'C')
                document_type = self.get_type_document(txt_line)
                document_number = self.get_document_number(
                    txt_line, 'inv_number')
                control_number = self.get_number(
                    txt_line.invoice_id.nro_ctrl, 'inv_ctrl', 20)
                document_affected = self.get_document_affected(txt_line)
                voucher_number = self.get_number(
                    txt_line.voucher_id.number, 'vou_number', 14)
                amount_exempt, amount_untaxed = \
                    self.get_amount_exempt_document(txt_line)
                amount_untaxed = amount_untaxed
                alicuota = self.get_alicuota(txt_line)
                amount_total, amount_exempt = self.get_amount_line(
                    txt_line, amount_exempt)

                txt_string = (
                    txt_string + buyer + '\t' + period2.strip() + '\t' +
                    txt_line.invoice_id.date_invoice + '\t' + operation_type +
                    '\t' + document_type + '\t' + vendor + '\t' +
                    document_number + '\t' + control_number + '\t' +
                    str(round(amount_total, 2)) + '\t' +
                    str(round(txt_line.untaxed, 2)) + '\t' +
                    str(round(txt_line.amount_withheld, 2)) + '\t' +
                    document_affected + '\t' + voucher_number + '\t' +
                    str(round(amount_exempt, 2)) + '\t' + str(alicuota) +
                    '\t' + '0' + '\n')
        return txt_string

    @api.multi
    def _write_attachment(self, root, context=None):
        """ Encrypt txt, save it to the db and view it on the client as an
        attachment
        @param root: location to save document
        """
        fecha = time.strftime('%Y_%m_%d_%H%M%S')
        name = 'IVA_' + fecha + '.' + 'txt'
        self.env['ir.attachment'].create({
            'name': name,
            'datas': base64.encodestring(root),
            'datas_fname': name,
            'res_model': 'txt.iva',
            'res_id': self.ids[0],
        })
        msg = _("File TXT %s generated.") % (name)
        self.message_post(body=msg)

    @api.multi
    def action_done(self):
        """ Transfer the document status to done
        """
        root = self.generate_txt()
        self._write_attachment(root)
        self.write({'state': 'done'})

        return True
Exemplo n.º 19
0
class sla_main_mast(models.Model):
    _name='sla.main.mast'

    name = fields.Char(string='SLA')
Exemplo n.º 20
0
class BankingExportSepaWizard(models.TransientModel):
    _name = 'banking.export.sepa.wizard'
    _inherit = ['banking.export.pain']
    _description = 'Export SEPA Credit Transfer File'

    state = fields.Selection([('create', 'Create'), ('finish', 'Finish')],
                             string='State',
                             readonly=True,
                             default='create')
    batch_booking = fields.Boolean(
        string='Batch Booking',
        help="If true, the bank statement will display only one debit "
        "line for all the wire transfers of the SEPA XML file ; if "
        "false, the bank statement will display one debit line per wire "
        "transfer of the SEPA XML file.")
    charge_bearer = fields.Selection(
        [('SLEV', 'Following Service Level'), ('SHAR', 'Shared'),
         ('CRED', 'Borne by Creditor'), ('DEBT', 'Borne by Debtor')],
        string='Charge Bearer',
        default='SLEV',
        required=True,
        help="Following service level : transaction charges are to be "
        "applied following the rules agreed in the service level "
        "and/or scheme (SEPA Core messages must use this). Shared : "
        "transaction charges on the debtor side are to be borne by "
        "the debtor, transaction charges on the creditor side are to "
        "be borne by the creditor. Borne by creditor : all "
        "transaction charges are to be borne by the creditor. Borne "
        "by debtor : all transaction charges are to be borne by the "
        "debtor.")
    nb_transactions = fields.Integer(string='Number of Transactions',
                                     readonly=True)
    total_amount = fields.Float(string='Total Amount', readonly=True)
    file = fields.Binary(string="File", readonly=True)
    filename = fields.Char(string="Filename", readonly=True)
    payment_order_ids = fields.Many2many('payment.order',
                                         'wiz_sepa_payorders_rel',
                                         'wizard_id',
                                         'payment_order_id',
                                         string='Payment Orders',
                                         readonly=True)

    @api.model
    def create(self, vals):
        payment_order_ids = self._context.get('active_ids', [])
        vals.update({
            'payment_order_ids': [[6, 0, payment_order_ids]],
        })
        return super(BankingExportSepaWizard, self).create(vals)

    @api.multi
    def create_sepa(self):
        """Creates the SEPA Credit Transfer file. That's the important code!"""
        pain_flavor = self.payment_order_ids[0].mode.type.code
        convert_to_ascii = \
            self.payment_order_ids[0].mode.convert_to_ascii
        if pain_flavor == 'pain.001.001.02':
            bic_xml_tag = 'BIC'
            name_maxsize = 70
            root_xml_tag = 'pain.001.001.02'
        elif pain_flavor == 'pain.001.001.03':
            bic_xml_tag = 'BIC'
            # size 70 -> 140 for <Nm> with pain.001.001.03
            # BUT the European Payment Council, in the document
            # "SEPA Credit Transfer Scheme Customer-to-bank
            # Implementation guidelines" v6.0 available on
            # http://www.europeanpaymentscouncil.eu/knowledge_bank.cfm
            # says that 'Nm' should be limited to 70
            # so we follow the "European Payment Council"
            # and we put 70 and not 140
            name_maxsize = 70
            root_xml_tag = 'CstmrCdtTrfInitn'
        elif pain_flavor == 'pain.001.001.04':
            bic_xml_tag = 'BICFI'
            name_maxsize = 140
            root_xml_tag = 'CstmrCdtTrfInitn'
        elif pain_flavor == 'pain.001.001.05':
            bic_xml_tag = 'BICFI'
            name_maxsize = 140
            root_xml_tag = 'CstmrCdtTrfInitn'
        # added pain.001.003.03 for German Banks
        # it is not in the offical ISO 20022 documentations, but nearly all
        # german banks are working with this instead 001.001.03
        elif pain_flavor == 'pain.001.003.03':
            bic_xml_tag = 'BIC'
            name_maxsize = 70
            root_xml_tag = 'CstmrCdtTrfInitn'
        else:
            raise Warning(
                _("Payment Type Code '%s' is not supported. The only "
                  "Payment Type Codes supported for SEPA Credit Transfers "
                  "are 'pain.001.001.02', 'pain.001.001.03', "
                  "'pain.001.001.04', 'pain.001.001.05'"
                  " and 'pain.001.003.03'.") % pain_flavor)
        gen_args = {
            'bic_xml_tag':
            bic_xml_tag,
            'name_maxsize':
            name_maxsize,
            'convert_to_ascii':
            convert_to_ascii,
            'payment_method':
            'TRF',
            'file_prefix':
            'sct_',
            'pain_flavor':
            pain_flavor,
            'pain_xsd_file':
            'account_banking_sepa_credit_transfer/data/%s.xsd' % pain_flavor,
        }
        pain_ns = {
            'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
            None: 'urn:iso:std:iso:20022:tech:xsd:%s' % pain_flavor,
        }
        xml_root = etree.Element('Document', nsmap=pain_ns)
        pain_root = etree.SubElement(xml_root, root_xml_tag)
        pain_03_to_05 = [
            'pain.001.001.03', 'pain.001.001.04', 'pain.001.001.05',
            'pain.001.003.03'
        ]
        # A. Group header
        group_header_1_0, nb_of_transactions_1_6, control_sum_1_7 = \
            self.generate_group_header_block(pain_root, gen_args)
        transactions_count_1_6 = 0
        total_amount = 0.0
        amount_control_sum_1_7 = 0.0
        lines_per_group = {}
        # key = (requested_date, priority)
        # values = list of lines as object
        for payment_order in self.payment_order_ids:
            total_amount = total_amount + payment_order.total
            for line in payment_order.bank_line_ids:
                priority = line.priority
                # The field line.date is the requested payment date
                # taking into account the 'date_prefered' setting
                # cf account_banking_payment_export/models/account_payment.py
                # in the inherit of action_open()
                key = (line.date, priority)
                if key in lines_per_group:
                    lines_per_group[key].append(line)
                else:
                    lines_per_group[key] = [line]
        for (requested_date, priority), lines in lines_per_group.items():
            # B. Payment info
            payment_info_2_0, nb_of_transactions_2_4, control_sum_2_5 = \
                self.generate_start_payment_info_block(
                    pain_root,
                    "self.payment_order_ids[0].reference + '-' "
                    "+ requested_date.replace('-', '')  + '-' + priority",
                    priority, False, False, requested_date, {
                        'self': self,
                        'priority': priority,
                        'requested_date': requested_date,
                    }, gen_args)
            self.generate_party_block(payment_info_2_0, 'Dbtr', 'B',
                                      self.payment_order_ids[0].mode.bank_id,
                                      gen_args)
            charge_bearer_2_24 = etree.SubElement(payment_info_2_0, 'ChrgBr')
            charge_bearer_2_24.text = self.charge_bearer
            transactions_count_2_4 = 0
            amount_control_sum_2_5 = 0.0
            for line in lines:
                transactions_count_1_6 += 1
                transactions_count_2_4 += 1
                # C. Credit Transfer Transaction Info
                credit_transfer_transaction_info_2_27 = etree.SubElement(
                    payment_info_2_0, 'CdtTrfTxInf')
                payment_identification_2_28 = etree.SubElement(
                    credit_transfer_transaction_info_2_27, 'PmtId')
                end2end_identification_2_30 = etree.SubElement(
                    payment_identification_2_28, 'EndToEndId')
                end2end_identification_2_30.text = self._prepare_field(
                    'End to End Identification',
                    'line.name', {'line': line},
                    35,
                    gen_args=gen_args)
                currency_name = self._prepare_field('Currency Code',
                                                    'line.currency.name',
                                                    {'line': line},
                                                    3,
                                                    gen_args=gen_args)
                amount_2_42 = etree.SubElement(
                    credit_transfer_transaction_info_2_27, 'Amt')
                instructed_amount_2_43 = etree.SubElement(amount_2_42,
                                                          'InstdAmt',
                                                          Ccy=currency_name)
                instructed_amount_2_43.text = '%.2f' % line.amount_currency
                amount_control_sum_1_7 += line.amount_currency
                amount_control_sum_2_5 += line.amount_currency
                if not line.bank_id:
                    raise Warning(
                        _("Bank account is missing on the bank payment line "
                          "of partner '%s' (reference '%s').") %
                        (line.partner_id.name, line.name))
                self.generate_party_block(
                    credit_transfer_transaction_info_2_27, 'Cdtr', 'C',
                    line.bank_id, gen_args)
                self.generate_remittance_info_block(
                    credit_transfer_transaction_info_2_27, line, gen_args)
            if pain_flavor in pain_03_to_05:
                nb_of_transactions_2_4.text = unicode(transactions_count_2_4)
                control_sum_2_5.text = '%.2f' % amount_control_sum_2_5
        if pain_flavor in pain_03_to_05:
            nb_of_transactions_1_6.text = unicode(transactions_count_1_6)
            control_sum_1_7.text = '%.2f' % amount_control_sum_1_7
        else:
            nb_of_transactions_1_6.text = unicode(transactions_count_1_6)
            control_sum_1_7.text = '%.2f' % amount_control_sum_1_7
        return self.finalize_sepa_file_creation(xml_root, total_amount,
                                                transactions_count_1_6,
                                                gen_args)

    @api.multi
    def save_sepa(self):
        """Save the SEPA file: send the done signal to all payment
        orders in the file. With the default workflow, they will
        transition to 'done', while with the advanced workflow in
        account_banking_payment they will transition to 'sent' waiting
        reconciliation.
        """
        for order in self.payment_order_ids:
            workflow.trg_validate(self._uid, 'payment.order', order.id, 'done',
                                  self._cr)
            self.env['ir.attachment'].create({
                'res_model': 'payment.order',
                'res_id': order.id,
                'name': self.filename,
                'datas': self.file,
            })
        return True
Exemplo n.º 21
0
class AccountOperationTemplate(models.Model):
    _name = "account.operation.template"
    _description = "Preset to create journal entries during a invoices and payments matching"

    name = fields.Char(string='Button Label', required=True)
    sequence = fields.Integer(required=True, default=10)
    has_second_line = fields.Boolean(string='Add a second line', default=False)
    company_id = fields.Many2one('res.company',
                                 string='Company',
                                 required=True,
                                 default=lambda self: self.env.user.company_id)

    account_id = fields.Many2one('account.account',
                                 string='Account',
                                 ondelete='cascade',
                                 domain=[('deprecated', '=', False)])
    journal_id = fields.Many2one(
        'account.journal',
        string='Journal',
        ondelete='cascade',
        help="This field is ignored in a bank statement reconciliation.")
    label = fields.Char(string='Journal Item Label')
    amount_type = fields.Selection([('fixed', 'Fixed'),
                                    ('percentage', 'Percentage of balance')],
                                   required=True,
                                   default='percentage')
    amount = fields.Float(
        digits=0,
        required=True,
        default=100.0,
        help=
        "Fixed amount will count as a debit if it is negative, as a credit if it is positive."
    )
    tax_id = fields.Many2one('account.tax',
                             string='Tax',
                             ondelete='restrict',
                             domain=[('type_tax_use', '=', 'purchase')])
    analytic_account_id = fields.Many2one('account.analytic.account',
                                          string='Analytic Account',
                                          ondelete='set null',
                                          domain=[('account_type', '=',
                                                   'normal')])

    second_account_id = fields.Many2one('account.account',
                                        string='Account',
                                        ondelete='cascade',
                                        domain=[('deprecated', '=', False)])
    second_journal_id = fields.Many2one(
        'account.journal',
        string='Journal',
        ondelete='cascade',
        help="This field is ignored in a bank statement reconciliation.")
    second_label = fields.Char(string='Journal Item Label')
    second_amount_type = fields.Selection(
        [('fixed', 'Fixed'), ('percentage', 'Percentage of amount')],
        string='Amount type',
        required=True,
        default='percentage')
    second_amount = fields.Float(
        string='Amount',
        digits=0,
        required=True,
        default=100.0,
        help=
        "Fixed amount will count as a debit if it is negative, as a credit if it is positive."
    )
    second_tax_id = fields.Many2one('account.tax',
                                    string='Tax',
                                    ondelete='restrict',
                                    domain=[('type_tax_use', '=', 'purchase')])
    second_analytic_account_id = fields.Many2one('account.analytic.account',
                                                 string='Analytic Account',
                                                 ondelete='set null',
                                                 domain=[('account_type', '=',
                                                          'normal')])

    @api.onchange('name')
    def onchange_name(self):
        self.label = self.name
Exemplo n.º 22
0
class wizard_ntty_product_import(models.TransientModel):
    _name = 'wizard.ntty.product.import'

    @api.one
    def _compute_enable_supplier_button(self):
        return_value = False
        ntty = self.env['ntty.config.settings'].search([])
        # ntty = self.env['ntty.config.settings'].browse(1)
        if not ntty:
            raise osv.except_osv('Error', 'Connection not defined')
            return_value = False
        if ntty and not ntty.ntty_check_supliers:
            return_value = True
        self.enable_supplier_button = return_value

    @api.one
    def _compute_len_detail_ids(self):
        self.len_detail_ids = len(self.detail_ids)

    ntty_id = fields.Char('NTTY ID')
    detail_ids = fields.One2many(
        comodel_name='wizard.ntty.product.import.detail',
        inverse_name='import_id')
    ntty_data = fields.Text('NTTY Data')
    certifications = fields.Text('Certifications')
    sqm_pcb = fields.Char('SQM PCB')
    enable_supplier_button = fields.Boolean(
        string='Enable Supplier Button',
        default=True,
        compute=_compute_enable_supplier_button)
    len_detail_ids = fields.Integer(string='len_detail_ids',
                                    compute=_compute_len_detail_ids)

    @api.multi
    def create_ntty_products(self):
        ntty_id = self.ntty_id
        if not ntty_id:
            raise osv.except_osv(('Error'), ('Please enter a NTTY ID'))
            return None

        ntty = self.env['ntty.config.settings'].search([])
        # ntty = self.env['ntty.config.settings'].browse(1)
        if not ntty:
            return None
        ntty_service_address = ntty.ntty_service_address
        ntty_service_user_email = ntty.ntty_service_user_email
        ntty_service_token = ntty.ntty_service_token

        ntty_check_supliers = ntty.ntty_check_supliers
        ntty_generate_price_list = ntty.ntty_generate_price_list
        ntty_related_products = ntty.ntty_related_products
        ntty_partner_info = ntty.ntty_partner_info
        ntty_product_category = ntty.ntty_product_category
        ntty_supplier_short_name = ntty.ntty_supplier_short_name

        if len(self.detail_ids) == 0 and ntty_check_supliers:
            raise osv.except_osv(
                ('Error'), ('Please enter a NTTY ID and pull its suppliers'))
            return None

        identifier = self.ntty_id
        ntty_data = self.ntty_data
        if not ntty_data:

            httplib.HTTPConnection._http_vsn = 10
            httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0'

            req = urllib2.Request(
                str(ntty_service_address) + "/entities/" + str(ntty_id))
            req.add_header('X-User-Email', str(ntty_service_user_email))
            req.add_header('X-User-Token', str(ntty_service_token))
            try:
                resp = urllib2.urlopen(req)
            except StandardError:
                raise except_orm(_('Warning'), _("Error connecting to NTTY."))
                return False

            if not resp.code == 200 and resp.msg == "OK":
                raise except_orm(_('Warning'), _("Unable to connect to NTTY."))
                return {}

            content = resp.read()
            res = json.loads(content)
        else:
            identifier = self.ntty_id
            res = ast.literal_eval(self.ntty_data)
        if not ntty_check_supliers:
            vals_detail = {
                'import_id': self.id,
                'ntty_partner_id': -999,
                'selected': 'yes'
            }
            self.env['wizard.ntty.product.import.detail'].create(vals_detail)

    # routes = self.env['stock.location.route'].search(['|',('name','=','Make To Order'),('name','=','Buy')])
        routes = self.env['stock.location.route'].search([('name', '=', 'Buy')
                                                          ])
        if not routes:
            raise osv.except_osv(('Error'),
                                 ('Routes MTO and Buy are not present!!!'))
            return None
        route_ids = [x.id for x in routes]

        created_products = []
        for detail in self.detail_ids:
            ntty_pull_log = ''
            if detail.selected == 'yes':
                entity = res['entity']
                ntty_pull_log = res['entity']
                part_numbers = res['part_numbers']
                temp_wcp_weight = entity['values'].get('wcp_weight', False)
                if not temp_wcp_weight:
                    weight = 0
                else:
                    try:
                        weight = float(
                            temp_wcp_weight[:temp_wcp_weight.find(' ')])
                    except:
                        weight = 0
                        pass
                flag_ul = False
                flag_rohs = False
                flag_defense = False
                flag_automotive = False
                certifications = entity['values'].get('certifications', False)
                categ_id = 1
                certification_technology = ''
                if certifications and ntty_product_category:
                    for certification in certifications:
                        if certification['id'] == 131:
                            flag_rohs = True
                        if certification['id'] == 132:
                            flag_ul = True

#if 'Automotive' in certification['name']:
#        flag_automotive = True
#        category_id = self.env['product.category'].search([('name','=','Automotive')])
#        if category_id:
#                categ_id = category_id.id
#if 'Defense' in certification['name']:
#        flag_defense = True
#        category_id = self.env['product.category'].search([('name','=','Defense')])
#        if category_id:
#                categ_id = category_id.id
                        temp_supplier = detail.partner_id
                        supplier_currency = temp_supplier.property_product_pricelist_purchase.currency_id.name
                        pcb_category_id = self.env['product.category'].search([
                            ('name', '=', 'PCB')
                        ])
                        currency_category_id = self.env[
                            'product.category'].search([('name', '=',
                                                         supplier_currency)])
                        if not pcb_category_id:
                            vals_pcb_category = {
                                'name': 'PCB',
                                'parent_id': 1,
                            }
                            pcb_category_id = self.env[
                                'product.category'].create(vals_pcb_category)
                        if not currency_category_id:
                            vals_currency_category = {
                                'name': supplier_currency,
                                'parent_id': pcb_category_id.id
                            }
                            currency_category_id = self.env[
                                'product.category'].create(
                                    vals_currency_category)
                            pcb_category_id = currency_category_id.id
                            categ_id = currency_category_id.id
                        else:
                            if len(currency_category_id) > 1:
                                for currency_category in currency_category_id:
                                    if currency_category.parent_id.id == pcb_category_id.id:
                                        pcb_category_id = currency_category.id
                                        categ_id = currency_category.id
                                        break
                            else:
                                pcb_category_id = currency_category_id.id
                                categ_id = currency_category_id.id
                        try:
                            if certification[
                                    'certification_type'] == 'Technology':
                                certification_technology = certification[
                                    'name']
                            if certification[
                                    'certification_type'] == 'Category':
                                product_category = self.env['product.category'].search(\
                                 [('name','=',certification['name'])])
                                parent_category = self.env['product.category'].search(\
                                 [('name','=',certification_technology)])
                                parent_category_id = None
                                if not parent_category:
                                    vals_parent_category = {
                                        'name': certification_technology,
                                        'parent_id': pcb_category_id,
                                    }
                                    parent_category = self.env[
                                        'product.category'].create(
                                            vals_parent_category)
                                    parent_category = parent_category.id
                                else:
                                    parent_category = parent_category.id
                                if product_category and len(
                                        product_category) == 1:
                                    categ_id = product_category.id
                                else:
                                    if certification_technology == '':
                                        for cert in certifications:
                                            if cert['certification_type'] == 'Technology':
                                                certification_technology = cert[
                                                    'name']
                                                parent_category = self.env['product.category'].search(\
                                                [('name','=',certification_technology)])
                                                parent_category_id = None
                                                if not parent_category:
                                                    vals_parent_category = {
                                                        'name':
                                                        certification_technology,
                                                        'parent_id':
                                                        pcb_category_id,
                                                    }
                                                    parent_category = self.env[
                                                        'product.category'].create(
                                                            vals_parent_category
                                                        )
                                                    parent_category = parent_category.id
                                                else:
                                                    parent_category = parent_category.id

                                    vals_category = {
                                        'name': certification['name'],
                                        'parent_id': parent_category,
                                    }
                                    category = self.env[
                                        'product.category'].create(
                                            vals_category)
                                    categ_id = category.id
                        except:
                            continue

    # Searches for product_owner
                product_brand = entity.get('product_owner', '')
                product_brand_text = entity.get('product_owner', '')
                lifecycle_blocks_orders = entity.get('lifecycle_blocks_orders',
                                                     False)
                lifecycle_blocks_quotes = entity.get('lifecycle_blocks_quotes',
                                                     False)
                lifecycle = entity.get('lifecycle', False)
                product_brand_id = None
                if product_brand:
                    product_brand_id = self.env['product.brand'].search([
                        ('name', '=', product_brand)
                    ])
                    if product_brand_id:
                        product_brand_id = product_brand_id[0].id
                    else:
                        vals_brand = {'name': product_brand}
                        product_brand_id = self.env['product.brand'].create(
                            vals_brand)
                        product_brand_id = product_brand_id.id

            # article_part_number = entity.get('article_part_number','')
                article_part_number = entity.get('name', 'N/A')
                identifier = entity.get('identifier', '')
                article_part_name = entity.get('article_part_name',
                                               'article_part_number')
                ntty_name = entity.get('article_part_name', 'N/A')
                short_description = entity.get('short_description', '')
                long_description = entity.get('long_description', '')
                article_id = entity.get('article_id', '')
                panel_factor = int(entity['values'].get(
                    'panel_units', entity['values'].get('units', 1)))

                try:
                    panel_weight = float(
                        entity['values']['weight_calculation_panel'][0]
                        ['panel_weight'])
                except KeyError:
                    panel_weight = 1.00

                try:
                    panel_length = float(entity['values']['pcb_length'])
                except KeyError:
                    panel_length = 0.00

                try:
                    panel_width = float(entity['values']['pcb_width'])
                except KeyError:
                    panel_width = 0.00

                try:
                    sqm_pcb = float(entity['values']['sqm_pcb'])
                except KeyError:
                    sqm_pcb = 0.00

                try:
                    signal_layers = float(entity['values']['signal_layers'])
                except KeyError:
                    signal_layers = 0.00

                try:
                    weight_calc = (panel_weight / panel_factor) / 1000
                except FloatingPointError:
                    weight_calc = 0.00

                try:
                    files = entity['files']
                    for file in files:
                        print file['url']
                except KeyError:
                    files = {}
                vals = {
                    'name': article_part_name,
                    'ntty_name': ntty_name,
                    'ntty_pull_log': ntty_pull_log,
                    'article_part_number': article_part_number,
                    'description': long_description,
                    'ntty_id': identifier,
                    'type': "product",
                    'panel_factor': panel_factor,
                    'panel_weight': panel_weight,
                    'panel_width': panel_width,
                    'panel_length': panel_length,
                    'weight_net': weight_calc,
                    'weight': weight_calc,
                    'sqm_pcb': sqm_pcb,
                    'signal_layers': signal_layers,
                    'product_brand_id': product_brand_id,
                    'product_brand_text': product_brand_text,
                    'ntty_rohs': flag_rohs,
                    'lifecycle_blocks_orders': lifecycle_blocks_orders,
                    'lifecycle_blocks_quotes': lifecycle_blocks_quotes,
                    'lifecycle': lifecycle,
                    'ntty_ul': flag_ul,
                    'sale_delay': 0,
                    'categ_id': categ_id,
                    'weight': weight / 1000,
                }
                product_code = ''
                part_name = ''
                part_description = ''
                default_code = ''

                # This is the place
                # prod = self.env['product.template'].search([('ntty_id', '=', identifier)])

                for part_number in part_numbers:
                    if part_number['part_organization'] == 'ELMATICA AS':
                        default_code = part_number.get('part_number', '')
                    if part_number['part_organization'] != 'ELMATICA AS'\
        and part_number['part_organization'] != product_brand_text:
                        part_name = part_number.get('part_name', '')
                        part_description = part_number.get(
                            'part_description', '')
                        product_code = part_number.get('part_number', '')
                        vals['product_code'] = product_code
                        default_code = product_code
                vals['default_code'] = default_code
                if detail.partner_id.short_name and ntty_supplier_short_name and ntty_check_supliers:
                    vals[
                        'name'] = article_part_number + ' ' + product_code + ' ' + detail.partner_id.short_name
                else:
                    if detail.partner_id.name:
                        vals[
                            'name'] = article_part_number + ' ' + product_code + ' ' + detail.partner_id.name
                    else:
                        vals['name'] = article_part_number + ' ' + product_code
                vals['description'] = part_description,
                identifier_odoo = identifier + '#' + str(
                    detail.partner_id.ntty_partner_id)
                prod = self.env['product.product'].search([('ntty_odoo', '=',
                                                            identifier_odoo)])
                vals['ntty_odoo'] = identifier_odoo
                vals['ntty_id'] = identifier
                if ntty.ntty_mto:
                    #routes = self.env['stock.location.route'].search([('name','=','Make To Order')])
                    #route_ids = []
                    #if routes:
                    #	for route in routes:
                    #		route_ids.append(route.id)
                    vals['route_ids'] = [(6, 0, route_ids)]
                if ntty.ntty_sold:
                    vals['sale_ok'] = True
                else:
                    vals['sale_ok'] = False
                if ntty.ntty_purchased:
                    vals['purchase_ok'] = True
                else:
                    vals['purchase_ok'] = False

                if not prod:
                    prod = prod.create(vals)
                else:
                    if len(prod) > 1:
                        raise osv.except_osv(('Error'), (
                            'More than one product/supplier for this NTTY ID'))
                        return None
                    prod.write(vals)
                created_products.append(prod)
                # Check suppliers setting
                if ntty_check_supliers:
                    vals_supplier = {
                        'name': detail.partner_id.id,
                        'company_id': 1,
                        'product_tmpl_id': prod.product_tmpl_id.id,
                    }
                    prod_sup = self.env['product.supplierinfo'].search([('name','=',detail.partner_id.id),\
                     ('product_tmpl_id','=',prod.product_tmpl_id.id)])
                    if not prod_sup:
                        prod_sup = self.env['product.supplierinfo'].create(
                            vals_supplier)
                # Creates attributes
                for entity_values_key in entity['values'].keys():
                    value = entity['values'][entity_values_key]
                    attribute_id = self.env['product.attribute'].search([
                        ('name', '=', entity_values_key)
                    ])
                    if attribute_id:
                        value_id = self.env['product.attribute.value'].\
                         search([('attribute_id','=',attribute_id.id),\
                          ('name','=',str(value))])
                        if not value_id:
                            vals_value = {
                                'attribute_id': attribute_id.id,
                                'name': str(value),
                            }
                            value_id = self.env[
                                'product.attribute.value'].create(vals_value)
                            value_id = value_id.id
                        else:
                            value_id = value_id.id
                        vals_attribute_line = {
                            'product_tmpl_id': prod.product_tmpl_id.id,
                            'attribute_id': attribute_id.id,
                            'value_ids': [(6, 0, [value_id])]
                        }
                        attribute_line_id = self.env['product.attribute.line'].\
                         create(vals_attribute_line)

        for detail in self.detail_ids:
            if detail.selected == 'no' and detail.partner_id:
                prod_sup = self.env['product.supplierinfo'].search([
                    ('name', '=', detail.partner_id.id)
                ])
                po_sup = self.env['purchase.order'].search([
                    ('partner_id', '=', detail.partner_id.id)
                ])
                inv_sup = self.env['account.invoice'].search([
                    ('partner_id', '=', detail.partner_id.id)
                ])
                so_sup = self.env['sale.order'].search(['|',('partner_invoice_id','=',detail.partner_id.id),\
                      ('partner_id','=',detail.partner_id.id)])
                if not prod_sup and not po_sup and not inv_sup and not so_sup:
                    supplier_to_delete = detail.partner_id
                    try:
                        supplier_to_delete.unlink()
                    except:
                        pass
            else:
                if detail.ntty_partner_id != -999:
                    detail.partner_id.message_post(
                        body="Supplier created. Needs setup", context={})

        if ntty_related_products:
            for prod in created_products:
                related_products = self.env['product.product'].search([
                    ('ntty_id', '=', self.ntty_id)
                ])
                related = [related_product.product_tmpl_id.id for related_product in related_products \
                 if prod.product_tmpl_id.id != related_product.product_tmpl_id.id]
                vals = {
                    'alternative_product_ids': [(6, 0, related)],
                }
                product_template = prod.product_tmpl_id
                product_template.write(vals)
        res = {
            "name": "product.template.ntty.pull" + str(product_template.id),
            "type": "ir.actions.act_window",
            "res_model": "product.template",
            "view_type": "form",
            "view_mode": "form",
            #"view_id": "product.product_template_form_view",
            "res_id": product_template.id,
            "nodestroy": True,
        }
        return res

    @api.multi
    def pull_ntty_suppliers(self):
        ntty_id = self.ntty_id
        if not ntty_id:
            raise osv.except_osv(('Error'), ('Please enter a NTTY ID'))
            return None
        ntty = self.env['ntty.config.settings'].search([])
        # ntty = self.env['ntty.config.settings'].browse(1)
        if not ntty:
            return None
        ntty_service_address = ntty.ntty_service_address
        ntty_service_user_email = ntty.ntty_service_user_email
        ntty_service_token = ntty.ntty_service_token

        httplib.HTTPConnection._http_vsn = 10
        httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0'

        req = urllib2.Request(
            str(ntty_service_address) + "/entities/" + str(ntty_id))
        req.add_header('X-User-Email', str(ntty_service_user_email))
        req.add_header('X-User-Token', str(ntty_service_token))
        try:
            resp = urllib2.urlopen(req)
        except StandardError:
            raise except_orm(_('Warning'), _("Error connecting to NTTY."))
            return False

        if not resp.code == 200 and resp.msg == "OK":
            raise except_orm(_('Warning'), _("Unable to connect to NTTY."))
            return {}

        content = resp.read()
        res = json.loads(content)
        self.write({'ntty_data': res})
        if res:
            if not ntty.ntty_check_supliers:
                return {
                    'type': 'ir.actions.act_window',
                    'res_model': self._name,  # this model
                    'res_id': self.id,  # the current wizard record
                    'view_type': 'form',
                    'view_mode': 'form',
                    'target': 'new'
                }

            import_id = self.id
            try:
                suppliers = res['entity']['values'].get(
                    'supplier_matching', [])
            except:
                suppliers = []
            try:
                sqm_pcb = res['entity']['values']['sqm_pcb']
            except:
                sqm_pcb = 0
            try:
                res_certifications = res['entity']['values']['certifications']
            except:
                res_certifications = []
            certifications = ''
            for certification in res_certifications:
                certifications = certifications + ' - ' + certification['name']
            self.write({'sqm_pcb': sqm_pcb, 'certifications': certifications})
            for supplier in suppliers:
                supplier_id = self.env['res.partner'].search([
                    ('ntty_partner_id', '=', supplier['id'])
                ])
                if not supplier_id:
                    sup_odoo = self.env['res.partner'].create({'supplier': True, \
                                                        'is_company': True, 'name': supplier['name'], 'ntty_partner_id': supplier['id'], \
                                                        'partner_approved': True, 'active': True})
                    # sup_odoo.message_post(body="Supplier created. Needs setup", context={})
                    supplier_id = sup_odoo.id
                else:
                    if len(supplier_id) > 1:
                        supplier_id = supplier_id[0].id
                    else:
                        supplier_id = supplier_id.id
                vals_sup = {
                    'import_id': import_id,
                    'partner_id': supplier_id,
                    'ntty_partner_id': supplier['id'],
                    'selected': 'no',
                }
                return_id = self.env[
                    'wizard.ntty.product.import.detail'].create(vals_sup)

        # return True
        return {
            'type': 'ir.actions.act_window',
            'res_model': self._name,  # this model
            'res_id': self.id,  # the current wizard record
            'view_type': 'form',
            'view_mode': 'form',
            'target': 'new'
        }
Exemplo n.º 23
0
class AccountAccount(models.Model):
    _name = "account.account"
    _description = "Account"
    _order = "code"

    #@api.multi
    #def _compute_has_unreconciled_entries(self):
    #    print "ici dedans"
    #    account_ids = self.ids
    #    for account in self:
    #        # Avoid useless work if has_unreconciled_entries is not relevant for this account
    #        if account.deprecated or not account.reconcile:
    #            account.has_unreconciled_entries = False
    #            account_ids = account_ids - account
    #    if account_ids:
    #        res = dict.fromkeys([x.id for x in account_ids], False)
    #        self.env.cr.execute(
    #            """ SELECT s.account_id FROM(
    #                    SELECT
    #                        a.id as account_id, a.last_time_entries_checked AS last_time_entries_checked,
    #                        MAX(l.write_date) AS max_date
    #                    FROM
    #                        account_move_line l
    #                        RIGHT JOIN account_account a ON (a.id = l.account_id)
    #                    WHERE
    #                        a.id in %s
    #                        AND EXISTS (
    #                            SELECT 1
    #                            FROM account_move_line l
    #                            WHERE l.account_id = a.id
    #                            AND l.amount_residual > 0
    #                        )
    #                        AND EXISTS (
    #                            SELECT 1
    #                            FROM account_move_line l
    #                            WHERE l.account_id = a.id
    #                            AND l.amount_residual < 0
    #                        )
    #                    GROUP BY a.id, a.last_time_entries_checked
    #                ) as s
    #                WHERE (last_time_entries_checked IS NULL OR max_date > last_time_entries_checked)
    #            """ % (account_ids,))
    #        res.update(self.env.cr.dictfetchall())
    #        for account in self.browse(res.keys()):
    #            if res[account.id]:
    #                account.has_unreconciled_entries = True

    @api.multi
    @api.constrains('internal_type', 'reconcile')
    def _check_reconcile(self):
        for account in self:
            if account.internal_type in (
                    'receivable', 'payable') and account.reconcile == False:
                raise ValueError(
                    _('You cannot have a receivable/payable account that is not reconciliable. (account code: %s)'
                      ) % account.code)

    name = fields.Char(required=True, index=True)
    currency_id = fields.Many2one(
        'res.currency',
        string='Account Currency',
        help="Forces all moves for this account to have this account currency."
    )
    code = fields.Char(size=64, required=True, index=True)
    deprecated = fields.Boolean(index=True, default=False)
    user_type_id = fields.Many2one(
        'account.account.type',
        string='Type',
        required=True,
        oldname="user_type",
        help=
        "Account Type is used for information purpose, to generate country-specific legal reports, and set the rules to close a fiscal year and generate opening entries."
    )
    internal_type = fields.Selection(related='user_type_id.type',
                                     store=True,
                                     readonly=True)
    #has_unreconciled_entries = fields.Boolean(compute='_compute_has_unreconciled_entries',
    #    help="The account has at least one unreconciled debit and credit since last time the invoices & payments matching was performed.")
    last_time_entries_checked = fields.Datetime(string='Latest Invoices & Payments Matching Date', readonly=True, copy=False,
        help='Last time the invoices & payments matching was performed on this account. It is set either if there\'s not at least '\
        'an unreconciled debit and an unreconciled credit Or if you click the "Done" button.')
    reconcile = fields.Boolean(
        string='Allow Reconciliation',
        default=False,
        help=
        "Check this box if this account allows invoices & payments matching of journal items."
    )
    tax_ids = fields.Many2many('account.tax',
                               'account_account_tax_default_rel',
                               'account_id',
                               'tax_id',
                               string='Default Taxes')
    note = fields.Text('Internal Notes')
    company_id = fields.Many2one('res.company',
                                 string='Company',
                                 required=True,
                                 default=lambda self: self.env['res.company'].
                                 _company_default_get('account.account'))
    tag_ids = fields.Many2many(
        'account.account.tag',
        'account_account_account_tag',
        string='Tags',
        help="Optional tags you may want to assign for custom reporting")

    _sql_constraints = [
        ('code_company_uniq', 'unique (code,company_id)',
         'The code of the account must be unique per company !')
    ]

    @api.model
    def name_search(self, name, args=None, operator='ilike', limit=100):
        args = args or []
        domain = []
        if name:
            domain = [
                '|', ('code', '=ilike', name + '%'), ('name', operator, name)
            ]
            if operator in expression.NEGATIVE_TERM_OPERATORS:
                domain = ['&', '!'] + domain[1:]
        accounts = self.search(domain + args, limit=limit)
        return accounts.name_get()

    @api.onchange('internal_type')
    def onchange_internal_type(self):
        if self.internal_type in ('receivable', 'payable'):
            self.reconcile = True

    @api.multi
    @api.depends('name', 'code')
    def name_get(self):
        result = []
        for account in self:
            name = account.code + ' ' + account.name
            result.append((account.id, name))
        return result

    @api.one
    def copy(self, default=None):
        default = dict(default or {})
        default.update(code=_("%s (copy)") % (self.code or ''))
        return super(AccountAccount, self).copy(default)

    @api.multi
    def write(self, vals):
        # Dont allow changing the company_id when account_move_line already exist
        if vals.get('company_id', False):
            move_lines = self.env['account.move.line'].search(
                [('account_id', 'in', self.ids)], limit=1)
            for account in self:
                if (account.company_id.id <>
                        vals['company_id']) and move_lines:
                    raise UserError(
                        _('You cannot change the owner company of an account that already contains journal items.'
                          ))
        return super(AccountAccount, self).write(vals)

    @api.multi
    def unlink(self):
        if self.env['account.move.line'].search(
            [('account_id', 'in', self.ids)], limit=1):
            raise UserError(
                _('You cannot do that on an account that contains journal items.'
                  ))
        #Checking whether the account is set as a property to any Partner or not
        values = [
            'account.account,%s' % (account_id, ) for account_id in self.ids
        ]
        partner_prop_acc = self.env['ir.property'].search(
            [('value_reference', 'in', values)], limit=1)
        if partner_prop_acc:
            raise UserError(
                _('You cannot remove/deactivate an account which is set on a customer or vendor.'
                  ))
        return super(AccountAccount, self).unlink()

    @api.multi
    def mark_as_reconciled(self):
        return self.write({
            'last_time_entries_checked':
            time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
        })

    @api.multi
    def action_open_reconcile(self):
        # Open reconciliation view for this account
        action_context = {
            'show_mode_selector': False,
            'account_ids': [
                self.id,
            ]
        }
        return {
            'type': 'ir.actions.client',
            'tag': 'manual_reconciliation_view',
            'context': action_context,
        }
Exemplo n.º 24
0
class MilkControlReport(models.Model):

    _name = 'milk.control.report'

    exploitation_1 = fields.Many2one('res.partner', 'Exploitation 1',
                                     required=True,
                                     domain=[('farm', '=', True),
                                             ('is_cooperative','=',False)])
    exploitation_2 = fields.Many2one('res.partner', 'Exploitation 2',
                                     domain=[('farm', '=', True),
                                             ('is_cooperative','=',False)])
    from_date = fields.Date('From date', required=True)
    to_date = fields.Date('To date', required=True)

    milking_type_1 = fields.Selection(MILKING_TYPES, required=True)
    milking_type_2 = fields.Selection(MILKING_TYPES)
    primerizas_cant_1 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_del_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_litros_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_grasa_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_proteina_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_celulas_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_urea_1 = fields.Float()
    adultas_cant_1 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_del_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_litros_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_grasa_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_proteina_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_celulas_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_urea_1 = fields.Float()
    total_cant_1 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_del_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_litros_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_grasa_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_proteina_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_celulas_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_urea_1 = fields.Float()
    vacas_invertidas_1 = fields.Char(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    coeficiente_persistencia_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    leche_corregida_a_del_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    prod_media_lact_acumulada_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    num_medio_partos_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    relacion_grasa_prot_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    pico_novillas_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    pico_novillas_del_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    num_novillas_pico_del_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    pico_adultas_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    pico_adultas_del_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    num_adultas_pico_del_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    relacion_entre_picos_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    prim_terc_lact_num_animales_1 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    prim_terc_lact_porcen_animales_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    prim_terc_lact_media_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    prim_terc_lact_litros_total_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    seg_terc_lact_num_animales_1 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    seg_terc_lact_porcen_animales_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    seg_terc_lact_media_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    seg_terc_lact_litros_total_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    terc_terc_lact_num_animales_1 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    terc_terc_lact_porcen_animales_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    terc_terc_lact_media_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    terc_terc_lact_litros_total_1 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_cant_2 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_del_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_litros_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_grasa_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_proteina_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_celulas_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    primerizas_urea_2 = fields.Float()
    adultas_cant_2 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_del_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_litros_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_grasa_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_proteina_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_celulas_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    adultas_urea_2 = fields.Float()
    total_cant_2 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_del_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_litros_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_grasa_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_proteina_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_celulas_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    total_urea_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    vacas_invertidas_2 = fields.Char(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    coeficiente_persistencia_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    leche_corregida_a_del_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    prod_media_lact_acumulada_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    num_medio_partos_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    relacion_grasa_prot_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    pico_novillas_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    pico_novillas_del_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    num_novillas_pico_del_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    pico_adultas_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    pico_adultas_del_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    num_adultas_pico_del_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    relacion_entre_picos_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    prim_terc_lact_num_animales_2 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    prim_terc_lact_porcen_animales_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    prim_terc_lact_media_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    prim_terc_lact_litros_total_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    seg_terc_lact_num_animales_2 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    seg_terc_lact_porcen_animales_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    seg_terc_lact_media_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    seg_terc_lact_litros_total_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    terc_terc_lact_num_animales_2 = fields.Integer(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    terc_terc_lact_porcen_animales_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    terc_terc_lact_media_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    terc_terc_lact_litros_total_2 = fields.Float(compute='_calculate_vals', digits=dp.get_precision('milk_report'))
    graphic_img = fields.Binary(compute='_calculate_vals')

    def _get_company(self):
        return self.env.user.company_id

    company_id = fields.Many2one('res.company', 'Company', default=_get_company)


    @api.multi
    def _get_data(self, num):
        self.ensure_one()
        suffix = u'_%s' % num
        milk_control = self.env['milk.control'].search([('date', '>=', self.from_date), ('date', '<=', self.to_date), ('exploitation_id', '=', self['exploitation%s' % suffix].id)])
        milk_data = milk_control.mapped('line_ids')
        if not milk_data:
            return
        self['primerizas_cant%s' % suffix] = sum([1 for x in milk_data if x.cib and x.birth_number == 1])
        self['primerizas_del%s' % suffix] = average([x.days for x in milk_data if x.birth_number == 1])
        self['primerizas_litros%s' % suffix] = average([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.birth_number == 1])
        self['primerizas_grasa%s' % suffix] = (sum([x.get_liters(self['milking_type%s' % suffix]) * (x.fat / 100) for x in milk_data if x.birth_number == 1 and x.fat > 0]) / (sum([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.fat > 0 and x.birth_number == 1]) or 1.0)) * 100
        self['primerizas_proteina%s' % suffix] = (sum([x.get_liters(self['milking_type%s' % suffix]) * (x.protein / 100) for x in milk_data if x.birth_number == 1 and x.protein > 0]) / (sum([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.protein > 0 and x.birth_number == 1]) or 1.0)) * 100
        self['primerizas_celulas%s' % suffix] = sum([x.rcs * 1000 * x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.birth_number == 1]) / ((sum([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.birth_number == 1 and x.fat > 0]) or 1.0) * 1000)


        self['adultas_cant%s' % suffix] = sum([1 for x in milk_data if x.birth_number > 1])
        self['adultas_del%s' % suffix] = average([x.days for x in milk_data if x.birth_number > 1])
        self['adultas_litros%s' % suffix] = average([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.birth_number > 1])


        self['adultas_grasa%s' % suffix] = (sum([x.get_liters(self['milking_type%s' % suffix]) * (x.fat / 100) for x in milk_data if x.birth_number > 1 and x.fat > 0]) / (sum([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.birth_number > 1 and x.fat > 0]) or 1.0)) * 100
        self['adultas_proteina%s' % suffix] = (sum([x.get_liters(self['milking_type%s' % suffix]) * (x.protein / 100) for x in milk_data if x.birth_number > 1 and x.protein > 0]) / (sum([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.birth_number > 1 and x.protein > 0]) or 1.0)) * 100
        self['adultas_celulas%s' % suffix] = sum([x.rcs * 1000 * x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.birth_number > 1]) / ((sum([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.birth_number > 1 and x.fat > 0]) or 1.0) * 1000)


        self['total_cant%s' % suffix] = sum([1 for x in milk_data if x.get_liters(self['milking_type%s' % suffix]) > 0])
        self['total_del%s' % suffix] = average([x.days for x in milk_data])
        self['total_litros%s' % suffix] = average([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data])

        self['total_grasa%s' % suffix] = (sum([x.get_liters(self['milking_type%s' % suffix]) * (x.fat / 100)]) / (sum([x.get_liters(self['milking_type%s' % suffix])]) or 1.0)) * 100
        self['total_proteina%s' % suffix] = (sum([x.get_liters(self['milking_type%s' % suffix]) * (x.protein / 100)]) / (sum([x.get_liters(self['milking_type%s' % suffix])]) or 1.0)) * 100
        self['total_celulas%s' % suffix] = sum([x.rcs * 1000 * x.get_liters(self['milking_type%s' % suffix]) for x in milk_data]) / ((sum([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.fat > 0]) or 1.0) * 1000)

        invertidas = sum([1 for x in milk_data if x.protein > x.fat])
        self['vacas_invertidas%s' % suffix] =  '%s (%0.2f%%)' % (invertidas, (float(invertidas) / (self['total_cant%s' % suffix] or 1.0)) * 100)

        self['coeficiente_persistencia%s' % suffix] = (average([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.days > 29 and x.days < 91]) - average([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.days > 179 and x.days < 306])) / ((average([x.days for x in milk_data if x.days > 179 and x.days < 306]) - average([x.days for x in milk_data if x.days > 29 and x.days < 91])) or 1.0)

        # TODO: Cambiar 165 por campo !!!!!




        self['leche_corregida_a_del%s' % suffix] = average([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data]) + ((average([x.days for x in milk_data]) - 165) * self['coeficiente_persistencia%s' % suffix])
        self['prod_media_lact_acumulada%s' % suffix] = average([x.cumulative_milk for x in milk_data]) / (average([x.days for x in milk_data]) or 1.0)
        self['num_medio_partos%s' % suffix] = average([x.birth_number for x in milk_data])
        self['relacion_grasa_prot%s' % suffix] = self['total_grasa%s' % suffix] / (self['total_proteina%s' % suffix] or 1.0)

        self['pico_novillas%s' % suffix] = average([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.birth_number == 1 and x.days > 29 and x.days < 91])
        self['pico_novillas_del%s' % suffix] = average([x.days for x in milk_data  if x.birth_number == 1 and x.days > 29 and x.days < 91])
        self['num_novillas_pico_del%s' % suffix] = sum([1 for x in milk_data  if x.birth_number == 1 and x.days > 29 and x.days < 91])

        self['pico_adultas%s' % suffix] = average([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.birth_number > 1 and x.days > 29 and x.days < 61])
        self['pico_adultas_del%s' % suffix] = average([x.days for x in milk_data  if x.birth_number > 1 and x.days > 29 and x.days < 61])
        self['num_adultas_pico_del%s' % suffix] = sum([1 for x in milk_data  if x.birth_number > 1 and x.days > 29 and x.days < 61])
        if self['pico_adultas%s' % suffix]:
            self['relacion_entre_picos%s' % suffix] = self['pico_novillas%s' % suffix] / (self['pico_adultas%s' % suffix] or 1.0)
        else:
            self['relacion_entre_picos%s' % suffix] = 0

        self['prim_terc_lact_num_animales%s' % suffix] = len(milk_data.filtered(lambda r: r.days < 91))
        self['prim_terc_lact_porcen_animales%s' % suffix] = (self['prim_terc_lact_num_animales%s' % suffix] / (float(len(milk_data)) or 1.0)) * 100
        self['prim_terc_lact_litros_total%s' % suffix] = sum([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.days < 91])
        self['prim_terc_lact_media%s' % suffix] = self['prim_terc_lact_litros_total%s' % suffix] / (self['prim_terc_lact_num_animales%s' % suffix] or 1.0)
        self['seg_terc_lact_num_animales%s' % suffix] = len(milk_data.filtered(lambda r: r.days > 90 and r.days < 181))
        self['seg_terc_lact_porcen_animales%s' % suffix] = (self['seg_terc_lact_num_animales%s' % suffix] / (float(len(milk_data)) or 1.0)) * 100
        self['seg_terc_lact_litros_total%s' % suffix] = sum([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.days > 90 and x.days < 181])
        self['seg_terc_lact_media%s' % suffix] = self['seg_terc_lact_litros_total%s' % suffix] / (self['seg_terc_lact_num_animales%s' % suffix] or 1.0)
        self['terc_terc_lact_num_animales%s' % suffix] = len(milk_data.filtered(lambda r : r.days > 180))
        self['terc_terc_lact_porcen_animales%s' % suffix] = (self['terc_terc_lact_num_animales%s' % suffix] / (float(len(milk_data)) or 1.0)) * 100
        self['terc_terc_lact_litros_total%s' % suffix] = sum([x.get_liters(self['milking_type%s' % suffix]) for x in milk_data if x.days > 180])
        self['terc_terc_lact_media%s' % suffix] = self['terc_terc_lact_litros_total%s' % suffix] / (self['terc_terc_lact_num_animales%s' % suffix] or 1.0)




    @api.multi
    def _calculate_vals(self):
        for report in self:
            report._get_data(1)
            report._get_data(2)

        milk_control_1 = self.env['milk.control'].search([('date', '>=', self.from_date), ('date', '<=', self.to_date), ('exploitation_id', '=', self.exploitation_1.id)])
        milk_data_1 = milk_control_1.mapped('line_ids')
        if self.exploitation_2:
            milk_control_2 = self.env['milk.control'].search([('date', '>=', self.from_date), ('date', '<=', self.to_date), ('exploitation_id', '=', self.exploitation_2.id)])
            milk_data_2 = milk_control_2.mapped('line_ids')
        if not milk_data_1:
            return
        graph_vals = []
        for vals  in [(0, 31), (30, 91), (90, 121), (120, 181), (180, 241), (240, 305)]:
            av = average([x.get_liters(self.milking_type_1) for x in milk_data_1 if x.days > vals[0] and x.days < vals[1]])
            cont = sum([1 for x in milk_data_1 if x.days > vals[0] and x.days < vals[1] and x.get_liters(self.milking_type_1) > 0])
            if self.exploitation_2:
                av_2 = average([x.get_liters(self.milking_type_2) for x in milk_data_2 if x.days > vals[0] and x.days < vals[1]])
                cont_2 = sum([1 for x in milk_data_2 if x.days > vals[0] and x.days < vals[1] and x.get_liters(self.milking_type_2) > 0])
                graph_vals.append([round(av, 2), cont, round(av_2, 2), cont_2])
            else:
                graph_vals.append([round(av, 2), cont])

        av = average([x.get_liters(self.milking_type_1) for x in milk_data_1 if x.days > 305])
        cont = sum([1 for x in milk_data_1 if x.days > 305 and x.get_liters(self.milking_type_1) > 0])
        if self.exploitation_2:
            av_2 = average([x.get_liters(self.milking_type_2) for x in milk_data_2 if x.days > 305])
            cont_2 = sum([1 for x in milk_data_2 if x.days > 305 and x.get_liters(self.milking_type_2) > 0])
            graph_vals.append([round(av, 2), cont, round(av_2, 2), cont_2])
        else:
            graph_vals.append([round(av, 2), cont])

        if not self.exploitation_2:
            y_vals = [x[0] for x in graph_vals] + [x[1] for x in graph_vals]
            series_labels=[u'Media(lts/vaca y dia)', u'n de animales']
            series_colors=['red', 'blue']
        else:
            y_vals = [x[0] for x in graph_vals] + [x[1] for x in graph_vals] + [x[2] for x in graph_vals] + [x[3] for x in graph_vals]
            series_labels=[u'Media(lts/vaca y dia) %s' % self.exploitation_1.name, u'n de animales %s' % self.exploitation_1.name, u'Media(lts/vaca y dia) %s' % self.exploitation_2.name, u'n de animales %s' % self.exploitation_2.name]
            series_colors=['red', 'blue', 'green', 'yellow']
        max_y = int(max(y_vals)) + 10
        min_y = int(min(y_vals)) - 5
        min_y = min_y > 0 and min_y or 0
        test = VerticalBarPlot("/tmp/object_way.png", graph_vals, 900, 700, display_values=True, series_labels=series_labels, series_colors=series_colors, y_bounds=[min_y, max_y], x_labels=['DEL <30', 'DE 31-90 DEL', 'DE 91-120 DEL', 'DE 121-180 DEL', 'DE 181-240 DEL', 'DE 241-305 DEL', 'DEL>305'], border=20)
        test.render()
        test.commit()
        f = open("/tmp/object_way.png", "rb")
        self.graphic_img = base64.b64encode(f.read())
Exemplo n.º 25
0
class AccountTax(models.Model):
    _name = 'account.tax'
    _description = 'Tax'
    _order = 'sequence'

    @api.model
    def _default_tax_group(self):
        return self.env['account.tax.group'].search([], limit=1)

    name = fields.Char(string='Tax Name', required=True, translate=True)
    type_tax_use = fields.Selection(
        [('sale', 'Sales'), ('purchase', 'Purchases'), ('none', 'None')],
        string='Tax Scope',
        required=True,
        default="sale",
        help=
        "Determines where the tax is selectable. Note : 'None' means a tax can't be used by itself, however it can still be used in a group."
    )
    amount_type = fields.Selection(default='percent',
                                   string="Tax Computation",
                                   required=True,
                                   oldname='type',
                                   selection=[
                                       ('group', 'Group of Taxes'),
                                       ('fixed', 'Fixed'),
                                       ('percent', 'Percentage of Price'),
                                       ('division',
                                        'Percentage of Price Tax Included')
                                   ])
    active = fields.Boolean(
        default=True,
        help="Set active to false to hide the tax without removing it.")
    company_id = fields.Many2one('res.company',
                                 string='Company',
                                 required=True,
                                 default=lambda self: self.env.user.company_id)
    children_tax_ids = fields.Many2many('account.tax',
                                        'account_tax_filiation_rel',
                                        'parent_tax',
                                        'child_tax',
                                        string='Children Taxes')
    sequence = fields.Integer(
        required=True,
        default=1,
        help=
        "The sequence field is used to define order in which the tax lines are applied."
    )
    amount = fields.Float(required=True, digits=(16, 4))
    account_id = fields.Many2one(
        'account.account',
        domain=[('deprecated', '=', False)],
        string='Tax Account',
        ondelete='restrict',
        help=
        "Account that will be set on invoice tax lines for invoices. Leave empty to use the expense account.",
        oldname='account_collected_id')
    refund_account_id = fields.Many2one(
        'account.account',
        domain=[('deprecated', '=', False)],
        string='Tax Account on Refunds',
        ondelete='restrict',
        help=
        "Account that will be set on invoice tax lines for refunds. Leave empty to use the expense account.",
        oldname='account_paid_id')
    description = fields.Char(string='Label on Invoices', translate=True)
    price_include = fields.Boolean(
        string='Included in Price',
        default=False,
        help=
        "Check this if the price you use on the product and invoices includes this tax."
    )
    include_base_amount = fields.Boolean(
        string='Affect Base of Subsequent Taxes',
        default=False,
        help=
        "If set, taxes which are computed after this one will be computed based on the price tax included."
    )
    analytic = fields.Boolean(
        string="Include in Analytic Cost",
        help=
        "If set, the amount computed by this tax will be assigned to the same analytic account as the invoice line (if any)"
    )
    tag_ids = fields.Many2many(
        'account.account.tag',
        'account_tax_account_tag',
        string='Tags',
        help="Optional tags you may want to assign for custom reporting")
    tax_group_id = fields.Many2one('account.tax.group',
                                   string="Tax Group",
                                   default=_default_tax_group,
                                   required=True)

    _sql_constraints = [
        ('name_company_uniq', 'unique(name, company_id, type_tax_use)',
         'Tax names must be unique !'),
    ]

    @api.multi
    def unlink(self):
        company_id = self.env.user.company_id.id
        ir_values = self.env['ir.values']
        supplier_taxes_id = set(
            ir_values.get_default('product.template',
                                  'supplier_taxes_id',
                                  company_id=company_id))
        deleted_sup_tax = self.filtered(
            lambda tax: tax.id in supplier_taxes_id)
        if deleted_sup_tax:
            ir_values.sudo().set_default('product.template',
                                         "supplier_taxes_id",
                                         list(supplier_taxes_id -
                                              set(deleted_sup_tax.ids)),
                                         for_all_users=True,
                                         company_id=company_id)
        taxes_id = set(self.env['ir.values'].get_default(
            'product.template', 'taxes_id', company_id=company_id))
        deleted_tax = self.filtered(lambda tax: tax.id in taxes_id)
        if deleted_tax:
            ir_values.sudo().set_default('product.template',
                                         "taxes_id",
                                         list(taxes_id - set(deleted_tax.ids)),
                                         for_all_users=True,
                                         company_id=company_id)
        return super(AccountTax, self).unlink()

    @api.one
    @api.constrains('children_tax_ids', 'type_tax_use')
    def _check_children_scope(self):
        if not all(child.type_tax_use in ('none', self.type_tax_use)
                   for child in self.children_tax_ids):
            raise UserError(
                _('The application scope of taxes in a group must be either the same as the group or "None".'
                  ))

    @api.one
    def copy(self, default=None):
        default = dict(default or {}, name=_("%s (Copy)") % self.name)
        return super(AccountTax, self).copy(default=default)

    @api.model
    def name_search(self, name, args=None, operator='ilike', limit=80):
        """ Returns a list of tupples containing id, name, as internally it is called {def name_get}
            result format: {[(id, name), (id, name), ...]}
        """
        args = args or []
        if operator in expression.NEGATIVE_TERM_OPERATORS:
            domain = [('description', operator, name),
                      ('name', operator, name)]
        else:
            domain = [
                '|', ('description', operator, name), ('name', operator, name)
            ]
        taxes = self.search(expression.AND([domain, args]), limit=limit)
        return taxes.name_get()

    @api.model
    def search(self, args, offset=0, limit=None, order=None, count=False):
        context = self._context or {}

        if context.get('type'):
            if context.get('type') in ('out_invoice', 'out_refund'):
                args += [('type_tax_use', '=', 'sale')]
            elif context.get('type') in ('in_invoice', 'in_refund'):
                args += [('type_tax_use', '=', 'purchase')]

        if context.get('journal_id'):
            journal = self.env['account.journal'].browse(
                context.get('journal_id'))
            if journal.type in ('sale', 'purchase'):
                args += [('type_tax_use', '=', journal.type)]

        return super(AccountTax, self).search(args,
                                              offset,
                                              limit,
                                              order,
                                              count=count)

    @api.onchange('amount')
    def onchange_amount(self):
        if self.amount_type in (
                'percent',
                'division') and self.amount != 0.0 and not self.description:
            self.description = "{0:.4g}%".format(self.amount)

    @api.onchange('account_id')
    def onchange_account_id(self):
        self.refund_account_id = self.account_id

    @api.onchange('price_include')
    def onchange_price_include(self):
        if self.price_include:
            self.include_base_amount = True

    def get_grouping_key(self, invoice_tax_val):
        """ Returns a string that will be used to group account.invoice.tax sharing the same properties"""
        self.ensure_one()
        return str(invoice_tax_val['tax_id']) + '-' + str(
            invoice_tax_val['account_id']) + '-' + str(
                invoice_tax_val['account_analytic_id'])

    def _compute_amount(self,
                        base_amount,
                        price_unit,
                        quantity=1.0,
                        product=None,
                        partner=None):
        """ Returns the amount of a single tax. base_amount is the actual amount on which the tax is applied, which is
            price_unit * quantity eventually affected by previous taxes (if tax is include_base_amount XOR price_include)
        """
        self.ensure_one()
        if self.amount_type == 'fixed':
            # Use copysign to take into account the sign of the base amount which includes the sign
            # of the quantity and the sign of the price_unit
            # Amount is the fixed price for the tax, it can be negative
            # Base amount included the sign of the quantity and the sign of the unit price and when
            # a product is returned, it can be done either by changing the sign of quantity or by changing the
            # sign of the price unit.
            # When the price unit is equal to 0, the sign of the quantity is absorbed in base_amount then
            # a "else" case is needed.
            if base_amount:
                return math.copysign(quantity, base_amount) * self.amount
            else:
                return quantity * self.amount
        if (self.amount_type == 'percent'
                and not self.price_include) or (self.amount_type == 'division'
                                                and self.price_include):
            return base_amount * self.amount / 100
        if self.amount_type == 'percent' and self.price_include:
            return base_amount - (base_amount / (1 + self.amount / 100))
        if self.amount_type == 'division' and not self.price_include:
            return base_amount / (1 - self.amount / 100) - base_amount

    @api.v8
    def compute_all(self,
                    price_unit,
                    currency=None,
                    quantity=1.0,
                    product=None,
                    partner=None):
        """ Returns all information required to apply taxes (in self + their children in case of a tax goup).
            We consider the sequence of the parent for group of taxes.
                Eg. considering letters as taxes and alphabetic order as sequence :
                [G, B([A, D, F]), E, C] will be computed as [A, D, F, C, E, G]

        RETURN: {
            'total_excluded': 0.0,    # Total without taxes
            'total_included': 0.0,    # Total with taxes
            'taxes': [{               # One dict for each tax in self and their children
                'id': int,
                'name': str,
                'amount': float,
                'sequence': int,
                'account_id': int,
                'refund_account_id': int,
                'analytic': boolean,
            }]
        } """
        if len(self) == 0:
            company_id = self.env.user.company_id
        else:
            company_id = self[0].company_id
        if not currency:
            currency = company_id.currency_id
        taxes = []
        # By default, for each tax, tax amount will first be computed
        # and rounded at the 'Account' decimal precision for each
        # PO/SO/invoice line and then these rounded amounts will be
        # summed, leading to the total amount for that tax. But, if the
        # company has tax_calculation_rounding_method = round_globally,
        # we still follow the same method, but we use a much larger
        # precision when we round the tax amount for each line (we use
        # the 'Account' decimal precision + 5), and that way it's like
        # rounding after the sum of the tax amounts of each line
        prec = currency.decimal_places
        if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(
                self.env.context.get("round", True)):
            prec += 5
        total_excluded = total_included = base = round(price_unit * quantity,
                                                       prec)

        # Sorting key is mandatory in this case. When no key is provided, sorted() will perform a
        # search. However, the search method is overridden in account.tax in order to add a domain
        # depending on the context. This domain might filter out some taxes from self, e.g. in the
        # case of group taxes.
        for tax in self.sorted(key=lambda r: r.sequence):
            if tax.amount_type == 'group':
                ret = tax.children_tax_ids.compute_all(price_unit, currency,
                                                       quantity, product,
                                                       partner)
                total_excluded = ret['total_excluded']
                base = ret['base']
                total_included = ret['total_included']
                tax_amount = total_included - total_excluded
                taxes += ret['taxes']
                continue

            tax_amount = tax._compute_amount(base, price_unit, quantity,
                                             product, partner)
            if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(
                    self.env.context.get("round", True)):
                tax_amount = round(tax_amount, prec)
            else:
                tax_amount = currency.round(tax_amount)

            if tax.price_include:
                total_excluded -= tax_amount
                base -= tax_amount
            else:
                total_included += tax_amount

            if tax.include_base_amount:
                base += tax_amount

            taxes.append({
                'id':
                tax.id,
                'name':
                tax.with_context(**{
                    'lang': partner.lang
                } if partner else {}).name,
                'amount':
                tax_amount,
                'sequence':
                tax.sequence,
                'account_id':
                tax.account_id.id,
                'refund_account_id':
                tax.refund_account_id.id,
                'analytic':
                tax.analytic,
            })

        return {
            'taxes':
            sorted(taxes, key=lambda k: k['sequence']),
            'total_excluded':
            currency.round(total_excluded) if bool(
                self.env.context.get("round", True)) else total_excluded,
            'total_included':
            currency.round(total_included) if bool(
                self.env.context.get("round", True)) else total_included,
            'base':
            base,
        }

    @api.v7
    def compute_all(self,
                    cr,
                    uid,
                    ids,
                    price_unit,
                    currency_id=None,
                    quantity=1.0,
                    product_id=None,
                    partner_id=None,
                    context=None):
        currency = currency_id and self.pool.get('res.currency').browse(
            cr, uid, currency_id, context=context) or None
        product = product_id and self.pool.get('product.product').browse(
            cr, uid, product_id, context=context) or None
        partner = partner_id and self.pool.get('res.partner').browse(
            cr, uid, partner_id, context=context) or None
        ids = isinstance(ids, (int, long)) and [ids] or ids
        recs = self.browse(cr, uid, ids, context=context)
        return AccountTax.compute_all(recs, price_unit, currency, quantity,
                                      product, partner)

    @api.model
    def _fix_tax_included_price(self, price, prod_taxes, line_taxes):
        """Subtract tax amount from price when corresponding "price included" taxes do not apply"""
        # FIXME get currency in param?
        incl_tax = prod_taxes.filtered(
            lambda tax: tax not in line_taxes and tax.price_include)
        if incl_tax:
            return incl_tax.compute_all(price)['total_excluded']
        return price
Exemplo n.º 26
0
class ProductWishlist(models.Model):
    _name = "product.wishlist"
    _sql_constrains = [
        ("session_or_user_id", "CHECK(session IS NULL != user_id IS NULL)",
         "Need a session or user for wishlisting a product, but never both."),
        ("product_unique_session", "UNIQUE(product_tmpl_id, session)",
         "Duplicated wishlisted product for this session."),
        ("product_unique_user_id", "UNIQUE(product_tmpl_id, user_id)",
         "Duplicated wishlisted product for this user."),
    ]

    product_tmpl_id = fields.Many2one(
        comodel_name="product.template",
        string="Product",
        required=True,
        ondelete="cascade",
        help="Wishlisted product.",
    )
    session = fields.Char(
        default=lambda self: self._default_session(),
        help="Website session identifier where this product was wishlisted.",
    )
    user_id = fields.Many2one(
        comodel_name="res.users",
        string="User",
        ondelete="cascade",
        default=lambda self: self._default_user_id(),
        help="User that wishlisted this product.",
    )
    website_id = fields.Many2one(
        comodel_name="website",
        string="Website",
        required=True,
        ondelete="cascade",
        default=lambda self: self._default_website_id(),
        help="Website where the user wishlisted the product.",
    )

    @api.model
    def _default_session(self):
        """Default to current session."""
        return request.session.sid if not self._default_user_id() else False

    @api.model
    def _default_user_id(self):
        """Default to current user if it's not the website user."""
        return (self.env.user != self._default_website_id().user_id
                and self.env.user)

    @api.model
    def _default_website_id(self):
        """Default to current website."""
        return self.env["website"].get_current_website()

    @api.model
    def _join_current_user_and_session(self):
        """Assign all dangling session wishlisted products to user."""
        user_products = self.search([
            ("user_id", "=", self.env.uid),
        ]).mapped("product_tmpl_id")
        session_domain = [
            ("session", "=", request.session.sid),
            ("user_id", "=", False),
        ]
        # Remove session products already present for the user
        self.search(
            session_domain + [("product_tmpl_id", "in", user_products.ids)]) \
            .unlink()
        # Assign the rest to the user
        self.search(session_domain).write({
            "user_id": self.env.uid,
            "session": False,
        })

    @api.model
    def _garbage_collector(self):
        """Remove wishlists for unexisting sessions."""
        self.search([
            ("session", "not in", root.session_store.list()),
            ("user_id", "=", False),
        ]).unlink()

    @api.model
    def _clear_methods_cache(self):
        """Clear cache for wishlist-related methods."""
        Website = self.env["website"]
        Product = self.env["product.template"]
        Website.wishlist_product_ids.clear_cache(Website)
        Website.wishlisted_product_template_ids.clear_cache(Website)
        Product.wishlisted.clear_cache(Product)

    @api.model
    def create(self, vals):
        self._clear_methods_cache()
        return super(ProductWishlist, self).create(vals)

    @api.multi
    def unlink(self):
        self._clear_methods_cache()
        return super(ProductWishlist, self).unlink()

    @api.multi
    def write(self, vals):
        self._clear_methods_cache()
        return super(ProductWishlist, self).write(vals)
Exemplo n.º 27
0
class ClouderModel(models.AbstractModel):
    """
    Define the clouder.model abstract object, which is inherited by most
    objects in clouder.
    """

    _name = 'clouder.model'
    _description = 'Clouder Model'

    _autodeploy = True

    BACKUP_BASE_DIR = '/base-backup/'
    BACKUP_DATA_DIR = '/opt/backup/'
    BACKUP_HOME_DIR = '/home/backup/'
    BACKUP_DATE_FILE = 'backup-date'

    # We create the name field to avoid warning for the constraints
    name = fields.Char('Name', required=True)
    job_ids = fields.One2many(
        'clouder.job', 'res_id',
        domain=lambda self: [('model_name', '=', self._name)],
        auto_join=True, string='Jobs')

    @property
    def version(self):
        return int(release.version.split('.')[0])

    @property
    def email_sysadmin(self):
        """
        Property returning the sysadmin email of the clouder.
        """
        return self.env.ref('clouder.clouder_settings').email_sysadmin

    @property
    def salt_master(self):
        """
        Property returning the salt master of the clouder.
        """
        return self.env.ref('clouder.clouder_settings').salt_master_id

    @property
    def user_partner(self):
        """
        Property returning the full name of the server.
        """
        return self.env['res.partner'].search(
            [('user_ids', 'in', int(self.env.uid))])[0]

    @property
    def archive_path(self):
        """
        Property returning the path where are stored the archives
        in the archive container.
        """
        return '/opt/archives'

    @property
    def services_hostpath(self):
        """
        Property returning the path where are stored the archives
        in the host system.
        """
        return '/opt/services'

    @property
    def home_directory(self):
        """
        Property returning the path to the home directory.
        """
        return expanduser("~")

    @property
    def now(self):
        """
        Property returning the actual date.
        """
        now = datetime.now()
        return now.strftime("%Y-%m-%d %H:%M:%S")

    @property
    def now_date(self):
        """
        Property returning the actual date.
        """
        now = datetime.now()
        return now.strftime("%Y-%m-%d")

    @property
    def now_hour(self):
        """
        Property returning the actual hour.
        """
        now = datetime.now()
        return now.strftime("%H-%M")

    @property
    def now_hour_regular(self):
        """
        Property returning the actual hour.
        """
        now = datetime.now()
        return now.strftime("%H:%M:%S")

    @property
    def now_bup(self):
        """
        Property returning the actual date, at the bup format.
        """
        now = datetime.now()
        return now.strftime("%Y-%m-%d-%H%M%S")

    @api.multi
    def get_directory_key(self, add_path=None):
        """ It returns the current working directory for keys
        :param add_path: (str|iter) String or Iterator of Strings indicating
            path parts to add to the default remote working path
        :returns: (str) Key directory on the local
        """
        name = 'key_%s' % self.env.uid
        return self._get_directory_tmp(name, add_path)

    @api.multi
    def get_directory_clouder(self, add_path=None):
        """ It returns the current clouder directory on the remote
        :param add_path: (str|iter) String or Iterator of Strings indicating
            path parts to add to the default remote working path
        :returns: (str) Clouder directory on the remote
        """
        return self._get_directory_tmp('clouder', add_path)

    @api.multi
    def _get_directory_tmp(self, name, add_path=None):
        """ It returns a directory in tmp for name
        :param name: (str) Name of the directory in tmp
        :param add_path: (str|iter) String or Iterator of Strings indicating
            path parts to add to the default remote working path
        :returns: (str) Clouder directory on the remote
        """
        if add_path is None:
            add_path = []
        elif not isinstance(add_path, (tuple, list, dict)):
            add_path = [str(add_path)]
        return os.path.join('/tmp', str(name), *add_path)

    @api.multi
    @contextmanager
    def _private_env(self):
        """ It provides an isolated environment/commit

        Usage:
            ``with self._private_env() as self``

        Yields:
            Current ``self``, but in a new environment
        """
        # with api.Environment.manage():
        #     with registry(self.env.cr.dbname).cursor() as cr:
        #         env = api.Environment(cr, self.env.uid, self.env.context)
        #         _logger.debug('Created new env %s for %s', env, self)
        yield self
        self.env.cr.commit()  # pylint: disable=E8102
        #         _logger.debug('Cursor %s has been committed', cr)

    @api.multi
    @api.constrains('name')
    def _check_config(self):
        """
        Check that we specified the sysadmin email in configuration before
        making any action.
        """
        if self._name != 'clouder.config.settings' and not \
                self.env.ref('clouder.clouder_settings').email_sysadmin:
            self.raise_error(
                "You need to specify the sysadmin email in configuration.",
            )

    @api.multi
    def check_priority(self):
        priority = False
        for record_job in self.job_ids:
            if record_job.job_id and record_job.job_id.state != 'done' and \
                    record_job.job_id.priority <= 999:
                priority = record_job.job_id.priority
        return priority

    @api.multi
    def control_priority(self):
        return False

    @api.multi
    def log(self, message):
        """
        Add a message in the logs specified in context.

        :param message: The message which will be logged.
        """

        with self._private_env() as self:

            job_obj = self.env['clouder.job']
            now = fields.Datetime.now()
            message = re.sub(r'$$$\w+$$$', '**********', message)
            message = filter(lambda x: x in string.printable, message)
            _logger.info(message)

            if 'clouder_jobs' not in self.env.context:
                return

            for key, job_id in self.env.context['clouder_jobs'].iteritems():
                if job_obj.search([('id', '=', job_id)]):
                    job = job_obj.browse(job_id)
                    if job.state == 'started':
                        job.log = '%s%s : %s\n' % (
                            (job.log or ''),
                            now,
                            message,
                        )

    @api.multi
    def raise_error(self, message, interpolations=None):
        """ Raises a ClouderError with a translated message
        :param message: (str) Message including placeholders for string
            interpolation. Interpolation takes place via the ``%`` operator.
        :param interpolations: (dict|tuple) Mixed objects to be used for
            string interpolation after message translation. Dict for named
            parameters or tuple for positional. Cannot use both.
        :raises: (clouder.exceptions.ClouderError)
        """
        if interpolations is None:
            interpolations = ()
        raise ClouderError(self, _(message) % interpolations)

    @api.multi
    def do(self, name, action, where=False):
        where = where or self
        if 'clouder_jobs' not in self.env.context:
            self = self.with_context(clouder_jobs={})
        job_id = False
        key = where._name + '_' + str(where.id)
        if key not in self.env.context['clouder_jobs']:
            job = self.env['clouder.job'].create({
                'name': name, 'action': action, 'model_name': where._name,
                'res_id': where.id, 'state': 'started'})
            jobs = self.env.context['clouder_jobs']
            jobs[key] = job.id
            self = self.with_context(clouder_jobs=jobs)
            job_id = job.id

#        if 'no_enqueue' not in self.env.context:
#            self.enqueue(name, action, job_id)
#        else:
        getattr(self, 'do_exec')(action, job_id)

    @api.multi
    def enqueue(self, name, action, clouder_job_id):
        return

    @api.multi
    def do_exec(self, action, job_id):

        if job_id:
            job = self.env['clouder.job'].browse(job_id)
            job.write({'start_date': self.now})

        try:
            getattr(self, action)()
            if job_id:
                job.write({'end_date': self.now, 'state': 'done'})
        except:
            self.log('===================')
            self.log('FAIL!')
            self.log('===================')
            if job_id:
                job.write({'end_date': self.now, 'state': 'failed'})
            raise

    @api.multi
    def deploy_frame(self):
        try:
            self.deploy()
            self.deploy_links()
        except:
            self.log('===================')
            self.log('FAIL! Reverting...')
            self.log('===================')
            self.purge()
            raise

    @api.multi
    def deploy(self):
        """
        Hook which can be used by inheriting objects to execute actions when
        we create a new record.
        """
        self.purge()
        return

    @api.multi
    def purge(self):
        """
        Hook which can be used by inheriting objects to execute actions when
        we delete a record.
        """
        self.purge_links()
        return

    @api.multi
    def deploy_links(self):
        """
        Force deployment of all links linked to a record.
        """
        if hasattr(self, 'link_ids'):
            for link in self.link_ids:
                link.deploy_()

    @api.multi
    def purge_links(self):
        """
        Force purge of all links linked to a record.
        """
        if hasattr(self, 'link_ids'):
            for link in self.link_ids:
                link.purge_()

    @api.multi
    def reinstall(self):
        """"
        Action which purge then redeploy a record.
        """
        self.do('reinstall', 'deploy_frame')

    @api.multi
    def hook_create(self):
        return

    @api.model
    def create(self, vals):
        """
        Override the default create function to create log, call deploy hook,
        and call unlink if something went wrong.

        :param vals: The values needed to create the record.
        """

        res = super(ClouderModel, self).create(vals)

        if self._autodeploy:
            res.hook_create()
            res.do('create', 'deploy_frame')

        return res

    @api.multi
    def unlink(self):
        """
        Override the default unlink function to create log and call purge hook.
        """
        for rec in self:
            if self._autodeploy:
                try:
                    rec.purge()
                except:
                    pass
        res = super(ClouderModel, self).unlink()
        self.env['clouder.job'].search([
            ('res_id', 'in', self.ids),
            ('model_name', '=', self._name)]).unlink()
        return res

    @api.multi
    def connect(self, server_name='', port=False, username=False):
        """
        Method which can be used to get an ssh connection to execute command.

        :param host: The host we need to connect.
        :param port: The port we need to connect.
        :param username: The username we need to connect.
        """

        server = self
        if self._name == 'clouder.container':
            username = False
            server = self.server_id

        if not server_name:
            server_name = server.fulldomain

        global ssh_connections
        host_fullname = server_name + \
            (port and ('_' + port) or '') + \
            (username and ('_' + username) or '')
        if host_fullname not in ssh_connections\
                or not ssh_connections[host_fullname]._transport:

            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

            ssh_config = paramiko.SSHConfig()
            user_config_file = os.path.expanduser("~/.ssh/config")
            if os.path.exists(user_config_file):
                with open(user_config_file) as f:
                    ssh_config.parse(f)
            user_config = ssh_config.lookup(server_name)

            identityfile = None
            if 'identityfile' in user_config:
                host = user_config['hostname']
                identityfile = user_config['identityfile']
                if not username:
                    username = user_config['user']
                if not port:
                    port = user_config['port']

            if identityfile is None:
                self.raise_error(
                    'Clouder does not have a record in the ssh config to '
                    'connect to your server.\n'
                    'Make sure there is a "%s" record in the "~/.ssh/config" '
                    'of the Clouder system user.\n'
                    'To easily add this record, you can click on the '
                    '"Reinstall" button of the server record, or the '
                    '"Reset Key" button of the container record you are '
                    'trying to access',
                    self.name
                )

            # Security with latest version of Paramiko
            # https://github.com/clouder-community/clouder/issues/11
            if isinstance(identityfile, list):
                identityfile = identityfile[0]

            # Probably not useful anymore, to remove later
            if not isinstance(identityfile, basestring):
                self.raise_error(
                    'For an unknown reason, the variable identityfile '
                    'in the connect ssh function is invalid. Please report '
                    'this message.\n'
                    'IdentityFile: "%s", Type: "%s"',
                    identityfile, type(identityfile),
                )

            self.log('connect: ssh ' + (username and username + '@' or '') +
                     host + (port and ' -p ' + str(port) or ''))

            try:
                ssh.connect(
                    host, port=int(port), username=username,
                    key_filename=os.path.expanduser(identityfile))
            except Exception as inst:
                self.raise_error(
                    'We were not able to connect to your server. Please '
                    'make sure you add the public key in the '
                    'authorized_keys file of your root user on your server.\n'
                    'If you were trying to connect to a container, '
                    'a click on the "Reset Key" button on the container '
                    'record may resolve the problem.\n'
                    'Target: "%s" \n'
                    'Error: "%s"',
                    host, inst,
                )
            ssh_connections[host_fullname] = ssh
        else:
            ssh = ssh_connections[host_fullname]

        return {'ssh': ssh, 'host': server_name, 'server': server}

    @api.multi
    def execute(self, cmd, stdin_arg=False,
                path=False, ssh=False, server_name='',
                username=False, executor='bash'):
        """
        Method which can be used with an ssh connection to execute command.

        :param ssh: The connection we need to use.
        :param cmd: The command we need to execute.
        :param stdin_arg: The command we need to execute in stdin.
        :param path: The path where the command need to be executed.
        """

        if self._name == 'clouder.container' \
                and self.childs and 'exec' in self.childs:
            return self.childs['exec'].execute(
                cmd, stdin_arg=stdin_arg, path=path, ssh=ssh,
                server_name=server_name, username=username, executor=executor)

        res_ssh = self.connect(server_name=server_name, username=username)
        ssh, host = res_ssh['ssh'], res_ssh['host']

        if path:
            self.log('path : ' + path)
            cmd.insert(0, 'cd ' + path + ';')

        if self._name == 'clouder.container':
            cmd_temp = []
            first = True
            for cmd_arg in cmd:
                cmd_arg = cmd_arg.replace('"', '\\"')
                if first:
                    cmd_arg = '"' + cmd_arg
                first = False
                cmd_temp.append(cmd_arg)
            cmd = cmd_temp
            cmd.append('"')
            cmd.insert(0, self.name + ' ' + executor + ' -c ')
            if username:
                cmd.insert(0, '-u ' + username)
            cmd.insert(0, 'docker exec')

        self.log('host : ' + host)
        self.log('command : ' + ' '.join(cmd))
        cmd = [c.replace('$$$', '') for c in cmd]

        transport = ssh.get_transport()
        channel = transport.open_session()
        channel.exec_command(' '.join(cmd))

        # Pushing additional input
        if stdin_arg:
            chnl_stdin = channel.makefile('wb', -1)
            for arg in stdin_arg:
                self.log('command : ' + arg)
                chnl_stdin.write(arg)
                chnl_stdin.flush()

        # Reading outputs
        stdout_read = ''
        chnl_out = ''
        chnl_err = ''
        chnl_buffer_size = 4096
        # As long as the command is running
        while not channel.exit_status_ready():
            rl, _, _ = select.select([channel], [], [], 0.0)
            if len(rl) > 0:
                # Polling and printing stdout
                if channel.recv_ready():
                    chnl_out += channel.recv(chnl_buffer_size)
                    stdout_read += chnl_out
                    chnl_pending_out = chnl_out.split('\n')
                    chnl_out = chnl_pending_out[-1]
                    for output_to_print in chnl_pending_out[:-1]:
                        self.log('stdout : {0}'.format(output_to_print))
                # Polling and printing stderr
                if channel.recv_stderr_ready():
                    chnl_err += channel.recv_stderr(chnl_buffer_size)
                    chnl_pending_err = chnl_err.split('\n')
                    chnl_err = chnl_pending_err[-1]
                    for err_to_print in chnl_pending_err[:-1]:
                        self.log('stderr : {0}'.format(err_to_print))

        # Polling last outputs if any:
        rl, _, _ = select.select([channel], [], [], 0.0)
        if len(rl) > 0:
            # Polling and printing stdout
            if channel.recv_ready():
                chnl_out += channel.recv(chnl_buffer_size)
                stdout_read += chnl_out
                chnl_pending_out = chnl_out.split('\n')
                for output_to_print in chnl_pending_out:
                    # The last one MAY be empty
                    if output_to_print:
                        self.log('stdout : {0}'.format(output_to_print))
            # Polling and printing stderr
            if channel.recv_stderr_ready():
                chnl_err += channel.recv_stderr(chnl_buffer_size)
                chnl_pending_err = chnl_err.split('\n')
                for err_to_print in chnl_pending_err:
                    # The last one MAY be empty
                    if err_to_print:
                        self.log('stderr : {0}'.format(err_to_print))
        return stdout_read

    @api.multi
    def get(self, source, destination, ssh=False):
        """
        Method which can be used with an ssh connection to transfer files.

        :param ssh: The connection we need to use.
        :param source: The path we need to get the file.
        :param destination: The path we need to send the file.
        """

        if self._name == 'clouder.container' and self.childs \
                and 'exec' in self.childs:
            return self.childs['exec'].get(source, destination, ssh=ssh)

        host = self.name
        if self._name == 'clouder.container':
            # TODO
            self.insert(0, 'docker exec ' + self.name)
            host = self.server_id.name

        if not ssh:
            ssh = self.connect(host)

        sftp = ssh.open_sftp()
        self.log('get : ' + source + ' to ' + destination)
        sftp.get(source, destination)
        sftp.close()

    @api.multi
    def send(self, source, destination, ssh=False, username=False):
        """
        Method which can be used with an ssh connection to transfer files.

        :param ssh: The connection we need to use.
        :param source: The path we need to get the file.
        :param destination: The path we need to send the file.
        """

        if self._name == 'clouder.container' and self.childs \
                and 'exec' in self.childs:
            return self.childs['exec'].send(
                source, destination, ssh=ssh, username=username)

        res_ssh = self.connect(username=username)
        ssh, server = res_ssh['ssh'], res_ssh['server']

        final_destination = destination
        tmp_dir = False
        if self != server:
            tmp_dir = self.get_directory_clouder(time.time())
            server.execute(['mkdir', '-p', tmp_dir])
            destination = os.path.join(tmp_dir, 'file')

        sftp = ssh.open_sftp()
        self.log('send: "%s" to "%s"' % (source, destination))
        sftp.put(source, destination)
        sftp.close()

        if tmp_dir:
            server.execute([
                'cat', destination, '|', 'docker', 'exec', '-i',
                username and ('-u %s' % username) or '',
                self.name, 'sh', '-c', "'cat > %s'" % final_destination,
            ])
#            if username:
#                server.execute([
#                    'docker', 'exec', '-i', self.name,
#                    'chown', username, final_destination])
            server.execute(['rm', '-rf', tmp_dir])

    @api.multi
    def send_dir(self, source, destination, ssh=False, username=False):
        self.log('Send directory ' + source + ' to ' + destination)
        self.execute(['mkdir', '-p', destination])
        for dirpath, dirnames, filenames in os.walk(source):
            self.log('dirpath ' + str(dirpath))
            self.log('dirnames ' + str(dirnames))
            self.log('filenames ' + str(filenames))
            relpath = os.path.relpath(dirpath, source)
            for dirname in dirnames:
                remote_path = os.path.join(
                    destination, os.path.join(relpath, dirname))
                self.execute(['mkdir', '-p', remote_path])
            for filename in filenames:
                local_path = os.path.join(dirpath, filename)
                remote_filepath = os.path.join(
                    destination, os.path.join(relpath, filename))
                self.send(
                    local_path, remote_filepath, ssh=ssh, username=username)

    @api.multi
    def execute_local(self, cmd, path=False, shell=False):
        """
        Method which can be used to execute command on the local system.

        :param cmd: The command we need to execute.
        :param path: The path where the command shall be executed.
        :param shell: Specify if the command shall be executed in shell mode.
        """
        self.log('command : ' + ' '.join(cmd))
        cwd = os.getcwd()
        if path:
            self.log('path : ' + path)
            os.chdir(path)
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT, shell=shell)
        out = ''
        for line in iter(proc.stdout.readline, b''):
            out += line
            self.log("stdout : {0}".format(line))

        os.chdir(cwd)
        return out

    @api.multi
    def exist(self, ssh, path):
        """
        Method which use an ssh connection to check is a file exist.

        :param ssh: The connection we need to use.
        :param path: The path we need to check.
        """
        sftp = ssh.open_sftp()
        try:
            sftp.stat(path)
        except IOError as e:
            if e.errno == errno.ENOENT:
                sftp.close()
                return False
            raise
        else:
            sftp.close()
            return True

    @api.multi
    def local_file_exist(self, localfile):
        """
        Method which check is a file exist on the local system.

        :param localfile: The path to the file we need to check.
        """
        return os.path.isfile(localfile)

    @api.multi
    def local_dir_exist(self, localdir):
        """
        Method which check is a directory exist on the local system.

        :param localdir: The path to the dir we need to check.
        """
        return os.path.isdir(localdir)

    @api.multi
    def execute_write_file(self, localfile, value, operator='a'):
        """
        Method which write in a file on the local system.

        :param localfile: The path to the file we need to write.
        :param value: The value we need to write in the file.
        """
        with open(localfile, operator) as f:
            f.write(value)

    def request(
            self, url, method='get', headers=None,
            data=None, params=None, files=None):

        self.log('request "%s" "%s"' % (method, url))

        if headers is None:
            headers = {}
        else:
            self.log('request "%s" "%s"' % (method, url))

        if data is None:
            data = {}
        else:
            self.log('data %s' % data)

        if params is None:
            params = {}
        else:
            self.log('params %s' % params)

        if files is None:
            files = {}
        else:
            self.log('files %s' % files)

        result = requests.request(
            method, url, headers=headers, data=data,
            params=params, files=files, verify=False,
        )
        self.log('status %s %s' % (result.status_code, result.reason))
        self.log('result %s' % result.json())

        return result
Exemplo n.º 28
0
class Session(models.Model):
    _name = 'openacademy.session'

    name = fields.Char(required=True)
    start_date = fields.Date(default=fields.Date.today)
    duration = fields.Float(digits=(6, 2), help="Duration in days")
    seats = fields.Integer(string="Number of seats")
    active = fields.Boolean(default=True)
    instructor_id = fields.Many2one(comodel_name="res.partner",
                                    string="Instructor",
                                    required=False,
                                    domain=[
                                        '|', ('instructor', '=', True),
                                        ('category_id.name', 'ilike',
                                         'Teacher')
                                    ])

    course_id = fields.Many2one(comodel_name="openacademy.course",
                                string="Course",
                                required=True,
                                ondelete='cascade')
    attendee_ids = fields.Many2many('res.partner', string="Attendees")

    taken_seats = fields.Float(string='Taken seats', compute='_taken_seats')

    end_date = fields.Date(string="End Date",
                           store=True,
                           compute='_get_end_date',
                           inverse='_set_end_date')

    hours = fields.Float(string="Duration in hours",
                         compute='_get_hours',
                         inverse='_set_hours')

    attendees_count = fields.Integer(string="Attendees count",
                                     compute='_get_attendees_count',
                                     store=True)

    # Save color for kanban view
    color = fields.Integer()

    # Workflow like
    state = fields.Selection([
        ('draft', "Draff"),
        ('confirmed', "Confirmed"),
        ('done', "Done"),
    ])

    @api.multi
    def action_draft(self):
        self.state = 'draft'

    @api.multi
    def action_confirm(self):
        self.state = 'confirmed'

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

    @api.depends('seats', 'attendee_ids')
    def _taken_seats(self):
        for r in self:
            if not r.seats:
                r.taken_seats = 0.0
            else:
                r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats

    @api.depends('start_date', 'duration')
    def _get_end_date(self):
        for r in self:
            if not (r.start_date and r.duration):
                r.end_date = r.start_date
                continue
            """
            Add duration to start_date, but: Monday + 5 days = Saturday, so
            subtract one second to get on Friday instead
            """
            start = fields.Datetime.from_string(r.start_date)
            duration = timedelta(days=r.duration, seconds=-1)
            r.end_date = start + duration

    def _set_end_date(self):
        for r in self:
            if not (r.start_date and r.end_date):
                continue
            """
            Compute the defference between dates, but: Friday - Monday = 4 days,
            so add one day to get 5 day instead
            """
            start_date = fields.Datetime.from_string(r.start_date)
            end_date = fields.Datetime.from_string(r.end_date)
            r.duration = (end_date - start_date).days + 1

    @api.depends('duration')
    def _get_hours(self):
        """
        Tra ve gia tri cho compute field hours
        :return:
        """
        for r in self:
            r.hours = r.duration * 24

    def _set_hours(self):
        """
        Ham set gia tri cho compute field hours
        :return:
        """
        for r in self:
            r.duration = r.hours / 24

    @api.depends('attendee_ids')
    def _get_attendees_count(self):
        for r in self:
            r.attendees_count = len(r.attendee_ids)

    @api.onchange('seats', 'attendee_ids')
    def _verify_valid_seats(self):
        """
        Dam bao so cho ngoi khong bi vuot qua
        :return:
        """
        if self.seats < 0:
            return {
                'warning': {
                    'title':
                    _("Incorrect 'seats' value"),
                    'message':
                    _("The number of available seats may not be negative"),
                }
            }
        if self.seats < len(self.attendee_ids):
            return {
                'warning': {
                    'title': _("Too many attendees"),
                    'message': _("Increase seats or remove excess attendees")
                }
            }

    @api.constrains('instructor_id', 'attendee_ids')
    def _check_instrcutor_not_in_attendees(self):
        """
        Checks that the instructor is not present in the attendees of his/her own session
        :return:
        """
        for r in self:
            if r.instructor_id and r.instructor_id in r.attendee_ids:
                raise exceptions.ValidationError(
                    _("A session's instrcutor can't be an attendee"))
Exemplo n.º 29
0
class Invite(models.TransientModel):
    """ Wizard to invite partners (or channels) and make them followers. """
    _name = 'mail.wizard.invite'
    _description = 'Invite wizard'

    @api.model
    def default_get(self, fields):
        result = super(Invite, self).default_get(fields)
        user_name = self.env.user.name_get()[0][1]
        model = result.get('res_model')
        res_id = result.get('res_id')
        if self._context.get('mail_invite_follower_channel_only'):
            result['send_mail'] = False
        if 'message' in fields and model and res_id:
            model_name = self.env['ir.model'].search([
                ('model', '=', self.pool[model]._name)
            ]).name_get()[0][1]
            document_name = self.env[model].browse(res_id).name_get()[0][1]
            message = _(
                '<div><p>Hello,</p><p>%s invited you to follow %s document: %s.<p></div>'
            ) % (user_name, model_name, document_name)
            result['message'] = message
        elif 'message' in fields:
            result['message'] = _(
                '<div><p>Hello,</p><p>%s invited you to follow a new document.</p></div>'
            ) % user_name
        return result

    res_model = fields.Char('Related Document Model',
                            required=True,
                            select=1,
                            help='Model of the followed resource')
    res_id = fields.Integer('Related Document ID',
                            select=1,
                            help='Id of the followed resource')
    partner_ids = fields.Many2many(
        'res.partner',
        string='Recipients',
        help=
        "List of partners that will be added as follower of the current document."
    )
    channel_ids = fields.Many2many(
        'mail.channel',
        string='Channels',
        help=
        'List of channels that will be added as listeners of the current document.'
    )
    message = fields.Html('Message')
    send_mail = fields.Boolean(
        'Send Email',
        default=True,
        help=
        "If checked, the partners will receive an email warning they have been added in the document's followers."
    )

    @api.multi
    def add_followers(self):
        email_from = self.env['mail.message']._get_default_from()
        for wizard in self:
            Model = self.env[wizard.res_model]
            document = Model.browse(wizard.res_id)

            # filter partner_ids to get the new followers, to avoid sending email to already following partners
            new_partners = wizard.partner_ids - document.message_partner_ids
            new_channels = wizard.channel_ids - document.message_channel_ids
            document.message_subscribe(new_partners.ids, new_channels.ids)

            model_ids = self.env['ir.model'].search([('model', '=',
                                                      wizard.res_model)])
            model_name = model_ids.name_get()[0][1]
            # send an email if option checked and if a message exists (do not send void emails)
            if wizard.send_mail and wizard.message and not wizard.message == '<br>':  # when deleting the message, cleditor keeps a <br>
                message = self.env['mail.message'].create({
                    'subject':
                    _('Invitation to follow %s: %s') %
                    (model_name, document.name_get()[0][1]),
                    'body':
                    wizard.message,
                    'record_name':
                    document.name_get()[0][1],
                    'email_from':
                    email_from,
                    'reply_to':
                    email_from,
                    'model':
                    wizard.res_model,
                    'res_id':
                    wizard.res_id,
                    'no_auto_thread':
                    True,
                })
                new_partners.with_context(auto_delete=True)._notify(
                    message, force_send=True, user_signature=True)
                message.unlink()
        return {'type': 'ir.actions.act_window_close'}
class ProjectLifecycle(models.Model):
    _name = 'compassion.project.ile'
    _description = 'Project lifecycle event'
    _order = 'date desc, id desc'

    project_id = fields.Many2one(
        'compassion.project', required=True, ondelete='cascade',
        readonly=True)
    date = fields.Date(readonly=True, default=fields.Date.today)
    type = fields.Selection('_get_type', readonly=True)
    action_plan = fields.Text(readonly=True)

    # Reactivation
    ##############
    icp_improvement_desc = fields.Text(readonly=True)

    # Suspension
    ############
    suspension_start_date = fields.Date(readonly=True)
    suspension_end_date = fields.Date(readonly=True)
    suspension_detail = fields.Char(readonly=True)
    suspension_reason_ids = fields.Many2many(
        'icp.lifecycle.reason', string='Suspension reason', readonly=True)

    hold_cdsp_funds = fields.Boolean(readonly=True)
    hold_csp_funds = fields.Boolean(readonly=True)
    hold_gifts = fields.Boolean(readonly=True)
    hold_s2b_letters = fields.Boolean(readonly=True)
    hold_b2s_letters = fields.Boolean(readonly=True)
    hold_child_updates = fields.Boolean(readonly=True)
    is_beneficiary_information_updates_withheld = fields.Boolean(
        readonly=True)

    extension_1 = fields.Boolean(
        help='Suspension is extended by 30 days', readonly=True)
    extension_1_reason = fields.Text(readonly=True)
    extension_2 = fields.Boolean(
        help='Suspension is extended by additional 30 days (60 in total)',
        readonly=True)
    extension_2_reason = fields.Text(readonly=True)

    # Transition
    ############
    transition_date = fields.Date(readonly=True)
    transition_complete = fields.Boolean(readonly=True)
    details = fields.Text(readonly=True)
    transition_reason_ids = fields.Many2many(
        'icp.lifecycle.reason', string='Transition reason', readonly=True)
    projected_transition_date = fields.Date(readonly=True)
    future_involvement_ids = fields.Many2many(
        'icp.involvement', string='Future involvement', readonly=True)

    name = fields.Char(readonly=True)
    reactivation_date = fields.Date(readonly=True)
    project_status = fields.Selection('_get_project_status', readonly=True)

    @api.model
    def _get_type(self):
        return [
            ('Suspension', 'Suspension'),
            ('Reactivation', 'Reactivation'),
            ('Transition', 'Transition'),
        ]

    @api.model
    def _get_project_status(self):
        return [
            ('Active', 'Active'),
            ('Phase Out', 'Phase Out'),
            ('Suspended', 'Suspended'),
            ('Transitioned', 'Transitioned'),
        ]

    @api.model
    def process_commkit(self, commkit_data):
        project_mapping = mapping.new_onramp_mapping(
            self._name,
            self.env,
            'new_project_lifecyle')

        lifecycle_ids = list()
        for single_data in commkit_data.get('ICPLifecycleEventList',
                                            [commkit_data]):
            vals = project_mapping.get_vals_from_connect(single_data)
            lifecycle = self.create(vals)
            lifecycle_ids.append(lifecycle.id)

            lifecycle.project_id.status_date = fields.Date.today()

        return lifecycle_ids