class product_product_line(models.Model): """Product of product""" _name = "product.product.line" _description = 'product product line' product_line_id = fields.Many2one('rr.housekeeping.line', 'Product line id') product_product_id = fields.Many2one('product.product', 'Product', required=True) qty = fields.Float('Qty', size=10) uom = fields.Many2one('uom.uom', 'UOM') api.onchange('product_product_id') @api.onchange('product_product_id') def onchange_product(self): if self.product_product_id: print('product_product_id==============', self.product_product_id) uom = self.product_product_id.uom_id.id self.uom = uom @api.model def create(self, vals): if 'qty' in vals: print("---------------------------------", vals['qty']) if vals['qty'] <= 0.0: raise Warning('Product Quntity should not be 0 ') return super(product_product_line, self).create(vals)
def _check_ks_is_combo(self): """ Manage product type: if product is combo, then product type must also be combo product """ if self.ks_is_combo: ======= @api.onchange('is_combo', 'type')
def _onchange_partner(self): if self.partner_id: self.delivery_id = self.partner_id # MAJ d'un autre champ # OU vals = {'delivery_id': self.partner_id.id} self.update(vals) # M2M : 2 possibilités : # - liste d'IDs, mais ça va AJOUTER les ids, comme (4, [IDs]) # - [(6, 0, [IDs])], ce qui va remplacer les ids # (cf module product_category_tax dans akretion/odoo-usability) # On utilise un autre M2M pour le mettre à jour, on peut faire # self.champ_M2M_ids.ids -> ça donne la liste des IDs # M2O : recordset (ou ID) # O2M : exemple v10 dans purchase/models/account_invoice.py # méthode purchase_order_change() # là, Odoo va jouer automatiquement le @api.onchange du champ delivery_id # pas besoin d'appeler le onchange de delivery_id dans notre code # Here, all form values are set on self # assigned values are not written to DB, but returned to the client # It is not possible to output a warning # It is not possible to put a raise UserError() # in this function (it will crash odoo) res = {'warning': {'title': _('Be careful'), {'message': _('here is the msg')}} # pour un domaine res = {'domain': { 'champ1': "[('product_id', '=', product_id)]", 'champ2': "[]"}, } # si on a besoin de changer le contexte (astuce qui peut être utile # pour ne pas déclancher en cascade les autres api.onchange qui filtreraient # sur le contexte self.env.context = self.with_context(olive_onchange=True).env.context # astuce trouvée sur https://github.com/odoo/odoo/issues/7472 return res # si je n'ai ni warning ni domain, je n'ai pas besoin de faire un return # La fonction de calcul du champ function price_subtotal @api.one # auto-loop decorator @api.depends('price_unit', 'discount', 'invoice_line_tax_id', 'quantity', 'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id') # @api.depends est utilisé pour: invalidation cache, recalcul, onchange # donc, maintenant, le fait d'avoir un champ calculé fait qu'il est # automatiquement mis à jour dans la vue quand un de ses champs 'depends' # est modifié ! COOOOOL ! # ATTENTION : si chgt de @api.depends, faire -u module ! # Pour un one2many : ne PAS juste indiquer le nom du champ o2m, sinon il ne fait rien # il faut aussi indiquer un champ sur le O2M. Exemple : 'line_ids.request_id' # Apparemment, on peut mettre dans @api.depends un champ fonction stocké et ça va bien # faire le recalcul en cascade # (ça n'a pas l'air de marcher qd on met un api.depends sur un champ non stocké) def _compute_price(self): price = self.price_unit * (1 - (self.discount or 0.0) / 100.0) taxes = self.invoice_line_tax_id.compute_all(price, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id) self.price_subtotal = taxes['total'] # calcul et stockage de la valeur self.second_field = 'iuit' # calcul et stockage d'un 2e champ # equivalent de multi='pouet' # Pour un champ O2M ou M2M, envoyer un recordset multiple ou une liste d'IDS # pour un champ M2O, donner le recordset ou l'ID if self.invoice_id: self.price_subtotal = self.invoice_id.currency_id.round(self.price_subtotal) # Pas besoin de return ! # on ne peut PAS faire un self.write({}) dans la fonction de calcul d'un champ fonction # Pour un champ fonction, on peut aussi faire @api.multi: # untaxed = fields.Float(compute='_amounts') # taxes = fields.Float(compute='_amounts') # total = fields.Float(compute='_amounts') @api.multi @api.depends('lines.amount', 'lines.taxes') def _amounts(self): for order in self: order.untaxed = sum(line.amount for line in order.lines) order.taxes = sum(line.taxes for line in order.lines) order.total = order.untaxed + order + taxes # Champ fonction inverse='_inverse_price' @api.onchange('name') # add @api.onchange on an inverse method to have it apply immediately and not upon save def _inverse_loud(self): for rec in self: rec.name = (rec.loud or '').lower() # MAJ du ou des autres champs # Champ fonction search='_search_price' def _search_loud(self, operator, value): if value is not False: value = value.lower() today = fields.Date.context_today(self) self._cr.execute('SELECT id FROM [cur_obj] WHERE (fortress_type <> %s OR (fortress_type = %s AND effectivity_date is not null)) AND (end_date is null OR end_date > %s)', (today, )) res_ids = [x[0] for x in self._cr.fetchall()] res = [('id', 'in', res_ids)] # recherche sur les autres champs return res # Fonction default=_default_account @api.model def _default_account(self): return valeur_par_defaut # M2O : retourne un recordset ou un ID (ou False) # (NOTE: apparemment, en v8, il veut un ID) # OUTDATED (?) : ATTENTION, si on veut un M2O à False, il ne pas que la fonction # _default_account retourne False mais self.env['..'].browse(False) # O2M : retourne une liste de dict contenant la valeur des champs # M2M : retourne un recordset multiple ? # date : string ou objet datetime # Fonction pour fields.selection @api.model def _type_list_get(self): return [('key1', _('String1')), ('key2', _('String2'))] ### CHAMPS # id, create_uid, write_uid, create_date et write_date # sont déjà utilisable dans le code python sans re-définition active = fields.Boolean(default=True) # Par défaut, string = nom du champ avec majuscule pour chaque début de mot login = fields.Char( string='Login', size=16, translate=True, required=True, help="My help message") display_name = fields.Char( string='Display Name', compute='_compute_display_name', readonly=True, store=True) comment = fields.Text(string='Comment', translate=True) html = fields.Html(string='report', translate=True) code_digits = fields.Integer( string='# of Digits', track_visibility='onchange', default=12, groups='base.group_user') # OU groups=['base.group_user', 'base.group_hr_manager'] # groups = XMLID : restriction du read/write et invisible ds les vues ET EXPORT # v13: track_visibility='onchange' => tracking=X sequence = fields.Integer(default=10) # track_visibility = always ou onchange amount_untaxed = fields.Float( 'Amount untaxed', digits='Product Unit of Measure', group_operator="avg") # Utile pour un pourcentage par exemple # v13 : digits='Product Unit of Measure' # v12- : digits=dp.get_precision('Account') # digits=(precision, scale) exemple (16, 2) # Scale est le nombre de chiffres après la virgule # quand le float est un fields.float ou un fields.function, # on met l'option : digits=dp.get_precision('Account') # Autres valeurs possibles pour get_precision : product/product_data.xml # Product Price, Discount, Stock Weight, Product Unit of Measure, # Product UoS (v8 only) # fields.Monetary is only in version >= 9.0 debit = fields.Monetary(default=0.0, currency_field='company_currency_id') start_date = fields.Date( string='Start Date', copy=False, default=fields.Date.context_today, index=True) # similaire : fields.Datetime and fields.Time start_datetime = fields.Datetime( string='Start Date and Time', default=fields.Datetime.now) # index=True => the field will be indexed in the database # (much faster when you search on that field) type = fields.Selection([ ('import', 'Import'), ('export', 'Export'), ], string="Type", default=lambda self: self._context.get('type', 'export')) # FIELDS.SELECTION ac selection dynamique : # type = fields.Selection('_type_list_get', string='Type', help='Pouet'), # Plus besoin de la double fonction pour que la 2e soit héritable # Pour ajouter des champs à un fields.Selection existant: # fields.Selection( # selection_add=[('new_key1', 'My new key1'), ('new_key2', 'My New Key2')]) # v14 : ondelete={"new_key1": "set default"} # other possible options for ondelete: set null, cascade (delete the records !) # Pour afficher la valeur 'lisible' du champ selection (v12+): # rec._fields['type'].convert_to_export(rec.type, rec) picture = fields.Binary(string='Picture', attachment=True) # Pour fields.binary, il existe une option filters='*.png, *.gif', # qui restreint les formats de fichiers sélectionnables dans # la boite de dialogue, mais ça ne marche pas en GTK (on # ne peut rien sélectionner) et c'est pas supporté en Web, cf # https://bugs.launchpad.net/openobject-server/+bug/1076895 picture_filename = fields.Char(string='Filename') # Les champs "picture" et "picture_filename" sont liés ensemble dans la vue # via la balise filename="picture_filename" sur le champ 'picture' # Il faut que le champ 'picture_filename' soit présent dans la vue # (il peut être invisible) # Pour un fichier à télécharger d'Odoo, le nom du fichier aura la valeur de # picture_filename # Pour un fichier à uploader dans Odoo, 'picture_filename' vaudra le nom # du fichier uploadé par l'utilisateur # Exemple de champ fonction stocké price_subtotal = fields.Float( string='Amount', digits= dp.get_precision('Account'), store=True, readonly=True, compute='_compute_price') # Exemple de champ function non stocké avec fonction inverse loud = fields.Char( store=False, compute='_compute_loud', inverse='_inverse_loud', search='_search_loud') account_id = fields.Many2one('account.account', string='Account', required=True, domain=[('type', 'not in', ['view', 'closed'])], default=lambda self: self._default_account(), check_company=True) # L'utilisation de lambda permet d'hériter la fonction _default_account() sans # hériter le champ. Sinon, on peut aussi utiliser default=_default_account # Possibilité d'hériter un domaine: # domain=lambda self: [('reconcile', '=', True), ('user_type_id.id', '=', self.env.ref('account.data_account_type_current_assets').id), ('deprecated', '=', False)] company_id = fields.Many2one( 'res.company', string='Company', ondelete='cascade', required=True, default=lambda self: self.env['res.company']._company_default_get() default=lambda self: self.env.company) # v13 # si on veut que tous les args soient nommés : comodel_name='res.company' user_id = fields.Many2one( 'res.users', string='Salesman', default=lambda self: self.env.user) # ATTENTION : si j'ai déjà un domaine sur la vue, # c'est le domaine sur la vue qui prime ! # ondelete='cascade' : # le fait de supprimer la company va supprimer l'objet courant ! # ondelete='set null' (default) # si on supprime la company, le champ company_id est mis à 0 # ondelete='restrict' : # si on supprime la company, ça déclanche une erreur d'intégrité ! # Champ Relation company_currency_id = fields.Many2one( 'res.currency', string='Currency', related='company_id.currency_id', store=True) # option related_sudo=True by default # ATTENTION, en nouvelle API, on ne peut PAS faire un fields.Char qui # soit un related d'un fields.Selection (bloque le démarrage d'Odoo # sans message d'erreur !) line_ids = fields.One2many( 'product.code.line', 'parent_id', string='Product lines', states={'done': [('readonly', True)]}, copy=True) # OU comodel_name='product.code.line', inverse_name='parent_id' # 2e arg = nom du champ sur l'objet destination qui est le M20 inverse # en v8 : # copy=True pour que les lignes soient copiées lors d'un duplicate # sinon, mettre copy=False (ça ne peut être qu'un booléen) # Valeur par défaut du paramètre "copy": True for normal fields, False for # one2many and computed fields, including property fields and related fields # ATTENTION : pour que states={} marche sur le champ A et que le # champ A est dans la vue tree, alors il faut que le champ "state" # soit aussi dans la vue tree. partner_ids = fields.Many2many( 'res.partner', 'product_code_partner_rel', 'code_id', 'partner_id', 'Related Partners') # 2e arg = nom de la table relation # 3e arg ou column1 = nom de la colonne dans la table relation # pour stocker l'ID du product.code # 4e arg ou column2 = nom de la colonne dans la table relation # pour stocker l'ID du res.partner # OU partner_ids = fields.Many2many( 'res.partner', column1='code_id', column2='partner_id', string='Related Partners') # OU partner_ids = fields.Many2many( 'res.partner', string='Related Partners') # Pour les 2 dernières définitions du M2M, il ne faut pas avoir # plusieurs champs M2M qui pointent du même obj vers le même obj # Champ property: il suffit de définit le champ comme un champ normal # et d'ajouter un argument company_dependent=True # Quand on veut lire la valeur d'un champ property dans une société # qui n'est pas celle de l'utilisateur, il faut passer dans le context # 'force_company': 8 (8 = ID de la company) }
def action_view_partner_invoices(self): self.ensure_one() action = self.env.ref('account.action_move_out_invoice_type').read()[0] action['domain'] = [ <<<<<<< HEAD ('type', 'in', ('out_invoice', 'out_refund')), ======= ('move_type', 'in', ('out_invoice', 'out_refund')), >>>>>>> f0a66d05e70e432d35dc68c9fb1e1cc6e51b40b8 ('partner_id', 'child_of', self.id), ] action['context'] = {'default_move_type':'out_invoice', 'move_type':'out_invoice', 'journal_type': 'sale', 'search_default_unpaid': 1} return action <<<<<<< HEAD @api.onchange('company_id', 'parent_id') def _onchange_company_id(self): super(ResPartner, self)._onchange_company_id() if self.company_id: company = self.company_id else: company = self.env.company return {'domain': {'property_account_position_id': [('company_id', 'in', [company.id, False])]}} ======= >>>>>>> f0a66d05e70e432d35dc68c9fb1e1cc6e51b40b8 def can_edit_vat(self): ''' Can't edit `vat` if there is (non draft) issued invoices. ''' can_edit_vat = super(ResPartner, self).can_edit_vat() if not can_edit_vat: return can_edit_vat
class ResPartnerPermission(models.Model): _name = 'res.partner.permission' _description = 'Permisos en contactos' permission_entity_id = fields.Many2one('res.entity', 'Entidades') permission_specialty_id = fields.Many2one('res.specialty.pure', 'Especialidades') permission_city_id = fields.Many2one('res.city', 'Ciudades') permission_state_id = fields.Many2one('res.country.state', 'Departamentos') is_permission_state = fields.Boolean('Todos los Departamentos', default="True") is_permission_entity = fields.Boolean('Todas las Entidades', default="True") is_permission_specialty = fields.Boolean('Todas las Especialidades', default="True") is_permission_city = fields.Boolean('Todas las Ciudades', default="True") partner_id = fields.Many2one('res.partner') permission_judged_id = fields.Many2one('res.partner', string="Despacho") is_permission_judged_id = fields.Boolean(string="Todos los Despachos", default="True") #permission_rol_id = fields.Many2one('res.partner.permission.group', string='Permission Rol') endpoint_key = fields.Char(string="Cadena Endpoint", compute="_compute_endpoint_key") api.onchange('permission_entity_id', 'permission_specialty_id', 'permission_city_id', 'permission_state_id', 'is_permission_state', 'is_permission_entity', 'is_permission_specialty', 'is_permission_city') def _compute_endpoint_key(self): for rec in self: key = '' if rec.is_permission_state: key += 'ALL' elif not rec.is_permission_state and rec.permission_city_id and not rec.permission_state_id: key += rec.permission_city_id.zipcode elif not rec.is_permission_state and not rec.permission_city_id and rec.permission_state_id: key += rec.permission_state_id.dane_code if rec.is_permission_entity: key += '-ALL' elif not rec.is_permission_entity and rec.permission_entity_id: key += '-' + rec.permission_entity_id.code if rec.is_permission_specialty: key += '-ALL' elif not rec.is_permission_specialty and rec.permission_specialty_id: key += '-' + rec.permission_specialty_id.code if rec.is_permission_judged_id: key += '-ALL' elif not rec.is_permission_judged_id and rec.permission_judged_id: key += '-' + rec.permission_judged_id.code rec.endpoint_key = key @api.model def create(self, vals): if 'permission_state_id' in vals and 'permission_city_id' in vals and 'is_permission_state' in vals: if vals['permission_state_id'] and vals[ 'permission_city_id'] and not vals['is_permission_state']: raise UserError( 'Departamentos y Ciudades son excluyentes, debe dejar en blanco alguno de los dos' ) if not vals['permission_state_id'] and not vals[ 'permission_city_id'] and not vals['is_permission_state']: raise UserError('Seleccione un departamento o una ciudad') res = super(ResPartnerPermission, self).create(vals) return res
It can also directly be set on each product.""") property_stock_valuation_account_id = fields.Many2one( 'account.account', 'Stock Valuation Account', company_dependent=True, domain="[('company_id', '=', allowed_company_ids[0]), ('deprecated', '=', False)]", check_company=True, help="""When automated inventory valuation is enabled on a product, this account will hold the current value of the products.""",) @api.constrains('property_stock_valuation_account_id', 'property_stock_account_output_categ_id', 'property_stock_account_input_categ_id') def _check_valuation_accouts(self): # Prevent to set the valuation account as the input or output account. for category in self: valuation_account = category.property_stock_valuation_account_id input_and_output_accounts = category.property_stock_account_input_categ_id | category.property_stock_account_output_categ_id if valuation_account and valuation_account in input_and_output_accounts: raise ValidationError(_('The Stock Input and/or Output accounts cannot be the same than the Stock Valuation account.')) @api.onchange('property_cost_method') def onchange_property_valuation(self): if not self._origin: # don't display the warning when creating a product category return return { 'warning': { 'title': _("Warning"), 'message': _("Changing your cost method is an important change that will impact your inventory valuation. Are you sure you want to make that change?"), } } def write(self, vals): impacted_categories = {} move_vals_list = [] Product = self.env['product.product']
class stock_picking(models.Model): _inherit = 'stock.picking' internal_note = fields.Text(string='Internal Note', ) def action_cancel(self): res = super(stock_picking, self).action_cancel() if res == True: for line in self.move_lines: line.write({ 'product_uom_qty': 0, 'price_tax': 0, 'price_total': 0, 'price_subtotal': 0 }) _logger.info('===========CANCEL_DELIVERY============') _logger.info(self.delivery_cost) self.write({'delivery_cost': 0}) _logger.info(self.delivery_cost) return res def default_start_point(self): address = self.env['cb.delivery.address'].search( [('auto_select_start_point', '=', True)], limit=1) if address: return address return start_point = fields.Many2one('cb.delivery.address', string="Điểm bắt đầu", default=default_start_point) end_point = fields.Many2one('cb.delivery.address', string="Điểm đến") payment_term_id = fields.Many2one('account.payment.term', string='Payment Term') delivery_service = fields.Selection([ ('internal', "Nội bộ"), ('collaborators', "Cộng tác viên"), ('partner', "Đối tác"), ], default="internal", string="Dịch vụ giao hàng") collaborators = fields.Many2one('res.partner', string="Nhân viên giao hàng") delivery_fee = fields.Float('Phụ phí giao hàng') source_way = fields.Float('Quãng đường đi', compute='_compute_way', store=True) destination_way = fields.Float('Quãng đường về', compute='_compute_way', store=True) total_way = fields.Float('Tổng quãng đường', compute='_compute_way', store=True) forecast_time = fields.Char('Thời gian dự đoán', compute='_compute_way', store=True) start_time = fields.Float('Thời gian xuất phát') end_time = fields.Float('Thời gian trở về') duration_time = fields.Char('Thời gian thực tế', compute='_compute_time', store=True) postage_total = fields.Float('Tổng cước phí', compute="_compute_postage_total", store=True) postage_delivery = fields.Float('Phí vận chuyển', compute='_compute_way', store=True) postage_delivery_fee = fields.Float('Phụ phí giao hàng') google_map = fields.Char('Map', compute='_compute_way', store=True) stock_tranfer_date = fields.Datetime('Dự kiến giao hàng') def default_stock_live_date(self): return self.stock_tranfer_date stock_live_date = fields.Datetime('Thực tế giao hàng', default=default_stock_live_date) stock_outin_date = fields.Datetime('Thời gian xuất/nhập kho') def do_new_transfer(self): res = super(stock_picking, self).do_new_transfer() _logger.info('++++++++++++++++++++++++++++++++++++++++++') _logger.info(res) self.stock_outin_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S") return res warehouse_name = fields.Char(string="Source Location Name", related='location_id.warehouse_name', store=True) warehouse_destination_name = fields.Char( string="Destination Location Name", related='location_dest_id.warehouse_name', store=True) delivery_status = fields.Selection([('pending_delivery', 'Chờ giao'), ('delivering', 'Đang giao'), ('delivered', 'Đã giao')], default="pending_delivery") note_stock_picking = fields.Text(string='Ghi chú', compute="_compute_get_note_stock_picking", store=True) customer_reference = fields.Char("Customer reference") customer_type = fields.Selection([('cash', 'Tiền mặt'), ('debts', 'Công nợ'), ('deposit', 'Ký gởi'), ('internal', 'Nội bộ')], compute="_compute_get_customer_type") amount_untaxed = fields.Float(string='Untaxed Amount', store=True, readonly=True, compute='_amount_all') amount_tax = fields.Float(string='Taxes', store=True, readonly=True, compute='_amount_all') amount_total = fields.Float(string='Total', store=True, readonly=True, compute='_amount_all') pricelist_id = fields.Many2one('product.pricelist', string='Pricelist', readonly=True, help="Pricelist for current sales order.") apply_discount = fields.Boolean('Apply Discount', default=False) discount_type_id = fields.Many2one('discount.type', 'Discount Type') discount_value = fields.Float('Sale Discount') discount_value_const = fields.Float('Sale Discount') discount_account = fields.Many2one('account.account', 'Discount Account') amount_after_discount = fields.Float('Amount After Discount', store=True, readonly=True, compute='_amount_all') @api.depends('discount_value', 'delivery_cost', 'move_lines.price_total') def _amount_all(self): """ Compute the total amounts of the SO. """ discount_tmp = 0.0 delivery_cost = 0.0 amount_untaxed = 0.0 amount_untaxed_tmp = 0.0 #add more amount_tax_tmp = 0.0 #add more amount_tax = 0.0 amount_after_discount = 0.0 discount = 0.0 for pick in self: amount_untaxed = amount_tax = 0.0 for line in pick.move_lines: if line.product_uom_qty > 0: amount_untaxed += line.price_subtotal amount_tax += line.price_tax if line.price_tax != False else 0 total_cost = amount_untaxed + amount_tax if ( amount_untaxed + amount_tax) > 0 else 0.0 amount_after_discount = total_cost discount_value = pick.discount_value if pick.apply_discount == True: discount_type_percent = self.env[ 'ir.model.data'].xmlid_to_res_id( 'bi_sale_purchase_invoice_discount.discount_type_percent_id' ) discount_type_fixed = self.env['ir.model.data'].xmlid_to_res_id( 'bi_sale_purchase_invoice_discount.discount_type_fixed_id') if pick.discount_type_id.id == discount_type_fixed: discount = total_cost - pick.discount_value amount_after_discount = discount elif pick.discount_type_id.id == discount_type_percent: discount_percent = total_cost * ( (pick.discount_value or 0.0) / 100.0) discount = total_cost - discount_percent amount_after_discount = discount else: amount_after_discount = total_cost _logger.info(pick.picking_type_code) if pick.picking_type_code == 'incoming': pick.update({ 'discount_tmp': 0, 'amount_untaxed': amount_untaxed, 'amount_tax': amount_tax, 'amount_total': amount_after_discount + delivery_cost, 'total_cost': total_cost, 'amount_after_discount': amount_after_discount }) elif pick.picking_type_code == 'outgoing': delivery_cost = pick.delivery_cost if (amount_after_discount + delivery_cost) <= 0: discount_tmp = abs(amount_after_discount + delivery_cost) discount_value = total_cost + delivery_cost # amount_after_discount -= delivery_cost if pick.pricelist_id: amount_untaxed_tmp = pick.pricelist_id.currency_id.round( amount_untaxed) amount_tax_tmp = pick.pricelist_id.currency_id.round( amount_tax) else: amount_untaxed_tmp = amount_untaxed amount_tax_tmp = amount_tax amount_untaxed = amount_untaxed_tmp if amount_untaxed_tmp > 0 else amount_untaxed amount_tax = amount_tax_tmp if amount_tax_tmp > 0 else amount_tax amount_total = amount_after_discount + delivery_cost if ( amount_after_discount + delivery_cost) > 0 else 0.0 pick.update({ 'discount_value': discount_value, 'discount_tmp': discount_tmp, # 'delivery_cost': delivery_cost if pick.state != 'cancel' else 0, 'amount_untaxed': amount_untaxed if pick.state != 'cancel' else 0, 'amount_tax': amount_tax if pick.state != 'cancel' else 0, 'amount_total': amount_total if pick.state != 'cancel' else 0, 'total_cost': total_cost if pick.state != 'cancel' else 0, 'amount_after_discount': amount_after_discount if pick.state != 'cancel' else 0 }) def _create_backorder(self, backorder_moves=[]): res = super(stock_picking, self)._create_backorder(backorder_moves) if res: do = self.search([('backorder_id', '=', self.id)], limit=1) if do: if self.apply_discount == True: discount_value = self.discount_value discount_type_fixed = self.env[ 'ir.model.data'].xmlid_to_res_id( 'bi_sale_purchase_invoice_discount.discount_type_fixed_id' ) if self.discount_type_id.id == discount_type_fixed: if self.discount_tmp > 0: discount_value = self.discount_tmp else: discount_value = 0.0 do.write({ 'discount_value': discount_value, 'delivery_cost': 0.0 }) do.update_total() self.update_total() else: do.write({'delivery_cost': 0.0}) do.update_total() self.update_total() return res def update_total(self): self._amount_all() # discount_tmp = 0.0 # delivery_cost = 0.0 # amount_untaxed = 0.0 # amount_untaxed_tmp = 0.0 #add more # amount_tax_tmp = 0.0 #add more # amount_tax = 0.0 # amount_after_discount = 0.0 # discount = 0.0 # amount_untaxed = amount_tax = 0.0 # for line in self.move_lines: # if line.product_uom_qty > 0: # amount_untaxed += line.price_subtotal # amount_tax += line.price_tax if line.price_tax != False else 0 # total_cost = amount_untaxed + amount_tax if (amount_untaxed + amount_tax) > 0 else 0.0 # amount_after_discount = total_cost # discount_value = self.discount_value # if self.apply_discount == True: # discount_type_percent = self.env['ir.model.data'].xmlid_to_res_id('bi_sale_purchase_invoice_discount.discount_type_percent_id') # discount_type_fixed = self.env['ir.model.data'].xmlid_to_res_id('bi_sale_purchase_invoice_discount.discount_type_fixed_id') # discount_value = self.discount_value # if self.discount_type_id.id == discount_type_fixed: # discount = total_cost - self.discount_value # amount_after_discount = discount # elif self.discount_type_id.id == discount_type_percent: # discount_percent = total_cost * ((self.discount_value or 0.0) / 100.0) # discount = total_cost - discount_percent # amount_after_discount = discount # else: # amount_after_discount = total_cost # _logger.info(self.picking_type_code) # if self.picking_type_code == 'incoming': # self.write({ # 'discount_tmp': 0, # 'amount_untaxed': amount_untaxed, # 'amount_tax': amount_tax, # 'amount_total': amount_after_discount + delivery_cost, # 'total_cost' : total_cost, # 'amount_after_discount' : amount_after_discount, # 'no_update': 1 # }) # elif self.picking_type_code == 'outgoing': # delivery_cost = self.delivery_cost # if (amount_after_discount + delivery_cost) < 0: # discount_tmp = abs(amount_after_discount + delivery_cost) # discount_value = total_cost + delivery_cost # amount_after_discount -= delivery_cost # if self.pricelist_id: # amount_untaxed_tmp = self.pricelist_id.currency_id.round(amount_untaxed) # amount_tax_tmp = self.pricelist_id.currency_id.round(amount_tax) # else: # amount_untaxed_tmp = amount_untaxed # amount_tax_tmp = amount_tax # amount_untaxed = amount_untaxed_tmp if amount_untaxed_tmp > 0 else amount_untaxed # amount_tax = amount_tax_tmp if amount_tax_tmp > 0 else amount_tax # amount_total = amount_after_discount + delivery_cost if (amount_after_discount + delivery_cost) > 0 else 0.0 # self.write({ # 'discount_value': discount_value, # 'discount_tmp': discount_tmp, # # 'delivery_cost': delivery_cost if self.state != 'cancel' else 0, # 'amount_untaxed': amount_untaxed if self.state != 'cancel' else 0, # 'amount_tax': amount_tax if self.state != 'cancel' else 0, # 'amount_total': amount_total if self.state != 'cancel' else 0, # 'total_cost' : total_cost if self.state != 'cancel' else 0, # 'amount_after_discount' : amount_after_discount if self.state != 'cancel' else 0, # 'no_update': 1 # }) @api.model def create(self, vals): po = so = None if self.picking_type_code: if self.picking_type_code == 'incoming': po = self.env['purchase.order'].search( [('name', '=', self.origin)], limit=1) # if po: # vals.update({ # 'amount_untaxed': po.amount_untaxed, # 'amount_tax': po.amount_tax, # 'amount_total': po.amount_total, # 'delivery_cost': 0, # 'discount_tmp': 0, # 'total_cost': po.amount_untaxed + po.amount_tax, # 'apply_discount': po.apply_discount, # 'discount_value': po.discount_value, # 'discount_value_const': po.discount_value, # 'discount_account': po.discount_account.id if po.discount_account != False else False # }) elif self.picking_type_code == 'outgoing': so = self.env['sale.order'].search( [('name', '=', self.origin)], limit=1) # if so: # vals.update({ # 'amount_untaxed': so.amount_untaxed, # 'amount_tax': so.amount_tax, # 'amount_total': so.amount_total, # 'pricelist_id': so.pricelist_id.id if so.pricelist_id != False else False, # 'delivery_cost': so.delivery_cost, # 'discount_tmp': 0, # 'total_cost': so.amount_untaxed + so.amount_tax, # 'apply_discount': so.apply_discount, # 'discount_type_id': so.discount_type_id.id if so.discount_type_id != False else False, # 'discount_value': so.discount_value, # 'discount_value_const': so.discount_value, # 'discount_account': so.discount_account.id if so.discount_account != False else False # }) _logger.info("=============LOG====") _logger.info(vals) if vals.has_key('move_lines'): for index, value in enumerate(vals['move_lines']): if vals['move_lines'][index][2]: product_id = vals['move_lines'][index][2]['product_id'] product = self.env['product.product'].browse(product_id) vals['move_lines'][index][2][ 'product_uom'] = product.uom_po_id.id or product.uom_id.id # if vals.has_key('pack_operation_product_ids'): # for index,value in enumerate(vals['pack_operation_product_ids']): # if vals['pack_operation_product_ids'][index][2]: # product_id = vals['pack_operation_product_ids'][index][2]['product_id'] # product = self.env['product.product'].browse(product_id) # vals['pack_operation_product_ids'][index][2]['product_uom'] = product.uom_po_id.id or product.uom_id.id _logger.info(vals) res = super(stock_picking, self).create(vals) if po: if po.delivery_status != self.state: po.write({'delivery_status': self.state}) if so: if so.delivery_status != self.state: so.write({'delivery_status': self.state}) return res @api.multi def write(self, vals): po = so = None _logger.info(vals) for record in self: if record.picking_type_code: if record.picking_type_code == 'incoming': po = record.env['purchase.order'].search( [('name', '=', record.origin)], limit=1) # if po: # if vals.has_key('no_update') == False: # vals.update({ # #'amount_untaxed': po.amount_untaxed, # #'amount_tax': po.amount_tax, # #'amount_total': po.amount_total, # 'delivery_cost': 0, # 'apply_discount': po.apply_discount, # 'discount_type_id': po.discount_type_id.id if po.discount_type_id != False else False, # 'discount_value': po.discount_value, # 'discount_value_const': po.discount_value, # 'discount_account': po.discount_account.id if po.discount_account != False else False # }) # else: # del vals['no_update'] elif record.picking_type_code == 'outgoing': so = record.env['sale.order'].search( [('name', '=', record.origin)], limit=1) # if so: # if vals.has_key('no_update') == False: # vals.update({ # #'amount_untaxed': so.amount_untaxed if record.state != 'cancel' else 0, # #'amount_tax': so.amount_tax if record.state != 'cancel' else 0, # #'amount_total': so.amount_total if record.state != 'cancel' else 0, # 'delivery_cost': so.delivery_cost if record.state != 'cancel' else 0, # 'apply_discount': so.apply_discount, # 'discount_type_id': so.discount_type_id.id if so.discount_type_id != False else False, # 'discount_value': so.discount_value, # 'discount_value_const': so.discount_value, # 'discount_account': so.discount_account.id if so.discount_account != False else False # }) # else: # del vals['no_update'] _logger.info("===========MOVE_LINES============") _logger.info(vals) if vals.has_key('move_lines'): for index, value in enumerate(vals['move_lines']): if vals['move_lines'][index][2] and vals['move_lines'][index][ 2].has_key('product_id'): product_id = vals['move_lines'][index][2]['product_id'] product = self.env['product.product'].browse(product_id) vals['move_lines'][index][2][ 'product_uom'] = product.uom_po_id.id or product.uom_id.id res = super(stock_picking, self).write(vals) for record in self: if po: if po.delivery_status != record.state: po.write({'delivery_status': record.state}) if so: if so.delivery_status != record.state: so.write({'delivery_status': record.state}) if vals.has_key("delivery_service") and vals.has_key( "postage_delivery") and ( vals.get("delivery_service") == 'partner' or vals.get("delivery_service") == 'internal'): record.write( {'postage_delivery': vals.get("postage_delivery")}) return res @api.onchange('delivery_service') def _onchange_delivery_service(self): if self.delivery_service != '': self.collaborators = None return { 'domain': { 'collaborators': [('delivery_service', '=', self.delivery_service)] } } pricelist_id = fields.Many2one('product.pricelist', string='Pricelist', required=True, readonly=True, states={ 'draft': [('readonly', False)], 'sent': [('readonly', False)] }, help="Pricelist for current sales order.") currency_id = fields.Many2one("res.currency", related='pricelist_id.currency_id', string="Currency", readonly=True, required=True) delivery_cost = fields.Monetary(string='Phí giao hàng', store=True, default=0, readonly=True) total_cost = fields.Monetary(store=True, string='Tồng tiền tạm tính', readonly=True, compute="_amount_all") discount_tmp = fields.Monetary(store=True, string='Giam gia con lai', readonly=True, compute="_amount_all") @api.onchange('start_point', 'end_point', 'delivery_service') def _onchange_start_end_point(self): if self.start_point.id > 0 and self.end_point.id > 0: return self.calc_way(self.start_point.name, self.end_point.name, self.delivery_service, self.start_point.id, self.end_point.id) @api.depends('start_point', 'end_point', 'source_way', 'destination_way', 'total_way', 'forecast_time', 'delivery_service') def _compute_way(self): if self.start_point.id > 0 and self.end_point.id > 0: ways = self.calc_way(self.start_point.name, self.end_point.name, self.delivery_service, self.start_point.id, self.end_point.id) self.source_way = ways['value']['source_way'] self.destination_way = ways['value']['destination_way'] self.total_way = ways['value']['total_way'] self.forecast_time = ways['value']['forecast_time'] self.postage_delivery = ways['value']['postage_delivery'] self.google_map = ways['value']['google_map'] def calc_way(self, start_point_name, end_point_name, delivery_service, start_point_id, end_point_id): source_way = 0.0 destination_way = 0.0 total_way = 0.0 forecast_time = '0 mins' postage_delivery = 0 google_map = '' delivery_address_info_object = self.env['cb.delivery.address.info'] delivery_address_info = delivery_address_info_object.search( [('start_point', '=', start_point_id), ('end_point', '=', end_point_id)], limit=1) if delivery_address_info: source_way = delivery_address_info.source_way destination_way = delivery_address_info.destination_way total_way = delivery_address_info.total_way forecast_time = delivery_address_info.forecast_time google_map = delivery_address_info.google_map if total_way > 0: postage_delivery = self.calc_postage_delivery( total_way, delivery_service, source_way) else: distance_matrix = gmaps.distance_matrix( origins=start_point_name, destinations=end_point_name, mode="driving") _logger.info('================GOOGLE MAPS=======================') _logger.info(distance_matrix) _logger.info(distance_matrix.get('status')) if distance_matrix.get('status') == 'OK': rows = distance_matrix.get('rows') _logger.info(rows[0]) elements = rows[0]['elements'][0] if (elements.get('status') == 'OK'): duration = elements.get('duration') distance = elements.get('distance') source_way = round(float(distance.get('value') / 1000.0), 2) destination_way = source_way total_way = source_way + destination_way forecast_time = duration.get('text') if total_way > 0: postage_delivery = self.calc_postage_delivery( total_way, delivery_service) google_map = "https://maps.google.com?saddr=" + str( start_point_name) + "&daddr=" + str(end_point_name) delivery_address_info_object.create({ 'name': start_point_name + end_point_name, 'start_point': start_point_id, 'end_point': end_point_id, 'source_way': source_way, 'destination_way': destination_way, 'total_way': total_way, 'forecast_time': forecast_time, 'google_map': google_map, }) return { 'value': { 'source_way': source_way, 'destination_way': destination_way, 'total_way': total_way, 'forecast_time': forecast_time, 'postage_delivery': postage_delivery, 'google_map': google_map } } # duration @api.onchange('start_time', 'end_time') def _get_duration_date(self): durationtext = '0 mins' if self.start_time and self.end_time: return self.calc_time(self.start_time, self.end_time) # duration = duration.days @api.depends('start_time', 'end_time', 'duration_time') def _compute_time(self): time = self.calc_time(self.start_time, self.end_time) self.duration_time = time['value']['duration_time'] def calc_time(self, start_time, end_time): duration = end_time - start_time _logger.info(duration) calcduration = duration * 60.0 if calcduration > 60: intpart = int(duration) floatpart = duration - intpart durationtext = str(int(round(intpart, 2))) + ' hours' if (floatpart > 0): _logger.info(floatpart) durationtext = durationtext + ' ' + str( int(round(floatpart * 60.0))) + ' mins' else: durationtext = str(calcduration) + ' mins' return {'value': {'duration_time': durationtext}} @api.depends('postage_delivery', 'postage_delivery_fee', 'postage_total') def _compute_postage_total(self): self.postage_total = self.postage_delivery + self.postage_delivery_fee @api.onchange('postage_delivery', 'postage_delivery_fee') def _onchange_postage(self): postage_total = self.postage_delivery + self.postage_delivery_fee return {'value': {'postage_total': postage_total}} def calc_postage_delivery(self, total_way, delivery_service, source_way=0): postage_delivery = 0 conf = self.env['ir.config_parameter'] if delivery_service == 'internal': _logger.info('internal') # gas = float(conf.get_param('gas_verification.gas')) # avggasinkm = float(conf.get_param('avggasinkm_verification.avggasinkm')) # if avggasinkm > 0: # postage_delivery = (total_way * gas) / avggasinkm else: if delivery_service == 'collaborators': if source_way < 5: postage_delivery = 20000 else: if source_way < 10: postage_delivery = 30000 else: if source_way < 15: postage_delivery = 40000 else: if source_way < 20: postage_delivery = 50000 else: postage_delivery = 60000 else: _logger.info('partner') return postage_delivery def export_data(self, fields, data=False): dataindex_1 = dataindex_2 = dataindex_3 = dataindex_4 = dataindex_5 = dataindex_6 = dataindex_7 = None _logger.info('+' * 20) for index, fieldlabel in enumerate(fields): _logger.info(fieldlabel) if fieldlabel == 'stock_tranfer_date': dataindex_1 = index if fieldlabel == 'min_date': dataindex_2 = index if fieldlabel == 'stock_outin_date': dataindex_3 = index if fieldlabel == 'stock_live_date': dataindex_4 = index if fieldlabel == 'sale_id/date_order': dataindex_5 = index if fieldlabel == 'sale_id/confirmation_date': dataindex_6 = index if fieldlabel == 'sale_id/stock_tranfer_date': dataindex_7 = index res = super(stock_picking, self).export_data(fields, data) try: for index, val in enumerate(res['datas']): if dataindex_1: service_date = res['datas'][index][dataindex_1] sdate = service_date if sdate: sdate = str(sdate) db_timezone = self.env.context.get( 'tz') or 'Asia/Ho_Chi_Minh' dbtz = pytz.timezone(db_timezone) utctz = pytz.timezone('UTC') sdate_dt = datetime.strptime(sdate, "%Y-%m-%d %H:%M:%S") utctz_dt = utctz.localize(sdate_dt, is_dst=None) db_dt = utctz_dt.astimezone(dbtz) sdate = db_dt.strftime('%m/%d/%Y %H:%M:%S') res['datas'][index][dataindex_1] = sdate if dataindex_2: service_date = res['datas'][index][dataindex_2] sdate = service_date if sdate: sdate = str(sdate) db_timezone = self.env.context.get( 'tz') or 'Asia/Ho_Chi_Minh' dbtz = pytz.timezone(db_timezone) utctz = pytz.timezone('UTC') sdate_dt = datetime.strptime(sdate, "%Y-%m-%d %H:%M:%S") utctz_dt = utctz.localize(sdate_dt, is_dst=None) db_dt = utctz_dt.astimezone(dbtz) sdate = db_dt.strftime('%m/%d/%Y %H:%M:%S') res['datas'][index][dataindex_2] = sdate if dataindex_3: service_date = res['datas'][index][dataindex_3] sdate = service_date if sdate: sdate = str(sdate) db_timezone = self.env.context.get( 'tz') or 'Asia/Ho_Chi_Minh' dbtz = pytz.timezone(db_timezone) utctz = pytz.timezone('UTC') sdate_dt = datetime.strptime(sdate, "%Y-%m-%d %H:%M:%S") utctz_dt = utctz.localize(sdate_dt, is_dst=None) db_dt = utctz_dt.astimezone(dbtz) sdate = db_dt.strftime('%m/%d/%Y %H:%M:%S') res['datas'][index][dataindex_3] = sdate if dataindex_4: service_date = res['datas'][index][dataindex_4] sdate = service_date if sdate: sdate = str(sdate) db_timezone = self.env.context.get( 'tz') or 'Asia/Ho_Chi_Minh' dbtz = pytz.timezone(db_timezone) utctz = pytz.timezone('UTC') sdate_dt = datetime.strptime(sdate, "%Y-%m-%d %H:%M:%S") utctz_dt = utctz.localize(sdate_dt, is_dst=None) db_dt = utctz_dt.astimezone(dbtz) sdate = db_dt.strftime('%m/%d/%Y %H:%M:%S') res['datas'][index][dataindex_4] = sdate if dataindex_5: service_date = res['datas'][index][dataindex_5] sdate = service_date if sdate: sdate = str(sdate) db_timezone = self.env.context.get( 'tz') or 'Asia/Ho_Chi_Minh' dbtz = pytz.timezone(db_timezone) utctz = pytz.timezone('UTC') sdate_dt = datetime.strptime(sdate, "%Y-%m-%d %H:%M:%S") utctz_dt = utctz.localize(sdate_dt, is_dst=None) db_dt = utctz_dt.astimezone(dbtz) sdate = db_dt.strftime('%m/%d/%Y %H:%M:%S') res['datas'][index][dataindex_5] = sdate if dataindex_6: service_date = res['datas'][index][dataindex_6] sdate = service_date if sdate: sdate = str(sdate) db_timezone = self.env.context.get( 'tz') or 'Asia/Ho_Chi_Minh' dbtz = pytz.timezone(db_timezone) utctz = pytz.timezone('UTC') sdate_dt = datetime.strptime(sdate, "%Y-%m-%d %H:%M:%S") utctz_dt = utctz.localize(sdate_dt, is_dst=None) db_dt = utctz_dt.astimezone(dbtz) sdate = db_dt.strftime('%m/%d/%Y %H:%M:%S') res['datas'][index][dataindex_6] = sdate if dataindex_7: service_date = res['datas'][index][dataindex_7] sdate = service_date if sdate: sdate = str(sdate) db_timezone = self.env.context.get( 'tz') or 'Asia/Ho_Chi_Minh' dbtz = pytz.timezone(db_timezone) utctz = pytz.timezone('UTC') sdate_dt = datetime.strptime(sdate, "%Y-%m-%d %H:%M:%S") utctz_dt = utctz.localize(sdate_dt, is_dst=None) db_dt = utctz_dt.astimezone(dbtz) sdate = db_dt.strftime('%m/%d/%Y %H:%M:%S') res['datas'][index][dataindex_7] = sdate except Exception: pass return res @api.depends('origin') def _compute_get_note_stock_picking(self): for record in self: if record.picking_type_code: if record.picking_type_code == 'incoming': po = record.env['purchase.order'].search( [('name', '=', record.origin)], limit=1) if po: record.note_stock_picking = po.notes elif record.picking_type_code == 'outgoing': so = record.env['sale.order'].search( [('name', '=', record.origin)], limit=1) if so: record.note_stock_picking = so.note @api.depends('partner_id') def _compute_get_customer_type(self): for record in self: if record.partner_id: if record.partner_id.customer_type: record.customer_type = record.partner_id.customer_type api.onchange('partner_id') def _onchange_partner_id(self): if record.partner_id: if record.partner_id.customer_type: record.customer_type = record.partner_id.customer_type
======= fiscal_position = self.env['account.fiscal.position'].with_company(self.company_id).get_fiscal_position( >>>>>>> f0a66d05e70e432d35dc68c9fb1e1cc6e51b40b8 self.partner_id.id, delivery_id=delivery_partner_id) if fiscal_position: self.fiscal_position_id = fiscal_position def unlink(self): downpayment_lines = self.mapped('line_ids.sale_line_ids').filtered(lambda line: line.is_downpayment) res = super(AccountMove, self).unlink() if downpayment_lines: downpayment_lines.unlink() return res @api.onchange('partner_id') def _onchange_partner_id(self): # OVERRIDE # Recompute 'partner_shipping_id' based on 'partner_id'. addr = self.partner_id.address_get(['delivery']) self.partner_shipping_id = addr and addr.get('delivery') res = super(AccountMove, self)._onchange_partner_id() # Recompute 'narration' based on 'company.invoice_terms'. if self.move_type == 'out_invoice': self.narration = self.company_id.with_context(lang=self.partner_id.lang).invoice_terms return res @api.onchange('invoice_user_id')