Develop Point of Sale App¶
Qua các phần trước, chúng ta đã khám phá hai cơ sở code khác nhau. Đầu tiên là code phía phần backend, nó dùng để tạo ra các views, actions, menus,... Thứ hai là code phía phần frontend, nó dùng để tạo ra các web page, controller, snippets,... Ở phần này, chúng ta sẽ khám phá cơ sở code thứ ba, được sử dụng cho ứng dụng Điểm bán hàng. Bạn có thể thắc mắc tại sao ứng dụng Điểm bán hàng lại cần một cơ sở code khác. Điều này là do nó sử dụng một kiến trúc khác, để hoạt động ngoại tuyến. Đồng thời, ta sẽ xem cách sửa đổi ứng dụng Điểm bán hàng.
Các mục chính trong bài viết này:
Ghi chú
Ứng dụng Điểm bán hàng chủ yếu được viết bằng JavaScript. Bài viết này hướng dẫn viết với giả định rằng bạn đã có kiến thức cơ bản về JavaScript. Bên cạnh đó cũng sử dụng OWL framework, vì vậy nếu bạn không biết về các thuật ngữ JavaScript này, xem The Odoo Web Library (OWL).
Trong suốt phần này, chúng ta sẽ sử dụng một Module bổ trợ có tên là viin_pos
.
Module viin_pos
sẽ phụ thuộc vào point_of_sale
vì chúng ta sẽ đi
tùy chỉnh ứng dụng Điểm bán hàng.
Thêm tệp JavaScript/SCSS tùy chỉnh¶
Ứng dụng Điểm bán hàng sử dụng các gói Assets khác nhau để quản lý các tệp JavaScript và style sheet. Trong công thức này, chúng ta sẽ tìm hiểu cách thêm các tệp SCSS và JavaScript vào Asset Bundles của Điểm bán hàng.
Sẵn sàng¶
Trong phần này, chúng ta sẽ tải SCSS style sheet và tệp JavaScript vào Ứng dụng Điểm bán hàng
Các bước thực hiện¶
Để tải nội dung trong ứng dụng Điểm bán hàng, hãy làm theo các bước sau:
Thêm tệp SCSS mới tại /viin_pos/static/src/scss/viin_pos.scss và chèn mã sau:
.pos .pos-content {
.price-tag {
background-color: black;
}
}
Thêm tệp JavaScript mới tại /viin_pos/static/src/js/viin_pos.js và chèn mã sau:
console.log('Viindoo Pos');
Đăng ký các tệp JavaScript và SCSS này vào point_of_sale assets:
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<script type="text/javascript" src="/viin_pos/static/src/js/viin_pos.js"></script>
<link rel="stylesheet" href="/viin_pos/static/src/scss/viin_pos.scss"/>
</xpath>
</template>
</odoo>
Cài đặt Module viin_pos
. Để xem các thay đổi của bạn trong hành động, hãy bắt đầu phiên mới từ Menu
Điểm bán hàng | Bảng điều khiển.
Cơ chế hoạt động¶
Trong công thức này, chúng ta đã tải một tệp JavaScript và một tệp SCSS vào
Ứng dụng Điểm bán hàng. Trong bước 1, chúng ta đã thay đổi màu nền của
nhãn giá của thẻ sản phẩm. Sau khi cài đặt Module viin_pos
, bạn sẽ có thể
để xem các thay đổi đối với nhãn giá:
Trong bước 2, chúng ta đã thêm tệp JavaScript. Trong đó, chúng ta đã thêm log vào bảng điều khiển. Để mà
xem thông báo, bạn sẽ cần mở các công cụ dành cho nhà phát triển của trình duyệt. Trong tab Bảng điều khiển,
hiển thị ra log sau Viindoo Pos
. Điều này cho thấy rằng tệp JavaScript của bạn đã được tải thành công.
Hiện tại, chúng ta mới chỉ thêm đoạn log vào tệp JavaScript, nhưng trong các phần sắp tới, chúng ta
sẽ thêm nhiều hơn vào nó:
Trong bước 3, chúng ta thêm tệp JavaScript và tệp SCSS vào nội dung Điểm bán hàng. Các
External ID của assets Điểm bán hàng là point_of_sale.assets
. Ở đây, chỉ có
External ID là khác nhau; mọi thứ khác hoạt động như các assets thông thường. Nếu bạn không biết assets
hoạt động thế nào trong Odoo, hãy tham khảo phần quản lý Static-assets trong CMS
Phát triển trang web.
Thêm nút tác vụ trên bàn phím¶
Như chúng ta đã thảo luận trong phần trước, ứng dụng Điểm bán hàng được thiết kế theo cách cách hoạt động ngoại tuyến. Nhờ đó, cấu trúc mã của ứng dụng Điểm bán hàng khác với các ứng dụng Odoo còn lại. Cơ sở mã của ứng dụng Điểm bán hàng phần lớn được viết bằng JavaScript và cung cấp các tiện ích khác nhau để tùy chỉnh. Trong phần này, chúng ta sẽ sử dụng một tiện ích như vậy và tạo một nút tác vụ ở đầu bàn phím bảng điều khiển.
Sẵn sàng¶
Trong phần này, chúng ta sẽ sử dụng Module viin_pos
được tạo trong phần Thêm tệp JavaScript/SCSS tùy chỉnh.
Chúng ta sẽ thêm một nút ở đầu bảng điều khiển bàn phím. Điều này
nút sẽ là một phím tắt để áp dụng chiết khấu cho các dòng đơn hàng.
Các bước thực hiện¶
Làm theo các bước sau để thêm nút hành động 10% Discount
vào bảng điều khiển bàn phím cho ứng dụng điểm bán hàng:
Thêm tệp đoạn code sau vào tệp /static/src/js/viin_pos.js, nó sẽ định nghĩa ra nút hành động:
odoo.define('viin_pos.discountbutton', function(require) {"use strict";
const PosComponent = require('point_of_sale.PosComponent');
const ProductScreen = require('point_of_sale.ProductScreen');
const Registries = require('point_of_sale.Registries');
class ViinPosDiscountButton extends PosComponent {
async _setDIscount() {
const order = this.env.pos.get_order();
order.selected_orderline.set_discount(10);
}
}
ViinPosDiscountButton.template = 'ViinPosDiscountButton';
ProductScreen.addControlButton({
component: ViinPosDiscountButton,
condition: function() {
return true;
},
});
Registries.Component.add(ViinPosDiscountButton);
return ViinPosDiscountButton;
});
Thêm mẫu QWeb cho nút trong /static/src/xml/viin_pos.xml:
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="ViinPosDiscountButton" owl="1">
<span class="control-button" t-on-click="_setDIscount">
<span>10% Discount</span>
</span>
</t>
</templates>
Đăng ký mẫu QWeb trong tệp manifest như sau:
'qweb': [
'static/src/xml/viin_pos.xml'
]
Cập nhật module
viin_pos
để áp dụng các thay đổi. Sau đó, bạn sẽ có thể xem nút10% Discount
phía trên bàn phím:
Sau khi nhấp vào đây, chiết khấu sẽ được áp dụng cho dòng đặt hàng đã chọn.
Cơ chế hoạt động¶
Ở Odoo v14, code của ứng dụng điểm bán hàng được viết lại hoàn toàn bằng cách sử dụng khung OWL. Bạn có thể tìm hiểu thêm về khung OWL trong Thư viện Web Odoo (OWL).
Để tạo nút hành động trong ứng dụng Điểm bán hàng, bạn sẽ cần mở rộng
PosComponent. Bây giờ, PosComponent được định nghĩa trong point_of_sale.
Không gian tên PosComponent, Để sử dụng nó trong code của bạn, bạn sẽ cần thêm nó. Trong
bước 1, chúng ta đã nhập vào màn hình với require('point_of_sale.PosComponent')
.
Sau đó, chúng ta tạo PosDiscountButton bằng cách mở rộng PosComponent. Nếu bạn muốn
tìm hiểu cơ chế require
hoạt động trong Odoo JavaScript, tham khảo Thêm CSS
và JavaScript cho một website trong Phát triển Trang web CMS. Trong bước 1,
chúng ta cũng đã nhập point_of_sale.ProductScreen
và point_of_sale.Registries
.
Bây giờ, point_of_sale.ProductScreen
được sử dụng để thêm một nút vào
màn hình Điểm bán hàng thông qua phương thức addControlButton
. Cuối cùng, chúng ta đã thêm
một nút được đăng ký tới point_of_sale.Registries
, là đăng ký toàn cục
chứa tất cả các thành phần OWL.
PosComponent có một số tiện ích tích hợp cho phép truy cập thông tin hữu ích như
chi tiết đơn đặt hàng, cấu hình Điểm bán hàng và hơn thế nữa. Bạn có thể truy cập nó qua biến this.env
.
Trong ví dụ của chúng ta, chúng ta đã truy cập thông tin đặt hàng hiện tại thông qua cái này.
phương thức env.pos.get_order()
. Sau đó, chúng ta sử dụng phương thức set_discount()
để thiết lập
chiết khấu 10%.
Trong bước 2 và bước 3, chúng ta đã thêm mẫu OWL, mẫu này sẽ được hiển thị trên bàn phím ứng dụng Điểm Bán. Nếu bạn muốn tìm hiểu thêm về điều này, vui lòng tham khảo The Odoo Thư viện Web (OWL).
Mở rộng¶
Phương thức addControlButton()
hỗ trợ một đối số nữa, đó là
condition
. Đối số này được sử dụng để ẩn/hiện nút dựa trên một số điều kiện.
Giá trị của đối số này là một hàm trả về Boolean
. Dựa trên giá trị trả lại,
hệ thống Điểm bán hàng sẽ ẩn hoặc hiện nút. Hãy xem ví dụ sau đây:
ProductScreen.addControlButton({
component: ViinPosDiscountButton,
condition: function() {
return this.env.pos.config.module_pos_discount;
},
});
Nút giảm giá theo điều kiện chỉ được hiển thị nếu chiết khấu được cho phép từ Cấu hình điểm bán hàng.
Thực hiện gọi RPC¶
Mặc dù ứng dụng Điểm bán hàng hoạt động ngoại tuyến, vẫn có thể thực hiện các cuộc gọi RPC đến máy chủ. Cuộc gọi RPC có thể được sử dụng cho bất kỳ hoạt động nào; bạn có thể sử dụng nó cho CRUD hoặc để thực hiện một hành động trên máy chủ. Trong phần này, chúng ta sẽ thực hiện một cuộc gọi RPC để lấy thông tin về hai đơn hóa đơn gần đây nhất của khách hàng.
Sẵn sàng¶
Trong phần này, chúng ta sẽ sử dụng module viin_pos
được tạo trong phần Thêm nút tác vụ trên bàn phím.
chúng ta sẽ xác định nút hành động. Khi người dùng nhấp vào nút hành động,
chúng ta sẽ thực hiện một cuộc gọi RPC để tìm nạp thông tin đơn hóa đơn và hiển thị nó trên cửa sổ bật lên.
Các bước thực hiện¶
Thực hiện theo các bước sau để hiển thị hai hóa đơn cuối cùng của khách hàng:
Thêm đoạn code sau vào tệp
/static/src/js/viin_pos.js;
điều này sẽ thêm một nút hành động mới để tìm nạp và hiển thị thông tin về hai hóa đơn khi người dùng nhấp vào nút:
class PostLastInvoice extends PosComponent {
// Đặt bước 2 ở đây
}
PostLastInvoice.template = 'PostLastInvoice';
ProductScreen.addControlButton({
component: PostLastInvoice,
condition: function () {
return true;
},
});
Registries.Component.add(PostLastInvoice);
Thêm hàm
_showLastInvoice
vào nút thành phầnPostLastInvoice
để quản lý nút nhấp chuột:
_showLastInvoice() {
var self = this;
const order = this.env.pos.get_order();
if (order.attributes.client) {
var domain = [
['partner_id', '=', order.attributes.client.id]
];
this.rpc({
model: 'account.move',
method: 'search_read',
args: [domain, ['name', 'amount_total']],
kwargs: {
limit: 2
},
}).then(function (moves) {
if (moves.length > 0) {
var order_list = _.map(moves, function (o) {
return {
'label': _.str.sprintf("%s -AMOUNT: %s", o.name, o.amount_total)
};
});
self.showPopup('SelectionPopup', {
title: 'Last 2 invoices',
list: order_list
});
} else {
self.showPopup('ErrorPopup', {
body: 'No invoices found'
});
}
});
} else {
self.showPopup('ErrorPopup', {
body: 'You must select the customer'
});
}
}
Thêm mẫu QWeb cho nút vào tệp
/static/src/xml/viin_pos.xml
:
<t t-name="PostLastInvoice" owl="1">
<span class="control-button" t-on-click="_showLastInvoice">
<span>Last Invoices</span>
</span>
</t>
Cập nhật module
viin_pos
để áp dụng các thay đổi. Sau đó, bạn sẽ có thể xem nút Last Invoices phía trên bảng điều khiển bàn phím. Khi nút này được nhấp, cửa sổ bật lên sẽ được hiển thị với thông tin hóa đơn:
Nếu không tìm thấy hóa đơn nào trước đó, cảnh báo sẽ được hiển thị.
Cơ chế hoạt động¶
Ở bước 1, chúng ta đã tạo và đăng ký nút hành động. Nếu bạn muốn tìm hiểu thêm về nút hành động, hãy tham khảo phần Thêm nút tác vụ trên bàn phím của phần này. Trước khi đi vào chi tiết kỹ thuật, hãy hiểu những gì chúng ta muốn đạt được với nút hành động này. Sau khi nhấp vào, chúng ta muốn hiển thị thông tin cho hai hóa đơn cho khách hàng đã chọn. Sẽ có một vài trường hợp khách hàng không chọn, hoặc khách hàng không có hóa đơn nào trước đó. Trong những trường hợp như vậy, ta sẽ hiển thị một cửa sổ bật lên với một thông điệp thích hợp.
Tiện ích RPC có sẵn với thuộc tính this.rpc
của component
. Trong bước 2,
chúng ta đã thêm chức năng xử lý nhấp chuột. Khi nhấp vào nút hành động, trình xử lý nhấp chuột
hàm sẽ được gọi. Hàm này sẽ làm cho RPC gọi vào máy chủ để tìm nạp đơn thông tin đặt hàng.
Chúng ta đã sử dụng phương thức rpc()
để thực hiện các cuộc gọi RPC. Sau đây là danh sách các
các đối số bạn có thể truyền trong phương thức rpc()
:
model
: Tên củamodel
mà bạn muốn thực hiện thao tác trên đó.method
: Tên của phương thức bạn muốn gọi.args
: Danh sách các đối số bắt buộc được phương thức chấp nhận.kwargs
: Một dictionary của các đối số tùy chọn được phương thức chấp nhận.
Trong phần này, chúng ta sử dụng phương thức search_read
để tìm nạp dữ liệu thông qua RPC. Chúng ta dùng
domain khách hàng để lọc các hóa đơn. Chúng ta cũng dùng đối số limit
để lấy xuống chỉ hai hóa đơn. rpc.query()
là một phương thức không đồng bộ và trả về
đối tượng Promise, để xử lý kết quả, bạn sẽ cần sử dụng phương thức then()
, hoặc bạn
có thể sử dụng từ khóa await
.
Ghi chú
Gọi RPC không hoạt động ở chế độ ngoại tuyến. Nếu mạng internet của bạn kết nối tốt và bạn không sử dụng chế độ ngoại tuyến thường xuyên, bạn có thể sử dụng RPC. Mặc dù ứng dụng Điểm bán hàng hoạt động ngoại tuyến, nhưng một vài thao tác, chẳng hạn như tạo hoặc cập nhật khách hàng yêu cầu phải kết nối internet thì cần sử dụng RPC để gọi nội bộ.
Chúng ta đã hiển thị thông tin đặt hàng trước đó trong cửa sổ bật lên. Chúng ta đã dùng
SelectionPopup
, nó được sử dụng để hiển thị danh sách có thể chọn; chúng ta sử dụng nó để hiển thị
hai hóa đơn cuối cùng. Chúng ta cũng đã dùng ErrorPopup
để hiển thị thông báo cảnh báo khi
khách hàng không được chọn hoặc không có hóa đơn trước đó được tìm thấy.
Trong bước 3, chúng ta đã thêm mẫu QWeb cho nút hành động. Ứng dụng Điểm bán hàng sẽ kết xuất mẫu này để hiển thị nút hành động.
Mở rộng¶
Có rất nhiều tiện ích Popup
khác. Ví dụ: NumberPopup
được sử dụng để lấy
nhập số từ người dùng. Tham khảo các tệp trong thư mục addons/point_of_sale/static/src/xml/Popups
để xem tất cả các tiện ích này.
Sửa đổi UI màn hình Điểm bán hàng¶
Giao diện người dùng của ứng dụng Điểm bán hàng được viết bằng mẫu OWL QWeb. Trong phần này, chúng ta sẽ tìm hiểu cách bạn có thể sửa đổi các phần tử UI trong ứng dụng Điểm bán hàng.
Sẵn sàng¶
Trong phần này, chúng ta sẽ sử dụng module viin_pos
được tạo trong Thực hiện gọi RPC.
Chúng ta sẽ sửa đổi UI, hiển thị thêm mã sản phẩm.
Các bước thực hiện¶
Làm theo các bước sau để hiển thị thêm mã sản phẩm trên thẻ sản phẩm:
Thêm đoạn code sau vào tệp /static/src/js/viin_pos.js để tìm nạp trường bổ sung trường mã sản phẩm của sản phẩm:
const pos_model = require('point_of_sale.models');
pos_model.load_fields("product.product", ["default_code"]);
Thêm đoạn code sau vào /static/src/xml/viin_pos.xml để hiển thị mã sản phẩm:
<t t-name="ProductItem" t-inherit="point_of_sale.ProductItem" t-inherit-mode="extension" owl="1">
<xpath expr="//div[hasclass('product-name')]" position="after">
<span t-if="props.product.default_code" class="default_code_product">
<t t-esc="props.product.default_code"/>
</span>
</xpath>
</t>
Thêm style sheet sau để tạo kiểu cho mã sản phẩ:
.default_code_product {
top: 2px;
line-height: 15px;
left: 2px;
background: pink;
position: absolute;
}
Cập nhật module viin_pos
để áp dụng các thay đổi:
Sửa đổi logic nghiệp vụ¶
Trong phần này, chúng ta sẽ tìm hiểu làm thế nào để mở rộng hoặc thay đổi logic nghiệp vụ hiện tại.
Sẵn sàng¶
Chúng ta sẽ hiển thị cảnh báo cho người dùng nếu họ bán số lượng sản phẩm nhiều hơn 5
Các bước thực hiện¶
Chúng ta sẽ thêm code JavaScript vào /static/src/js/viin_pos.js
để hiển thị cảnh báo cho người dùng nếu
bán nhiều hơn số lượng 5
const UpdatedProductScreen = ProductScreen =>
class extends ProductScreen {
_setValue(val) {
super._setValue(val);
const orderline = this.env.pos.get_order().selected_orderline;
if (orderline && orderline.quantity >= 5) {
this.showPopup('ErrorPopup', {
title:
'Warning', body: 'Product quantity must be less than 5.'
});
}
}
};
Registries.Component.extend(ProductScreen, UpdatedProductScreen);
Nâng cấp module viin_pos
để áp dụng các thay đổi. Sau khi nâng cấp,
bạn khổng thể bán số lượng sản phẩm bằng hoặc lớn hơn 5. Một cảnh báo sẽ hiện ra nếu bạn cố
chọn lớn hơn.
Cơ chế hoạt động¶
Ứng dụng Điểm bán hàng cung cấp một số phương thưc extend
để thực hiện các thay đổi đối với
chức năng hiện có.
Trong ví dụ, chúng ta đã sửa đổi phương thức _setValue()
. phương thức _setValue ()
của ProductScreen
được gọi bất cứ khi nào người dùng thực hiện thay đổi đối với đơn đặt hàng.
chúng ta định nghĩa một phương thức _setValue()
mới và gọi phương thức super
; điều này
sẽ đảm bảo rằng bất kỳ hành động nào mà người dùng thực hiện đều được áp dụng. Sau khi gọi phương thức super
,
chúng ta viết logic nghiệp vụ của mình.
Ghi chú
Sử dụng super
có thể phá hỏng luồng nếu không được sử dụng cẩn thận. Nếu phương thức được
kế thừa từ một số tệp, bạn phải gọi phương thức super
; nếu không, nó
sẽ bỏ qua logic trong kế thừa tiếp theo. Điều này đôi khi dẫn đến
trạng thái dữ liệu nội bộ bị hỏng.
Sửa đổi biên lai của khách hàng¶
Khi bạn tùy chỉnh ứng dụng Điểm bán hàng, một yêu cầu phổ biến mà bạn nhận được từ khách hàng là sửa đổi biên lai của khách hàng. Trong phần này, bạn sẽ học cách sửa đổi phiếu thu của khách hàng.
Sẵn sàng¶
Chúng ta sẽ thêm một dòng để cho người dùng biết họ có bao nhiêu dòng đơn hàng
Các bước thực hiện¶
Thêm đoạn code sau vào tệp /static/src/js/viin_pos.js. Điều này sẽ thêm dữ liệu bổ sung trong môi trường biên nhận:
var models = require('point_of_sale.models');
var _super_order = models.Order.prototype;
models.Order = models.Order.extend({
export_for_printing: function () {
var result = _super_order.export_for_printing.apply(this, arguments);
const order = this.pos.get_order();
result.line_count = order.orderlines.length;
return result;
},
});
Thêm đoạn code sau vào /static/src/xml/viin_pos.xml. Nó sẽ mở rộng mẫu biên lai mặc định:
<t t-name="OrderReceipt" t-inherit="point_of_sale.OrderReceipt" t-inherit-mode="extension" owl="1">
<xpath expr="//div[hasclass('before-footer')]" position="before">
<div style="text-align:center;">
<div t-if="receipt.line_count">You have <t t-esc="receipt.line_count"/> lines on this order.</div>
</div>
</xpath>
</t>
Nâng cấp module viin_pos
để xem các thay đổi