跳至内容
Odoo 菜单
  • 登录
  • 免费试用
  • 应用程序
    财务
    • 会计
    • 发票
    • 费用
    • 电子表格 (BI)
    • 文档
    • 电子签名
    销售
    • 客户关系管理
    • 销售
    • POS 销售点管理-零售
    • POS 销售点管理 - 餐厅
    • 订阅
    • 租赁
    网站
    • 网站设计
    • 电子商务
    • 博客
    • 论坛
    • 在线客服
    • 在线学习
    供应链
    • 库存
    • 制造
    • 产品生命周期
    • 采购
    • 维护保养
    • 品控
    人力资源
    • 员工
    • 招聘
    • 休假
    • 评价
    • 内部推荐
    • 车队
    营销
    • 社媒营销
    • 电邮营销
    • 短信营销
    • 近期活动
    • 营销自动化
    • 网上调查
    服务
    • 项目管理
    • 工时单
    • 现场服务
    • 服务台
    • 排期
    • 预约
    生产力
    • 讨论
    • 批核
    • IoT物联网
    • VoIP
    • 知识库
    • WhatsApp
    第三方应用软件 Odoo 定制 Odoo云端平台
  • 行业
    零售
    • 书店
    • 服装店
    • 家具店
    • 食品杂货店
    • 五金店
    • 玩具店
    餐饮与酒店服务
    • 酒吧及酒馆
    • 餐厅
    • 快餐
    • 民宿
    • 饮品分销商
    • 酒店
    房地产
    • 房地产代理
    • 建筑师事务所
    • 建造业
    • 地产管理
    • 园艺
    • 业主协会
    咨询
    • 会计师事务所
    • Odoo合作伙伴
    • 市场推广公司
    • 律师事务所
    • 人才招聘
    • 审核 & 认证
    制造
    • 纺织
    • 金属
    • 家具
    • 食品
    • 啤酒厂
    • 企业礼品
    保健与健身
    • 体育俱乐部
    • 眼镜店
    • 健身中心
    • 健康从业者
    • 药房
    • 发型屋
    商贸服务
    • 维修人员
    • IT 硬件及支持
    • 太阳能系统
    • 鞋匠
    • 清洁服务
    • 暖通空调服务
    其他
    • 非营利组织
    • 环境机构
    • 广告牌租赁
    • 摄影服务
    • 自行车租赁
    • 软件经销商
    浏览所有行业
  • 社区
    学习
    • 教学视频
    • 文档
    • 认证
    • 培训
    • 博客
    • 播客
    赋能教育
    • 教育计划
    • Scale Up! 商业游戏
    • 参观Odoo
    获取软件
    • 下载
    • 版本对比
    • 发布
    合作
    • Github
    • 论坛
    • 近期活动
    • 翻译
    • 成为合作伙伴
    • 合作伙伴服务
    • 注册您的会计事务所
    获取服务
    • 寻找合作伙伴
    • 查找会计服务
    • 预约顾问咨询
    • 安装及推行服务
    • 客户参考
    • 支持
    • 升级
    Github Youtube Twitter Linkedin Instagram Facebook Spotify
    +1 (650) 691-3277
    获取演示
  • 定价
  • 技术支持

Odoo is the world's easiest all-in-one management software.
It includes hundreds of business apps:

  • 客户关系管理
  • e-Commerce
  • 会计
  • 库存
  • PoS
  • 项目
  • MRP
All apps
只限注册用戶才可与社群互动。
所有帖文 人 徽章
标签 (查看所有)
odoo accounting v14 pos v15
关于此论坛区
只限注册用戶才可与社群互动。
所有帖文 人 徽章
标签 (查看所有)
odoo accounting v14 pos v15
关于此论坛区
帮助

inheritance of PosModel in POS models.js file

订阅

此帖文有活动时,接收通知

此问题已终结
posinheritancejs
5 回复
34325 查看
形象
Harold

I made a template to facilitate POS inheritance, easy to use with the command scaffold. You can find it here https://github.com/haroldtamo/Odoo-Pos-Inheritance-Template

Sorry, I don't have enough karma to post it as answer, so i posted it here. The original question begin here :

Hi,

I want to override the PosModel in model.js (begin on line 17) by adding a new field in the fetch of products (product.product class) like this :

return self.fetch(
                        'product.product', 
                        ['name', 'field_to_add','list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code',
                         'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description'],
                        [['sale_ok','=',true],['available_in_pos','=',true]],
                        {pricelist: self.get('shop').pricelist_id[0]} // context for price
                    );

Unfortunately, i think i use a wrong method for inheritance : in fact, i copy all the class PosModel and i add my field in a new addons so, when the POS session is loaded, products are loaded twice : first when odoo call the original class and second when it call mine. The consequence is that the loading of the pos session is very slow. So i want to know the right method to override or inherit that class (PosModel)

Here is my code for inheritance (sorry i don't see the [CODE] tag ) (look the part in bold) :

function openerp_pos_price_control_models(instance, module){ var module = instance.point_of_sale; var QWeb = instance.web.qweb; _t = instance.web._t; var round_di = instance.web.round_decimals; var round_pr = instance.web.round_precision; module.PosModel = Backbone.Model.extend({ initialize: function(session, attributes) { Backbone.Model.prototype.initialize.call(this, attributes); var self = this; this.session = session; this.ready = $.Deferred(); // used to notify the GUI that the PosModel has loaded all resources this.flush_mutex = new $.Mutex(); // used to make sure the orders are sent to the server once at time this.barcode_reader = new module.BarcodeReader({'pos': this}); // used to read barcodes this.proxy = new module.ProxyDevice(); // used to communicate to the hardware devices via a local proxy this.db = new module.PosLS(); // a database used to store the products and categories this.db.clear('products','categories'); this.debug = jQuery.deparam(jQuery.param.querystring()).debug !== undefined; //debug mode // default attributes values. If null, it will be loaded below. this.set({ 'nbr_pending_operations': 0, 'currency': {symbol: '$', position: 'after'}, 'shop': null, 'company': null, 'user': null, // the user that loaded the pos 'user_list': null, // list of all users 'partner_list': null, // list of all partners with an ean 'cashier': null, // the logged cashier, if different from user 'orders': new module.OrderCollection(), //this is the product list as seen by the product list widgets, it will change based on the category filters 'products': new module.ProductCollection(), 'cashRegisters': null, 'bank_statements': null, 'taxes': null, 'pos_session': null, 'pos_config': null, 'units': null, 'units_by_id': null, 'pricelist': null, 'selectedOrder': null, }); this.get('orders').bind('remove', function(){ self.on_removed_order(); }); // We fetch the backend data on the server asynchronously. this is done only when the pos user interface is launched, // Any change on this data made on the server is thus not reflected on the point of sale until it is relaunched. // when all the data has loaded, we compute some stuff, and declare the Pos ready to be used. $.when(this.load_server_data()) .done(function(){ //self.log_loaded_data(); //Uncomment if you want to log the data to the console for easier debugging self.ready.resolve(); }).fail(function(){ //we failed to load some backend data, or the backend was badly configured. //the error messages will be displayed in PosWidget self.ready.reject(); }); }, // helper function to load data from the server fetch: function(model, fields, domain, ctx){ return new instance.web.Model(model).query(fields).filter(domain).context(ctx).all() }, // loads all the needed data on the sever. returns a deferred indicating when all the data has loaded. load_server_data: function(){ var self = this; var loaded = self.fetch('res.users',['name','company_id'],[['id','=',this.session.uid]]) .then(function(users){ self.set('user',users[0]); return self.fetch('res.company', [ 'currency_id', 'email', 'website', 'company_registry', 'vat', 'name', 'phone', 'partner_id', ], [['id','=',users[0].company_id[0]]]); }).then(function(companies){ self.set('company',companies[0]); return self.fetch('res.partner',['contact_address'],[['id','=',companies[0].partner_id[0]]]); }).then(function(company_partners){ self.get('company').contact_address = company_partners[0].contact_address; return self.fetch('product.uom', null, null); }).then(function(units){ self.set('units',units); var units_by_id = {}; for(var i = 0, len = units.length; i < len; i++){ units_by_id[units[i].id] = units[i]; } self.set('units_by_id',units_by_id); return self.fetch('product.packaging', null, null); }).then(function(packagings){ self.set('product.packaging',packagings); return self.fetch('res.users', ['name','ean13'], [['ean13', '!=', false]]); }).then(function(users){ self.set('user_list',users); return self.fetch('res.partner', ['name','ean13'], [['ean13', '!=', false]]); }).then(function(partners){ self.set('partner_list',partners); return self.fetch('account.tax', ['amount', 'price_include', 'type']); }).then(function(taxes){ self.set('taxes', taxes); return self.fetch( 'pos.session', ['id', 'journal_ids','name','user_id','config_id','start_at','stop_at'], [['state', '=', 'opened'], ['user_id', '=', self.session.uid]] ); }).then(function(sessions){ self.set('pos_session', sessions[0]); return self.fetch( 'pos.config', ['name','journal_ids','shop_id','journal_id', 'iface_self_checkout', 'iface_led', 'iface_cashdrawer', 'iface_payment_terminal', 'iface_electronic_scale', 'iface_barscan', 'iface_vkeyboard', 'iface_print_via_proxy','iface_cashdrawer','state','sequence_id','session_ids'], [['id','=', self.get('pos_session').config_id[0]]] ); }).then(function(configs){ var pos_config = configs[0]; self.set('pos_config', pos_config); self.iface_electronic_scale = !!pos_config.iface_electronic_scale; self.iface_print_via_proxy = !!pos_config.iface_print_via_proxy; self.iface_vkeyboard = !!pos_config.iface_vkeyboard; self.iface_self_checkout = !!pos_config.iface_self_checkout; self.iface_cashdrawer = !!pos_config.iface_cashdrawer; return self.fetch('sale.shop',[],[['id','=',pos_config.shop_id[0]]]); }).then(function(shops){ self.set('shop',shops[0]); return self.fetch('product.pricelist',['currency_id'],[['id','=',self.get('shop').pricelist_id[0]]]); }).then(function(pricelists){ self.set('pricelist',pricelists[0]); return self.fetch('res.currency',['symbol','position','rounding','accuracy'],[['id','=',self.get('pricelist').currency_id[0]]]); }).then(function(currencies){ self.set('currency',currencies[0]); return self.fetch('product.packaging',['ean','product_id']); }).then(function(packagings){ self.db.add_packagings(packagings); return self.fetch('pos.category', ['id','name','parent_id','child_id','image']) }).then(function(categories){ self.db.add_categories(categories); return self.fetch( 'product.product', ['name', 'min_sale_price', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code', 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description'], [['sale_ok','=',true],['available_in_pos','=',true]], {pricelist: self.get('shop').pricelist_id[0]} // context for price ); }).then(function(products){ self.db.add_products(products); return self.fetch( 'account.bank.statement', ['account_id','currency','journal_id','state','name','user_id','pos_session_id'], [['state','=','open'],['pos_session_id', '=', self.get('pos_session').id]] ); }).then(function(bank_statements){ var journals = new Array(); _.each(bank_statements,function(statement) { journals.push(statement.journal_id[0]) }); self.set('bank_statements', bank_statements); return self.fetch('account.journal', undefined, [['id','in', journals]]); }).then(function(journals){ self.set('journals',journals); // associate the bank statements with their journals. var bank_statements = self.get('bank_statements'); for(var i = 0, ilen = bank_statements.length; i < ilen; i++){ for(var j = 0, jlen = journals.length; j < jlen; j++){ if(bank_statements[i].journal_id[0] === journals[j].id){ bank_statements[i].journal = journals[j]; bank_statements[i].self_checkout_payment_method = journals[j].self_checkout_payment_method; } } } self.set({'cashRegisters' : new module.CashRegisterCollection(self.get('bank_statements'))}); }); return loaded; }, // logs the usefull posmodel data to the console for debug purposes log_loaded_data: function(){ console.log('PosModel data has been loaded:'); console.log('PosModel: units:',this.get('units')); console.log('PosModel: bank_statements:',this.get('bank_statements')); console.log('PosModel: journals:',this.get('journals')); console.log('PosModel: taxes:',this.get('taxes')); console.log('PosModel: pos_session:',this.get('pos_session')); console.log('PosModel: pos_config:',this.get('pos_config')); console.log('PosModel: cashRegisters:',this.get('cashRegisters')); console.log('PosModel: shop:',this.get('shop')); console.log('PosModel: company:',this.get('company')); console.log('PosModel: currency:',this.get('currency')); console.log('PosModel: user_list:',this.get('user_list')); console.log('PosModel: user:',this.get('user')); console.log('PosModel.session:',this.session); console.log('PosModel end of data log.'); }, // this is called when an order is removed from the order collection. It ensures that there is always an existing // order and a valid selected order on_removed_order: function(removed_order){ if( this.get('orders').isEmpty()){ this.add_new_order(); }else{ this.set({ selectedOrder: this.get('orders').last() }); } }, // saves the order locally and try to send it to the backend. 'record' is a bizzarely defined JSON version of the Order push_order: function(record) { this.db.add_order(record); this.flush(); }, //creates a new empty order and sets it as the current order add_new_order: function(){ var order = new module.Order({pos:this}); this.get('orders').add(order); this.set('selectedOrder', order); }, // attemps to send all pending orders ( stored in the pos_db ) to the server, // and remove the successfully sent ones from the db once // it has been confirmed that they have been sent correctly. flush: function() { //TODO make the mutex work //this makes sure only one _int_flush is called at the same time /* return this.flush_mutex.exec(_.bind(function() { return this._flush(0); }, this)); */ this._flush(0); }, // attempts to send an order of index 'index' in the list of order to send. The index // is used to skip orders that failed. do not call this method outside the mutex provided // by flush() _flush: function(index){ var self = this; var orders = this.db.get_orders(); self.set('nbr_pending_operations',orders.length); var order = orders[index]; if(!order){ return; } //try to push an order to the server // shadow : true is to prevent a spinner to appear in case of timeout (new instance.web.Model('pos.order')).call('create_from_ui',[[order]],undefined,{ shadow:true }) .fail(function(unused, event){ //don't show error popup if it fails event.preventDefault(); console.error('Failed to send order:',order); self._flush(index+1); }) .done(function(){ //remove from db if success self.db.remove_order(order.id); self._flush(index); }); }, scan_product: function(parsed_ean){ var self = this; var product = this.db.get_product_by_ean13(parsed_ean.base_ean); var selectedOrder = this.get('selectedOrder'); if(!product){ return false; } if(parsed_ean.type === 'price'){ selectedOrder.addProduct(new module.Product(product), {price:parsed_ean.value}); }else if(parsed_ean.type === 'weight'){ selectedOrder.addProduct(new module.Product(product), {quantity:parsed_ean.value, merge:false}); }else{ selectedOrder.addProduct(new module.Product(product)); } return true; }, }); }

thanks!

2
形象
丢弃
形象
Lithin T
最佳答案

Here is the easy way to inherit PosModel to load new fields quick and fast

function pos_product_avialble(instance,module){ //where module=instance.point_of_sale
var models = module.PosModel.prototype.models;
for(var i=0; i<models.length; i++){
var model=models[i];
if(model.model === 'product.product'){
model.fields.push('new_field1','new_field2');
}
}
}

5
形象
丢弃
形象
Ivan Elizaryev
最佳答案

I think, right way is extend load_server_data function and then update product list by adding new fields:

function pos_product_available(instance, module){

    var PosModelSuper = module.PosModel
    module.PosModel = module.PosModel.extend({
        load_server_data: function(){
            var self = this;
            var loaded = PosModelSuper.prototype.load_server_data.call(this);

            loaded = loaded.then(function(){
                return self.fetch(
                    'product.product',
                    ['qty_available'], //new field
                    [['sale_ok','=',true],['available_in_pos','=',true]],
                    {}
                );

            }).then(function(products){
                $.each(products, function(){
                    $.extend(self.db.get_product_by_id(this.id) || {}, this)
                });
                return $.when()
            })
            return loaded;
        },
    })
}

(function(){
    var _super = window.openerp.point_of_sale;
    window.openerp.point_of_sale = function(instance){
        _super(instance);
        var module = instance.point_of_sale;

        pos_product_available(instance, module);

    }
})()

 

From here: https://github.com/yelizariev/pos-addons/blob/8.0/pos_product_available/static/src/js/pos.js

3
形象
丢弃
Harold
编写者

Thanks for your answer

Nirav Jani

Is it working properly ?

形象
EasyPME
最佳答案

Lithin T 's answer works

In Odoo v9 it is : 

odoo.define('pos_product_available', function (require) {

"use strict";

var module = require('point_of_sale.models');

var models = module.PosModel.prototype.models;

for(var i=0; i<models.length; i++){

var model=models[i];

if(model.model === 'product.product'){

model.fields.push('new_field1','new_field2');

}

}

});

0
形象
丢弃
Peter Alabaster

This works in Odoo 10 too. Thanks

喜欢讨论吗?不要只阅读,加入进来!

立即创建账户,享受专属功能,与我们的精彩社区互动!

注册
相关帖文 回复 查看 活动
Issue Migrating bus_service Notification Handling from Odoo 17 to Odoo 18 (OWL-based POS)
pos js
形象
0
4月 25
2959
How i can modify JS method in POS? 已解决
pos js
形象
1
9月 23
8468
How to override js function in POS Orderline object? 已解决
pos js
形象
形象
2
2月 18
11707
inherit js file odoo 9
inheritance js
形象
形象
2
7月 17
5370
pos error js instance.web.cordova
pos js
形象
0
6月 17
4732
社区
  • 教学视频
  • 文档
  • 论坛
开源
  • 下载
  • Github
  • Runbot
  • 翻译
服务
  • Odoo.sh 托管
  • 支持
  • 升级
  • 自定义开发服务
  • 教育
  • 查找会计服务
  • 寻找合作伙伴
  • 成为合作伙伴
关于我们
  • 我们的公司
  • 品牌资产
  • 联系我们
  • 招聘
  • 近期活动
  • 播客
  • 博客
  • 客户
  • 法律 • 隐私
  • 安全
الْعَرَبيّة Català 简体中文 繁體中文 (台灣) Čeština Dansk Nederlands English Suomi Français Deutsch हिंदी Bahasa Indonesia Italiano 日本語 한국어 (KR) Lietuvių kalba Język polski Português (BR) română русский язык Slovenský jazyk slovenščina Español (América Latina) Español ภาษาไทย Türkçe українська Tiếng Việt

Odoo致力于为企业管理提供高效智能的开源解决方案,是全球业内高速成长的软件服务商之一,逾七百五十万用户选择Odoo进行数字化升级。通过一系列全业务链覆盖、高度集成、简单易用的商业应用,助力企业实现信息化改革、降本增效并释放公司增长潜力。

Odoo独特的价值在于是一款非常容易使用又完全集成的应用。

Website made with

Odoo Experience on YouTube

1. Use the live chat to ask your questions.
2. The operator answers within a few minutes.

Live support on Youtube
Watch now