default
4 years ago
126 changed files with 11801 additions and 0 deletions
-
24hw_customer_display/__init__.py
-
86hw_customer_display/__manifest__.py
-
24hw_customer_display/controllers/__init__.py
-
192hw_customer_display/controllers/main.py
-
14hw_customer_display/i18n/hw_customer_display.pot
-
80hw_customer_display/test-scripts/customer-display-test.py
-
4hw_customer_display_currency/__init__.py
-
19hw_customer_display_currency/__manifest__.py
-
2hw_customer_display_currency/controllers/__init__.py
-
133hw_customer_display_currency/controllers/main.py
-
102hw_customer_display_currency/test-scripts/customer_display_currency_test.py
-
81pos_container/README.rst
-
2pos_container/__init__.py
-
36pos_container/__manifest__.py
-
20pos_container/data/product.xml
-
19pos_container/demo/demo.xml
-
384pos_container/i18n/fr.po
-
377pos_container/i18n/pos_container.pot
-
3pos_container/models/__init__.py
-
13pos_container/models/barcode.py
-
45pos_container/models/container.py
-
15pos_container/models/pos_order_line.py
-
2pos_container/readme/CONTRIBUTORS.rst
-
5pos_container/readme/DESCRIPTION.rst
-
2pos_container/readme/USAGE.rst
-
3pos_container/security/ir.model.access.csv
-
431pos_container/static/description/index.html
-
191pos_container/static/src/css/container.css
-
431pos_container/static/src/js/container.js
-
619pos_container/static/src/js/models_and_db.js
-
244pos_container/static/src/js/tests.js
-
442pos_container/static/src/xml/pos.xml
-
17pos_container/templates/templates.xml
-
1pos_container/tests/__init__.py
-
14pos_container/tests/test_pos_container.py
-
43pos_container/views/container.xml
-
3pos_customer_display_currency/__init__.py
-
33pos_customer_display_currency/__manifest__.py
-
3pos_customer_display_currency/models/__init__.py
-
30pos_customer_display_currency/models/pos_customer_display_currency.py
-
310pos_customer_display_currency/static/src/js/pos_customer_display_currency.js
-
10pos_customer_display_currency/templates/templates.xml
-
17pos_customer_display_currency/views/pos_config_view.xml
-
1pos_hash_cert/__init__.py
-
14pos_hash_cert/__manifest__.py
-
1pos_hash_cert/models/__init__.py
-
63pos_hash_cert/models/pos_hash_cert.py
-
15pos_hash_cert/views/ir_module.xml
-
105pos_loyalty/README.rst
-
3pos_loyalty/__init__.py
-
36pos_loyalty/__manifest__.py
-
8pos_loyalty/demo/templates.xml
-
523pos_loyalty/i18n/es.po
-
504pos_loyalty/i18n/fr.po
-
498pos_loyalty/i18n/hr_HR.po
-
497pos_loyalty/i18n/it.po
-
498pos_loyalty/i18n/nl_NL.po
-
483pos_loyalty/i18n/pos_loyalty.pot
-
9pos_loyalty/models/__init__.py
-
30pos_loyalty/models/loyalty_program.py
-
73pos_loyalty/models/loyalty_reward.py
-
34pos_loyalty/models/loyalty_rule.py
-
14pos_loyalty/models/pos_config.py
-
30pos_loyalty/models/pos_order.py
-
20pos_loyalty/models/pos_order_line.py
-
13pos_loyalty/models/res_partner.py
-
4pos_loyalty/readme/CONFIGURE.rst
-
3pos_loyalty/readme/CONTRIBUTORS.rst
-
6pos_loyalty/readme/DESCRIPTION.rst
-
11pos_loyalty/readme/USAGE.rst
-
7pos_loyalty/security/ir.model.access.csv
-
BINpos_loyalty/static/description/icon.png
-
449pos_loyalty/static/description/index.html
-
21pos_loyalty/static/src/css/pos.css
-
477pos_loyalty/static/src/js/pos.js
-
170pos_loyalty/static/src/js/tests.js
-
85pos_loyalty/static/src/xml/pos.xml
-
2pos_loyalty/tests/__init__.py
-
74pos_loyalty/tests/test_pos_loyalty.py
-
83pos_loyalty/views/loyalty_program_view.xml
-
37pos_loyalty/views/loyalty_reward_view.xml
-
31pos_loyalty/views/loyalty_rule_view.xml
-
23pos_loyalty/views/pos_config_view.xml
-
15pos_loyalty/views/pos_order_view.xml
-
18pos_loyalty/views/res_partner_view.xml
-
9pos_loyalty/views/templates.xml
-
86pos_mail_receipt/README.rst
-
1pos_mail_receipt/__init__.py
-
14pos_mail_receipt/__manifest__.py
-
19pos_mail_receipt/data/email.xml
-
131pos_mail_receipt/i18n/fr.po
-
122pos_mail_receipt/i18n/pos_mail_receipt.pot
-
1pos_mail_receipt/models/__init__.py
-
86pos_mail_receipt/models/pos_order.py
-
3pos_mail_receipt/readme/CONTRIBUTORS.rst
-
5pos_mail_receipt/readme/DESCRIPTION.rst
-
3pos_mail_receipt/readme/USAGE.rst
-
BINpos_mail_receipt/static/description/icon.png
-
434pos_mail_receipt/static/description/index.html
-
3pos_mail_receipt/static/src/css/pos.css
@ -0,0 +1,24 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Hardware Customer Display module for Odoo |
|||
# Copyright (C) 2014 Akretion (http://www.akretion.com) |
|||
# @author Alexis de Lattre <alexis.delattre@akretion.com> |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
|
|||
from . import controllers |
@ -0,0 +1,86 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Hardware Customer Display module for Odoo |
|||
# Copyright (C) 2014 Akretion (http://www.akretion.com) |
|||
# @author Alexis de Lattre <alexis.delattre@akretion.com> |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
|
|||
{ |
|||
'name': 'Hardware Customer Display', |
|||
'version': '12.0.0.1.0', |
|||
'category': 'Hardware Drivers', |
|||
'license': 'AGPL-3', |
|||
'summary': 'Adds support for Customer Display in the Point of Sale', |
|||
'description': """ |
|||
Hardware Customer Display |
|||
========================= |
|||
|
|||
This module adds support for Customer Display in the Point of Sale. |
|||
This module is designed to be installed on the *POSbox* (i.e. the |
|||
proxy on which the USB devices are connected) and not on the main |
|||
Odoo server. On the main Odoo server, you should install the module |
|||
*pos_customer_display*. |
|||
|
|||
The configuration of the hardware is done in the configuration file of |
|||
the Odoo server of the POSbox. You should add the following entries in |
|||
the configuration file: |
|||
|
|||
* customer_display_device_name (default = /dev/ttyUSB0) |
|||
* customer_display_device_rate (default = 9600) |
|||
* customer_display_device_timeout (default = 2 seconds) |
|||
|
|||
The number of cols of the Customer Display (usually 20) should be |
|||
configured on the main Odoo server, in the menu Point of Sale > |
|||
Configuration > Point of Sales. The number of rows is supposed to be 2. |
|||
|
|||
It should support most serial and USB-serial LCD displays out-of-the-box |
|||
or with inheritance of a few functions. |
|||
|
|||
It has been tested with: |
|||
|
|||
* Bixolon BCD-1100 (Datasheet : |
|||
http://www.bixolon.com/html/en/product/product_detail.xhtml?prod_id=61) |
|||
* Bixolon BCD-1000 |
|||
|
|||
To setup the BCD-1100 on Linux, you will find some technical instructions |
|||
on this page: |
|||
http://techtuxwords.blogspot.fr/2012/12/linux-and-bixolon-bcd-1100.html |
|||
|
|||
If you have a kernel >= 3.12, you should also read this: |
|||
http://www.leniwiec.org/en/2014/06/25/ubuntu-14-04lts-how-to-pass-id-ven |
|||
dor-and-id-product-to-ftdi_sio-driver/ |
|||
|
|||
This module has been developped during a POS code sprint at Akretion |
|||
France from July 7th to July 10th 2014. This module is part of the POS |
|||
project of the Odoo Community Association http://odoo-community.org/. |
|||
You are invited to become a member and/or get involved in the |
|||
Association ! |
|||
|
|||
This module has been written by Alexis de Lattre from Akretion |
|||
<alexis.delattre@akretion.com>. |
|||
""", |
|||
'author': "Akretion,Odoo Community Association (OCA)", |
|||
'website': 'http://www.akretion.com', |
|||
'depends': ['hw_proxy'], |
|||
'external_dependencies': { |
|||
'python': ['serial', 'unidecode'], |
|||
}, |
|||
'data': [], |
|||
'installable':'False' |
|||
} |
@ -0,0 +1,24 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Hardware Customer Display module for Odoo |
|||
# Copyright (C) 2014 Akretion (http://www.akretion.com) |
|||
# @author Alexis de Lattre <alexis.delattre@akretion.com> |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
|
|||
from . import main |
@ -0,0 +1,192 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Hardware Customer Display module for Odoo |
|||
# Copyright (C) 2014 Akretion (http://www.akretion.com) |
|||
# @author Alexis de Lattre <alexis.delattre@akretion.com> |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
|
|||
import logging |
|||
import json as simplejson |
|||
import time |
|||
from threading import Thread, Lock |
|||
from queue import Queue |
|||
import odoo.addons.hw_proxy.controllers.main as hw_proxy |
|||
from odoo import http |
|||
from odoo.tools.config import config |
|||
|
|||
logger = logging.getLogger(__name__) |
|||
|
|||
CLEAR_DISPLAY = b'\x0C' |
|||
MOVE_CURSOR_TO = b'\x1B\x6C' |
|||
CURSOR_OFF = b'\x1F\x43\x00' |
|||
|
|||
try: |
|||
from serial import Serial |
|||
from unidecode import unidecode |
|||
except (ImportError, IOError) as err: |
|||
logger.debug(err) |
|||
|
|||
|
|||
class CustomerDisplayDriver(Thread): |
|||
def __init__(self): |
|||
Thread.__init__(self) |
|||
self.queue = Queue() |
|||
self.lock = Lock() |
|||
self.status = {'status': 'connecting', 'messages': []} |
|||
self.device_name = config.get( |
|||
'customer_display_device_name', '/dev/ttyUSB0') |
|||
self.device_rate = int(config.get( |
|||
'customer_display_device_rate', 9600)) |
|||
self.device_timeout = int(config.get( |
|||
'customer_display_device_timeout', 2)) |
|||
self.serial = False |
|||
|
|||
def get_status(self): |
|||
self.push_task('status') |
|||
return self.status |
|||
|
|||
def set_status(self, status, message=None): |
|||
if status == self.status['status']: |
|||
if message is not None and message != self.status['messages'][-1]: |
|||
self.status['messages'].append(message) |
|||
else: |
|||
self.status['status'] = status |
|||
if message: |
|||
self.status['messages'] = [message] |
|||
else: |
|||
self.status['messages'] = [] |
|||
|
|||
if status == 'error' and message: |
|||
logger.error('Display Error: ' + message) |
|||
elif status == 'disconnected' and message: |
|||
logger.warning('Disconnected Display: ' + message) |
|||
|
|||
def lockedstart(self): |
|||
with self.lock: |
|||
if not self.isAlive(): |
|||
self.daemon = True |
|||
self.start() |
|||
|
|||
def push_task(self, task, data=None): |
|||
self.lockedstart() |
|||
self.queue.put((time.time(), task, data)) |
|||
|
|||
def move_cursor(self, col, row): |
|||
# Bixolon spec : 11. "Move Cursor to Specified Position" |
|||
position = MOVE_CURSOR_TO + chr(col).encode('ascii') + chr(row).encode('ascii') |
|||
self.cmd_serial_write(position) |
|||
|
|||
def display_text(self, lines): |
|||
logger.debug( |
|||
"Preparing to send the following lines to LCD: %s" % lines) |
|||
# We don't check the number of rows/cols here, because it has already |
|||
# been checked in the POS client in the JS code |
|||
for row, line in enumerate(lines, start=1): |
|||
self.move_cursor(1, row) |
|||
self.serial_write(unidecode(line).encode('ascii')) |
|||
|
|||
def setup_customer_display(self): |
|||
"""Set LCD cursor to off |
|||
If your LCD has different setup instruction(s), you should |
|||
inherit this function""" |
|||
# Bixolon spec : 35. "Set Cursor On/Off" |
|||
self.cmd_serial_write(CURSOR_OFF) |
|||
logger.debug('LCD cursor set to off') |
|||
|
|||
def clear_customer_display(self): |
|||
"""If your LCD has different clearing instruction, you should inherit |
|||
this function""" |
|||
# Bixolon spec : 12. "Clear Display Screen and Clear String Mode" |
|||
self.cmd_serial_write(CLEAR_DISPLAY) |
|||
logger.debug('Customer display cleared') |
|||
|
|||
def cmd_serial_write(self, command): |
|||
"""If your LCD requires a prefix and/or suffix on all commands, |
|||
you should inherit this function""" |
|||
assert isinstance(command, bytes), 'command must be a bytes string' |
|||
self.serial_write(command) |
|||
|
|||
def serial_write(self, text): |
|||
assert isinstance(text, bytes), 'text must be a bytes string' |
|||
self.serial.write(text) |
|||
|
|||
def send_text_customer_display(self, text_to_display): |
|||
"""This function sends the data to the serial/usb port. |
|||
We open and close the serial connection on every message display. |
|||
Why ? |
|||
1. Because it is not a problem for the customer display |
|||
2. Because it is not a problem for performance, according to my tests |
|||
3. Because it allows recovery on errors : you can unplug/replug the |
|||
customer display and it will work again on the next message without |
|||
problem |
|||
""" |
|||
lines = simplejson.loads(text_to_display) |
|||
assert isinstance(lines, list), 'lines_list should be a list' |
|||
try: |
|||
logger.debug( |
|||
'Opening serial port %s for customer display with baudrate %d' |
|||
% (self.device_name, self.device_rate)) |
|||
self.serial = Serial( |
|||
self.device_name, self.device_rate, |
|||
timeout=self.device_timeout) |
|||
logger.debug('serial.is_open = %s' % self.serial.isOpen()) |
|||
self.setup_customer_display() |
|||
self.clear_customer_display() |
|||
self.display_text(lines) |
|||
except Exception as e: |
|||
logger.error('Exception in serial connection: %s' % str(e)) |
|||
finally: |
|||
if self.serial: |
|||
logger.debug('Closing serial port for customer display') |
|||
self.serial.close() |
|||
|
|||
def run(self): |
|||
while True: |
|||
try: |
|||
timestamp, task, data = self.queue.get(True) |
|||
if task == 'display': |
|||
self.send_text_customer_display(data) |
|||
elif task == 'status': |
|||
serial = Serial( |
|||
self.device_name, self.device_rate, |
|||
timeout=self.device_timeout) |
|||
if serial.isOpen(): |
|||
self.set_status( |
|||
'connected', |
|||
'Connected to %s' % self.device_name |
|||
) |
|||
self.serial = serial |
|||
except Exception as e: |
|||
self.set_status('error', str(e)) |
|||
|
|||
|
|||
driver = CustomerDisplayDriver() |
|||
|
|||
hw_proxy.drivers['customer_display'] = driver |
|||
|
|||
|
|||
class CustomerDisplayProxy(hw_proxy.Proxy): |
|||
@http.route( |
|||
'/hw_proxy/send_text_customer_display', type='json', auth='none', |
|||
cors='*') |
|||
def send_text_customer_display(self, text_to_display): |
|||
logger.debug( |
|||
'LCD: Call send_text_customer_display with text=%s', |
|||
text_to_display) |
|||
driver.push_task('display', text_to_display) |
@ -0,0 +1,14 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
@ -0,0 +1,80 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Author : Alexis de Lattre <alexis.delattre@akretion.com> |
|||
# The licence is in the file __manifest__.py |
|||
# This is a test script, that you can use if you want to test/play |
|||
# with the customer display independantly from the Odoo server |
|||
# It has been tested with a Bixolon BCD-1100 |
|||
|
|||
import sys |
|||
import logging |
|||
import time |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
try: |
|||
from serial import Serial |
|||
from unidecode import unidecode |
|||
except (ImportError, IOError) as err: |
|||
_logger.debug(err) |
|||
|
|||
DEVICE = '/dev/bixolon' |
|||
DEVICE_RATE = 9600 |
|||
DEVICE_COLS = 20 |
|||
|
|||
CLEAR_DISPLAY = b'\x0C' |
|||
MOVE_CURSOR_TO = b'\x1B\x6C' |
|||
CURSOR_OFF = b'\x1F\x43\x00' |
|||
|
|||
|
|||
def display_text(ser, line1, line2): |
|||
print(("set lines to the right length (%s)" % DEVICE_COLS)) |
|||
for line in [line1, line2]: |
|||
if len(line) < DEVICE_COLS: |
|||
line += ' ' * (DEVICE_COLS - len(line)) |
|||
elif len(line) > DEVICE_COLS: |
|||
line = line[0:DEVICE_COLS] |
|||
assert len(line) == DEVICE_COLS, 'Wrong length' |
|||
print("try to clear display") |
|||
ser.write(CLEAR_DISPLAY) |
|||
print("clear done") |
|||
print("try to position at start of 1st line") |
|||
l1 = MOVE_CURSOR_TO + chr(1).encode('ascii') + chr(1).encode('ascii') |
|||
ser.write(l1) |
|||
print("position done") |
|||
print("try to write 1st line") |
|||
ser.write(unidecode(line1).encode('ascii')) |
|||
print("write 1st line done") |
|||
time.sleep(1) |
|||
print("try to position at start of 2nd line") |
|||
l2 = MOVE_CURSOR_TO + chr(1).encode('ascii') + chr(2).encode('ascii') |
|||
ser.write(l2) |
|||
print("position done") |
|||
print("try to write 2nd line") |
|||
ser.write(unidecode(line2).encode('ascii')) |
|||
print("write done") |
|||
|
|||
|
|||
def open_close_display(line1, line2): |
|||
ser = False |
|||
try: |
|||
print("open serial port") |
|||
ser = Serial(DEVICE, DEVICE_RATE, timeout=2) |
|||
print(("serial port open =", ser.isOpen())) |
|||
print(("serial name =", ser.name)) |
|||
print("try to set cursor to off") |
|||
ser.write(CURSOR_OFF) |
|||
print("cursor set to off") |
|||
display_text(ser, line1, line2) |
|||
except Exception as e: |
|||
print(('EXCEPTION e={}'.format(e))) |
|||
sys.exit(1) |
|||
finally: |
|||
if ser: |
|||
print("close serial port") |
|||
ser.close() |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
line1 = 'Coop IT Easy' |
|||
line2 = 'Migration to 12.0' |
|||
open_close_display(line1, line2) |
@ -0,0 +1,4 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from . import controllers |
@ -0,0 +1,19 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Vincent Van Rossem <vvrossem@gmail.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).{ |
|||
{ |
|||
'name': "Currency Management Extension for the Hardware Customer Display ", |
|||
'version': '12.0.1.0.0', |
|||
'summary': """ |
|||
Adds currency management to the Hardware Customer Display""", |
|||
|
|||
"author": "Coop IT Easy SCRLfs, " |
|||
"Odoo Community Association (OCA)", |
|||
'license': "AGPL-3", |
|||
'website': "https://github.com/OCA/pos/", |
|||
'category': 'Hardware Drivers', |
|||
'depends': ['hw_customer_display'], |
|||
'data': [], |
|||
'installable': False, |
|||
} |
@ -0,0 +1,2 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from . import main |
@ -0,0 +1,133 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Vincent Van Rossem <vvrossem@gmail.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import logging |
|||
import json as simplejson |
|||
|
|||
import odoo.addons.hw_proxy.controllers.main as hw_proxy |
|||
from odoo.addons.hw_customer_display.controllers.main import CustomerDisplayDriver |
|||
from odoo import http |
|||
|
|||
logger = logging.getLogger(__name__) |
|||
|
|||
SELECT_USER_DEFINED_CHAR = b'\x1B\x25\x01' |
|||
DEFINE_USER_DEFINED_CHAR = b'\x1B\x26\x01' |
|||
EURO_SYMBOL_DRAWING = b'\x05\x14\x3E\x55\x41\x22' |
|||
INIT_DISPLAY = b'\x1B\x40' |
|||
|
|||
|
|||
try: |
|||
from serial import Serial |
|||
from unidecode import unidecode |
|||
except (ImportError, IOError) as err: |
|||
logger.debug(err) |
|||
|
|||
|
|||
class CustomerDisplayCurrencyDriver(CustomerDisplayDriver): |
|||
def __init__(self): |
|||
super().__init__() |
|||
self.currency_char_code = None |
|||
self.currency_code = None |
|||
|
|||
def init_customer_display(self): |
|||
self.serial.write(INIT_DISPLAY) |
|||
logger.debug('Init display') |
|||
|
|||
def set_currency_code(self, currency_code): |
|||
self.currency_code = currency_code |
|||
|
|||
def set_currency_char_code(self, currency_char_code): |
|||
self.currency_char_code = currency_char_code.encode('ascii') |
|||
|
|||
def send_currency_data_customer_display(self, currency_data): |
|||
currency_data = simplejson.loads(currency_data) |
|||
self.set_currency_code(currency_data['currency_code']) |
|||
self.set_currency_char_code(currency_data['currency_char']) |
|||
|
|||
def draw_euro_symbol(self): |
|||
cmd = DEFINE_USER_DEFINED_CHAR + self.currency_char_code + self.currency_char_code + EURO_SYMBOL_DRAWING |
|||
self.serial_write(cmd) |
|||
self.serial_write(SELECT_USER_DEFINED_CHAR) |
|||
logger.debug('Draw euro symbol') |
|||
|
|||
def send_text_customer_display(self, text_to_display): |
|||
"""This function sends the data to the serial/usb port. |
|||
We open and close the serial connection on every message display. |
|||
Why ? |
|||
1. Because it is not a problem for the customer display |
|||
2. Because it is not a problem for performance, according to my tests |
|||
3. Because it allows recovery on errors : you can unplug/replug the |
|||
customer display and it will work again on the next message without |
|||
problem |
|||
""" |
|||
lines = simplejson.loads(text_to_display) |
|||
assert isinstance(lines, list), 'lines_list should be a list' |
|||
try: |
|||
logger.debug( |
|||
'Opening serial port %s for customer display with baudrate %d' |
|||
% (self.device_name, self.device_rate)) |
|||
self.serial = Serial( |
|||
self.device_name, self.device_rate, |
|||
timeout=self.device_timeout) |
|||
logger.debug('serial.is_open = %s' % self.serial.isOpen()) |
|||
|
|||
self.init_customer_display() |
|||
self.setup_customer_display() # set cursor off |
|||
|
|||
if self.currency_code == 'EUR': |
|||
self.draw_euro_symbol() |
|||
self.display_text(lines) |
|||
except Exception as e: |
|||
logger.error('Exception in serial connection: %s' % str(e)) |
|||
finally: |
|||
if self.serial: |
|||
logger.debug('Closing serial port for customer display') |
|||
self.serial.close() |
|||
|
|||
def run(self): |
|||
while True: |
|||
try: |
|||
timestamp, task, data = self.queue.get(True) |
|||
if task == 'display': |
|||
self.send_text_customer_display(data) |
|||
elif task == 'currency': |
|||
self.send_currency_data_customer_display(data) |
|||
elif task == 'status': |
|||
serial = Serial( |
|||
self.device_name, self.device_rate, |
|||
timeout=self.device_timeout) |
|||
if serial.isOpen(): |
|||
self.set_status( |
|||
'connected', |
|||
'Connected to %s' % self.device_name |
|||
) |
|||
self.serial = serial |
|||
except Exception as e: |
|||
self.set_status('error', str(e)) |
|||
|
|||
|
|||
driver = CustomerDisplayCurrencyDriver() |
|||
|
|||
hw_proxy.drivers['customer_display'] = driver |
|||
|
|||
|
|||
class CustomerDisplayCurrencyProxy(hw_proxy.Proxy): |
|||
@http.route( |
|||
'/hw_proxy/send_text_customer_display', type='json', auth='none', |
|||
cors='*') |
|||
def send_text_customer_display(self, text_to_display): |
|||
logger.debug( |
|||
'LCD: Call send_text_customer_display with text=%s', |
|||
text_to_display) |
|||
driver.push_task('display', text_to_display) |
|||
|
|||
@http.route( |
|||
'/hw_proxy/send_currency_data_customer_display', type='json', auth='none', |
|||
cors='*') |
|||
def send_currency_data_customer_display(self, currency_data): |
|||
logger.debug( |
|||
'LCD: Call send_currency_data_customer_display with json=%s', |
|||
currency_data) |
|||
driver.push_task('currency', currency_data) |
@ -0,0 +1,102 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Vincent Van Rossem <vvrossem@gmail.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import sys |
|||
import logging |
|||
import time |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
try: |
|||
from serial import Serial |
|||
from unidecode import unidecode |
|||
except (ImportError, IOError) as err: |
|||
_logger.debug(err) |
|||
|
|||
DEVICE = '/dev/bixolon' |
|||
DEVICE_RATE = 9600 |
|||
DEVICE_COLS = 20 |
|||
|
|||
# Bixolon BCD-1100 COMMANDS |
|||
INIT_DISPLAY = b'\x1B\x40' |
|||
MOVE_CURSOR_TO = b'\x1B\x6C' |
|||
SET_CURSOR_OFF = b'\x1F\x43\x00' |
|||
SELECT_USER_DEFINED_CHAR = b'\x1B\x25\x01' |
|||
DEFINE_USER_DEFINED_CHAR = b'\x1B\x26\x01' |
|||
EURO_SYMBOL_DRAWING = b'\x05\x14\x3E\x55\x41\x22' |
|||
|
|||
|
|||
def draw_euro_symbol(ser, char_code): |
|||
char_code = char_code.encode('ascii') |
|||
print('char_code encoded:', char_code) |
|||
cmd = DEFINE_USER_DEFINED_CHAR + char_code + char_code + EURO_SYMBOL_DRAWING |
|||
ser.write(cmd) |
|||
ser.write(SELECT_USER_DEFINED_CHAR) |
|||
|
|||
|
|||
def display_text(ser, line1, line2): |
|||
print("\nset lines to the right length (%s)" % DEVICE_COLS) |
|||
for line in [line1, line2]: |
|||
if len(line) < DEVICE_COLS: |
|||
line += ' ' * (DEVICE_COLS - len(line)) |
|||
elif len(line) > DEVICE_COLS: |
|||
line = line[0:DEVICE_COLS] |
|||
assert len(line) == DEVICE_COLS, 'Wrong length' |
|||
|
|||
print("\ntry to draw euro symbol") |
|||
draw_euro_symbol(ser, '~') |
|||
print('\tdraw euro symbol done') |
|||
|
|||
print("\ntry to position at start of 1st line") |
|||
l1 = MOVE_CURSOR_TO + chr(1).encode('ascii') + chr(1).encode('ascii') |
|||
ser.write(l1) |
|||
print("\tposition done") |
|||
|
|||
print("\ntry to write 1st line") |
|||
ser.write(unidecode(line1).encode('ascii')) |
|||
print("\twrite 1st line done") |
|||
|
|||
time.sleep(1) |
|||
|
|||
print("\ntry to position at start of 2nd line") |
|||
l2 = MOVE_CURSOR_TO + chr(1).encode('ascii') + chr(2).encode('ascii') |
|||
ser.write(l2) |
|||
print("\tposition done") |
|||
|
|||
print("\ntry to write 2nd line") |
|||
ser.write(unidecode(line2).encode('ascii')) |
|||
print("\twrite 2nd line done") |
|||
|
|||
|
|||
def open_close_display(line1, line2): |
|||
ser = False |
|||
try: |
|||
print("open serial port") |
|||
ser = Serial(DEVICE, DEVICE_RATE, timeout=2) |
|||
print("serial port open =", ser.isOpen()) |
|||
print("serial name =", ser.name) |
|||
|
|||
print("\ntry to (re)initialize display") |
|||
ser.write(INIT_DISPLAY) |
|||
print("\t(re)initialize display done") |
|||
|
|||
print("\ntry to set cursor to off") |
|||
ser.write(SET_CURSOR_OFF) |
|||
print("\tcursor set to off") |
|||
|
|||
display_text(ser, line1, line2) |
|||
except Exception as e: |
|||
print('EXCEPTION e={}'.format(e)) |
|||
sys.exit(1) |
|||
finally: |
|||
if ser: |
|||
print("\nclose serial port") |
|||
ser.close() |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
line1 = 'Coop IT Easy' |
|||
line2 = 'Draw € symbol: ~' |
|||
open_close_display(line1, line2) |
@ -0,0 +1,81 @@ |
|||
============= |
|||
POS Container |
|||
============= |
|||
|
|||
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|||
!! This file is generated by oca-gen-addon-readme !! |
|||
!! changes will be overwritten. !! |
|||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|||
|
|||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png |
|||
:target: https://odoo-community.org/page/development-status |
|||
:alt: Beta |
|||
.. |badge2| image:: https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github |
|||
:target: https://github.com/OCA/pos/tree/12.0/pos_container |
|||
:alt: OCA/pos |
|||
.. |badge3| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png |
|||
:target: https://translation.odoo-community.org/projects/pos-12-0/pos-12-0-pos_container |
|||
:alt: Translate me on Weblate |
|||
.. |badge4| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png |
|||
:target: https://runbot.odoo-community.org/runbot/184/12.0 |
|||
:alt: Try me on Runbot |
|||
|
|||
|badge1| |badge2| |badge3| |badge4| |
|||
|
|||
This module allows to handle use of reusable containers in POS, |
|||
this is useful to handle selling product in bulk without having to calculate |
|||
the tare of the container. |
|||
|
|||
Each container is identified by a barcode, the weight is stored in Odoo. |
|||
|
|||
**Table of contents** |
|||
|
|||
.. contents:: |
|||
:local: |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
You have to create a Barcode Nomenclature to handle containers before using the |
|||
module. |
|||
|
|||
Bug Tracker |
|||
=========== |
|||
|
|||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/pos/issues>`_. |
|||
In case of trouble, please check there if your issue has already been reported. |
|||
If you spotted it first, help us smashing it by providing a detailed and welcomed |
|||
`feedback <https://github.com/OCA/pos/issues/new?body=module:%20pos_container%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. |
|||
|
|||
Do not contact contributors directly about support or help with technical issues. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Authors |
|||
~~~~~~~ |
|||
|
|||
* Coop IT Easy SCRLfs |
|||
|
|||
Contributors |
|||
~~~~~~~~~~~~ |
|||
|
|||
* Pierrick Brun <pierrick.brun@akretion.com> |
|||
* Robin Keunen <robin.keunen@coopiteasy.be> |
|||
|
|||
Maintainers |
|||
~~~~~~~~~~~ |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use. |
|||
|
|||
This module is part of the `OCA/pos <https://github.com/OCA/pos/tree/12.0/pos_container>`_ project on GitHub. |
|||
|
|||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. |
@ -0,0 +1,2 @@ |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
from . import models |
@ -0,0 +1,36 @@ |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Robin Keunen <robin@coopiteasy.be> |
|||
# Pierrick Brun <pierrick.brun@akretion.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).{ |
|||
{ |
|||
'name': "POS Container", |
|||
'version': '12.0.1.0.0', |
|||
|
|||
'summary': """ |
|||
Allows managing pre-weighted containers for bulk shop""", |
|||
|
|||
"author": "Coop IT Easy SCRLfs, " |
|||
"Odoo Community Association (OCA)", |
|||
'license': "AGPL-3", |
|||
'website': "https://github.com/OCA/pos/", |
|||
|
|||
'category': 'Point of Sale', |
|||
|
|||
'depends': ['point_of_sale'], |
|||
|
|||
'data': [ |
|||
'data/product.xml', |
|||
'views/container.xml', |
|||
'templates/templates.xml', |
|||
'security/ir.model.access.csv', |
|||
], |
|||
'demo': [ |
|||
'demo/demo.xml', |
|||
], |
|||
|
|||
'qweb': [ |
|||
'static/src/xml/pos.xml', |
|||
], |
|||
|
|||
'installable': True, |
|||
} |
@ -0,0 +1,20 @@ |
|||
<odoo> |
|||
<record id="temporary_container_product" model="product.product"> |
|||
<field name="name">Container without product</field> |
|||
<!-- The barcode is used to find it from the POS --> |
|||
<field name="barcode">CONTAINER</field> |
|||
<field name="uom_id" ref="uom.product_uom_kgm"/> |
|||
<field name="uom_po_id" ref="uom.product_uom_kgm"/> |
|||
<field name="purchase_ok" eval="False"/> |
|||
<field name="description"> |
|||
This product is used to describe POS order lines having a container but no product yet |
|||
</field> |
|||
<field name="list_price">0</field> |
|||
<field name="available_in_pos" eval="True"/> |
|||
<field name="to_weight" eval="True"/> |
|||
<field name="active" eval="False"/> |
|||
</record> |
|||
<record id="temporary_container_product_product_template" model="product.template"> |
|||
<field name="active" eval="False"/> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,19 @@ |
|||
<odoo> |
|||
<record id="container_1" model="pos.container"> |
|||
<field name="name">Container 1</field> |
|||
<field name="barcode">0498765456789</field> |
|||
<field name="weight">0.123</field> |
|||
</record> |
|||
|
|||
<record id="container_2" model="pos.container"> |
|||
<field name="name">Container 2</field> |
|||
<field name="barcode">0490987654356</field> |
|||
<field name="weight">0.234</field> |
|||
</record> |
|||
|
|||
<record id="container_3" model="pos.container"> |
|||
<field name="name">Container 3</field> |
|||
<field name="barcode">0490987654398</field> |
|||
<field name="weight">0.567</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,384 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_container |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 12.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2019-11-19 09:34+0000\n" |
|||
"PO-Revision-Date: 2019-11-19 10:42+0100\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"Plural-Forms: \n" |
|||
"Language: fr\n" |
|||
"X-Generator: Poedit 2.2.4\n" |
|||
|
|||
#. module: pos_container |
|||
#: model:product.product,description:pos_container.temporary_container_product |
|||
#: model:product.template,description:pos_container.temporary_container_product_product_template |
|||
msgid "" |
|||
"\n" |
|||
" This product is used to describe POS order lines having a container but no product yet\n" |
|||
" " |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:161 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:202 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:252 |
|||
#, python-format |
|||
msgid "% discount" |
|||
msgstr "% de réduction" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:127 |
|||
#, python-format |
|||
msgid "Add a container" |
|||
msgstr "Ajouter un contenant" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Alias" |
|||
msgstr "Alias" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:184 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:318 |
|||
#, python-format |
|||
msgid "Automatic Weighing" |
|||
msgstr "Pesée automatique" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:124 |
|||
#, python-format |
|||
msgid "Back" |
|||
msgstr "Retour" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:48 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__barcode |
|||
#, python-format |
|||
msgid "Barcode" |
|||
msgstr "Code Barre" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_barcode_rule |
|||
msgid "Barcode Rule" |
|||
msgstr "Règle de code barre" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:22 |
|||
#, python-format |
|||
msgid "Cancel" |
|||
msgstr "Annuler" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Cashier" |
|||
msgstr "Caissier" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Client" |
|||
msgstr "Client" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:374 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:6 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:135 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__container_id |
|||
#, python-format |
|||
msgid "Container" |
|||
msgstr "Contenants" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__container_weight |
|||
msgid "Container Weight" |
|||
msgstr "Poids du contenant" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:80 |
|||
#, python-format |
|||
msgid "Container deletion" |
|||
msgstr "Suppression de contenant" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_pos_container |
|||
msgid "Container for bulk items" |
|||
msgstr "Contenant pour produits en vrac" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:134 |
|||
#, python-format |
|||
msgid "Container name:" |
|||
msgstr "Nom du contenant:" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Container unit" |
|||
msgstr "Unité de contenant" |
|||
|
|||
#. module: pos_container |
|||
#: model:product.product,name:pos_container.temporary_container_product |
|||
#: model:product.template,name:pos_container.temporary_container_product_product_template |
|||
msgid "Container without product" |
|||
msgstr "Contenant sans produit" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.actions.act_window,name:pos_container.pos_container_action_window |
|||
#: model:ir.ui.menu,name:pos_container.pos_container_menu |
|||
msgid "Containers" |
|||
msgstr "Contenants" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__create_uid |
|||
msgid "Created by" |
|||
msgstr "Créé par" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__create_date |
|||
msgid "Created on" |
|||
msgstr "Créé le" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:30 |
|||
#, python-format |
|||
msgid "Delete container" |
|||
msgstr "Supprimer le contenant" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:295 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:335 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:383 |
|||
#, python-format |
|||
msgid "Discount:" |
|||
msgstr "Réduction:" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Discounted Product" |
|||
msgstr "Article en promotion" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__display_name |
|||
msgid "Display Name" |
|||
msgstr "Nom affiché" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:81 |
|||
#, python-format |
|||
msgid "Do you want to delete this container ?\n" |
|||
msgstr "Voulez-vous supprimer ce contenant ?\n" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:144 |
|||
#, python-format |
|||
msgid "Error: Could not Save Changes" |
|||
msgstr "Erreur: Impossible de sauvegarder les changements" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:84 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:101 |
|||
#, python-format |
|||
msgid "Gross :" |
|||
msgstr "Poids Brut :" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__id |
|||
msgid "ID" |
|||
msgstr "ID" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container____last_update |
|||
msgid "Last Modified on" |
|||
msgstr "Dernière modification le" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "Dernière mise à jour par" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__write_date |
|||
msgid "Last Updated on" |
|||
msgstr "Dernière mise à jour le" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Location" |
|||
msgstr "Lieu" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Lot" |
|||
msgstr "Lot" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:234 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:366 |
|||
#, python-format |
|||
msgid "Manual Input" |
|||
msgstr "Saisie manuelle" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:47 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__name |
|||
#, python-format |
|||
msgid "Name" |
|||
msgstr "Nom" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__owner_id |
|||
msgid "Owner" |
|||
msgstr "Propriétaire" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Package" |
|||
msgstr "Colis" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_pos_order_line |
|||
msgid "Point of Sale Order Lines" |
|||
msgstr "Lignes de Commande en Point de Vente" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Priced Product" |
|||
msgstr "Article à prix fixe" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:137 |
|||
#, python-format |
|||
msgid "Save" |
|||
msgstr "Sauvegarder" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:26 |
|||
#, python-format |
|||
msgid "Search Containers" |
|||
msgstr "Recherche contenant" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:34 |
|||
#, python-format |
|||
msgid "Select a container" |
|||
msgstr "Sélection contenant" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:12 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__tare |
|||
#, python-format |
|||
msgid "Tare" |
|||
msgstr "Tare" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:215 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:266 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:356 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:404 |
|||
#, python-format |
|||
msgid "Tare :" |
|||
msgstr "PT :" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_barcode_rule__type |
|||
msgid "Type" |
|||
msgstr "Type" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Unit Product" |
|||
msgstr "Unité de produit" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:49 |
|||
#, python-format |
|||
msgid "Weight" |
|||
msgstr "Poids" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__weight |
|||
msgid "Weight (g)" |
|||
msgstr "Poids (kg)" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Weighted Product" |
|||
msgstr "Article pesé" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:160 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:201 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:251 |
|||
#, python-format |
|||
msgid "With a" |
|||
msgstr "Avec un" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:138 |
|||
#, python-format |
|||
msgid "Your Internet connection is probably down." |
|||
msgstr "Votre connexion internet est probablement coupée." |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:85 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:102 |
|||
#: model:product.product,uom_name:pos_container.temporary_container_product |
|||
#: model:product.product,weight_uom_name:pos_container.temporary_container_product |
|||
#: model:product.template,uom_name:pos_container.temporary_container_product_product_template |
|||
#: model:product.template,weight_uom_name:pos_container.temporary_container_product_product_template |
|||
#, python-format |
|||
msgid "kg" |
|||
msgstr "kg" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:90 |
|||
#, python-format |
|||
msgid "" |
|||
"kg \n" |
|||
" -" |
|||
msgstr "" |
|||
"kg\n" |
|||
" -" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:107 |
|||
#, python-format |
|||
msgid "" |
|||
"kg \n" |
|||
" - Manual tare" |
|||
msgstr "" |
|||
"kg \n" |
|||
" - Tare manuelle" |
@ -0,0 +1,377 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_container |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 12.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2019-11-19 09:33+0000\n" |
|||
"PO-Revision-Date: 2019-11-19 09:33+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: pos_container |
|||
#: model:product.product,description:pos_container.temporary_container_product |
|||
#: model:product.template,description:pos_container.temporary_container_product_product_template |
|||
msgid "\n" |
|||
" This product is used to describe POS order lines having a container but no product yet\n" |
|||
" " |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:161 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:202 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:252 |
|||
#, python-format |
|||
msgid "% discount" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:127 |
|||
#, python-format |
|||
msgid "Add a container" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Alias" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:184 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:318 |
|||
#, python-format |
|||
msgid "Automatic Weighing" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:124 |
|||
#, python-format |
|||
msgid "Back" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:48 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__barcode |
|||
#, python-format |
|||
msgid "Barcode" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_barcode_rule |
|||
msgid "Barcode Rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:22 |
|||
#, python-format |
|||
msgid "Cancel" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Cashier" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Client" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:374 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:6 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:135 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__container_id |
|||
#, python-format |
|||
msgid "Container" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__container_weight |
|||
msgid "Container Weight" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:80 |
|||
#, python-format |
|||
msgid "Container deletion" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_pos_container |
|||
msgid "Container for bulk items" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:134 |
|||
#, python-format |
|||
msgid "Container name:" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Container unit" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:product.product,name:pos_container.temporary_container_product |
|||
#: model:product.template,name:pos_container.temporary_container_product_product_template |
|||
msgid "Container without product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.actions.act_window,name:pos_container.pos_container_action_window |
|||
#: model:ir.ui.menu,name:pos_container.pos_container_menu |
|||
msgid "Containers" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__create_uid |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__create_date |
|||
msgid "Created on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:30 |
|||
#, python-format |
|||
msgid "Delete container" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:295 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:335 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:383 |
|||
#, python-format |
|||
msgid "Discount:" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Discounted Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__display_name |
|||
msgid "Display Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:81 |
|||
#, python-format |
|||
msgid "Do you want to delete this container ?\n" |
|||
"" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:144 |
|||
#, python-format |
|||
msgid "Error: Could not Save Changes" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:84 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:101 |
|||
#, python-format |
|||
msgid "Gross :" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__id |
|||
msgid "ID" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container____last_update |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__write_date |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Location" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Lot" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:234 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:366 |
|||
#, python-format |
|||
msgid "Manual Input" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:47 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__name |
|||
#, python-format |
|||
msgid "Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__owner_id |
|||
msgid "Owner" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Package" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model,name:pos_container.model_pos_order_line |
|||
msgid "Point of Sale Order Lines" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Priced Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:137 |
|||
#, python-format |
|||
msgid "Save" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:26 |
|||
#, python-format |
|||
msgid "Search Containers" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:34 |
|||
#, python-format |
|||
msgid "Select a container" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:12 |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_order_line__tare |
|||
#, python-format |
|||
msgid "Tare" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:215 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:266 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:356 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:404 |
|||
#, python-format |
|||
msgid "Tare :" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_barcode_rule__type |
|||
msgid "Type" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Unit Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:49 |
|||
#, python-format |
|||
msgid "Weight" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: model:ir.model.fields,field_description:pos_container.field_pos_container__weight |
|||
msgid "Weight (g)" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#: selection:barcode.rule,type:0 |
|||
msgid "Weighted Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:160 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:201 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:251 |
|||
#, python-format |
|||
msgid "With a" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/js/container.js:138 |
|||
#, python-format |
|||
msgid "Your Internet connection is probably down." |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:85 |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:102 |
|||
#: model:product.product,uom_name:pos_container.temporary_container_product |
|||
#: model:product.product,weight_uom_name:pos_container.temporary_container_product |
|||
#: model:product.template,uom_name:pos_container.temporary_container_product_product_template |
|||
#: model:product.template,weight_uom_name:pos_container.temporary_container_product_product_template |
|||
#, python-format |
|||
msgid "kg" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:90 |
|||
#, python-format |
|||
msgid "kg \n" |
|||
" -" |
|||
msgstr "" |
|||
|
|||
#. module: pos_container |
|||
#. openerp-web |
|||
#: code:addons/pos_container/static/src/xml/pos.xml:107 |
|||
#, python-format |
|||
msgid "kg \n" |
|||
" - Manual tare" |
|||
msgstr "" |
|||
|
@ -0,0 +1,3 @@ |
|||
from . import container |
|||
from . import barcode |
|||
from . import pos_order_line |
@ -0,0 +1,13 @@ |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Pierrick Brun <pierrick.brun@akretion.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import models, fields |
|||
|
|||
|
|||
class BarcodeRule(models.Model): |
|||
_inherit = 'barcode.rule' |
|||
|
|||
type = fields.Selection( |
|||
selection_add=[('container', 'Container unit')], |
|||
) |
@ -0,0 +1,45 @@ |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Robin Keunen <robin@coopiteasy.be> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import models, fields, api |
|||
|
|||
|
|||
class Container(models.Model): |
|||
_name = 'pos.container' |
|||
_description = 'Container for bulk items' |
|||
|
|||
name = fields.Char( |
|||
string='Name', |
|||
) |
|||
barcode = fields.Char( |
|||
'Barcode', |
|||
size=13, |
|||
) |
|||
weight = fields.Float( |
|||
string='Weight (kg)', |
|||
) |
|||
owner_id = fields.Many2one( |
|||
comodel_name='res.partner', |
|||
inverse_name='container_ids', |
|||
string='Owner', |
|||
) |
|||
|
|||
_sql_constraints = [ |
|||
('barcode_uniq', |
|||
'unique(barcode)', |
|||
"A barcode can only be assigned to one container !"), |
|||
] |
|||
|
|||
@api.model |
|||
def create_from_ui(self, containers): |
|||
# retourne la liste des ids dans le même ordre que la liste fournie |
|||
container_ids = [] |
|||
for container in containers: |
|||
container_id = container.pop('id', False) |
|||
if container_id: # Modifying existing container |
|||
self.browse(container_id).write(container) |
|||
else: |
|||
container_id = self.create(container).id |
|||
container_ids.append(container_id) |
|||
return container_ids |
@ -0,0 +1,15 @@ |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# @author Pierrick Brun <pierrick.brun@akretion.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from odoo import fields, models |
|||
|
|||
|
|||
class PosOrderLine(models.Model): |
|||
_inherit = 'pos.order.line' |
|||
|
|||
tare = fields.Float('Tare') |
|||
container_weight = fields.Float('Container Weight') |
|||
container_id = fields.Many2one( |
|||
'pos.container', |
|||
'Container') |
@ -0,0 +1,2 @@ |
|||
* Pierrick Brun <pierrick.brun@akretion.com> |
|||
* Robin Keunen <robin.keunen@coopiteasy.be> |
@ -0,0 +1,5 @@ |
|||
This module allows to handle use of reusable containers in POS, |
|||
this is useful to handle selling product in bulk without having to calculate |
|||
the tare of the container. |
|||
|
|||
Each container is identified by a barcode, the weight is stored in Odoo. |
@ -0,0 +1,2 @@ |
|||
You have to create a Barcode Nomenclature to handle containers before using the |
|||
module. |
@ -0,0 +1,3 @@ |
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|||
access_pos_container_pos_user,access_pos_container_pos_user,model_pos_container,point_of_sale.group_pos_user,1,1,1,1 |
|||
access_pos_container_pos_manager,access_pos_container_pos_manager,model_pos_container,point_of_sale.group_pos_manager,1,1,1,1 |
@ -0,0 +1,431 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
|||
<head> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
|||
<meta name="generator" content="Docutils 0.14: http://docutils.sourceforge.net/" /> |
|||
<title>POS Container</title> |
|||
<style type="text/css"> |
|||
|
|||
/* |
|||
:Author: David Goodger (goodger@python.org) |
|||
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $ |
|||
:Copyright: This stylesheet has been placed in the public domain. |
|||
|
|||
Default cascading style sheet for the HTML output of Docutils. |
|||
|
|||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to |
|||
customize this style sheet. |
|||
*/ |
|||
|
|||
/* used to remove borders from tables and images */ |
|||
.borderless, table.borderless td, table.borderless th { |
|||
border: 0 } |
|||
|
|||
table.borderless td, table.borderless th { |
|||
/* Override padding for "table.docutils td" with "! important". |
|||
The right padding separates the table cells. */ |
|||
padding: 0 0.5em 0 0 ! important } |
|||
|
|||
.first { |
|||
/* Override more specific margin styles with "! important". */ |
|||
margin-top: 0 ! important } |
|||
|
|||
.last, .with-subtitle { |
|||
margin-bottom: 0 ! important } |
|||
|
|||
.hidden { |
|||
display: none } |
|||
|
|||
.subscript { |
|||
vertical-align: sub; |
|||
font-size: smaller } |
|||
|
|||
.superscript { |
|||
vertical-align: super; |
|||
font-size: smaller } |
|||
|
|||
a.toc-backref { |
|||
text-decoration: none ; |
|||
color: black } |
|||
|
|||
blockquote.epigraph { |
|||
margin: 2em 5em ; } |
|||
|
|||
dl.docutils dd { |
|||
margin-bottom: 0.5em } |
|||
|
|||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { |
|||
overflow: hidden; |
|||
} |
|||
|
|||
/* Uncomment (and remove this text!) to get bold-faced definition list terms |
|||
dl.docutils dt { |
|||
font-weight: bold } |
|||
*/ |
|||
|
|||
div.abstract { |
|||
margin: 2em 5em } |
|||
|
|||
div.abstract p.topic-title { |
|||
font-weight: bold ; |
|||
text-align: center } |
|||
|
|||
div.admonition, div.attention, div.caution, div.danger, div.error, |
|||
div.hint, div.important, div.note, div.tip, div.warning { |
|||
margin: 2em ; |
|||
border: medium outset ; |
|||
padding: 1em } |
|||
|
|||
div.admonition p.admonition-title, div.hint p.admonition-title, |
|||
div.important p.admonition-title, div.note p.admonition-title, |
|||
div.tip p.admonition-title { |
|||
font-weight: bold ; |
|||
font-family: sans-serif } |
|||
|
|||
div.attention p.admonition-title, div.caution p.admonition-title, |
|||
div.danger p.admonition-title, div.error p.admonition-title, |
|||
div.warning p.admonition-title, .code .error { |
|||
color: red ; |
|||
font-weight: bold ; |
|||
font-family: sans-serif } |
|||
|
|||
/* Uncomment (and remove this text!) to get reduced vertical space in |
|||
compound paragraphs. |
|||
div.compound .compound-first, div.compound .compound-middle { |
|||
margin-bottom: 0.5em } |
|||
|
|||
div.compound .compound-last, div.compound .compound-middle { |
|||
margin-top: 0.5em } |
|||
*/ |
|||
|
|||
div.dedication { |
|||
margin: 2em 5em ; |
|||
text-align: center ; |
|||
font-style: italic } |
|||
|
|||
div.dedication p.topic-title { |
|||
font-weight: bold ; |
|||
font-style: normal } |
|||
|
|||
div.figure { |
|||
margin-left: 2em ; |
|||
margin-right: 2em } |
|||
|
|||
div.footer, div.header { |
|||
clear: both; |
|||
font-size: smaller } |
|||
|
|||
div.line-block { |
|||
display: block ; |
|||
margin-top: 1em ; |
|||
margin-bottom: 1em } |
|||
|
|||
div.line-block div.line-block { |
|||
margin-top: 0 ; |
|||
margin-bottom: 0 ; |
|||
margin-left: 1.5em } |
|||
|
|||
div.sidebar { |
|||
margin: 0 0 0.5em 1em ; |
|||
border: medium outset ; |
|||
padding: 1em ; |
|||
background-color: #ffffee ; |
|||
width: 40% ; |
|||
float: right ; |
|||
clear: right } |
|||
|
|||
div.sidebar p.rubric { |
|||
font-family: sans-serif ; |
|||
font-size: medium } |
|||
|
|||
div.system-messages { |
|||
margin: 5em } |
|||
|
|||
div.system-messages h1 { |
|||
color: red } |
|||
|
|||
div.system-message { |
|||
border: medium outset ; |
|||
padding: 1em } |
|||
|
|||
div.system-message p.system-message-title { |
|||
color: red ; |
|||
font-weight: bold } |
|||
|
|||
div.topic { |
|||
margin: 2em } |
|||
|
|||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, |
|||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { |
|||
margin-top: 0.4em } |
|||
|
|||
h1.title { |
|||
text-align: center } |
|||
|
|||
h2.subtitle { |
|||
text-align: center } |
|||
|
|||
hr.docutils { |
|||
width: 75% } |
|||
|
|||
img.align-left, .figure.align-left, object.align-left, table.align-left { |
|||
clear: left ; |
|||
float: left ; |
|||
margin-right: 1em } |
|||
|
|||
img.align-right, .figure.align-right, object.align-right, table.align-right { |
|||
clear: right ; |
|||
float: right ; |
|||
margin-left: 1em } |
|||
|
|||
img.align-center, .figure.align-center, object.align-center { |
|||
display: block; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
} |
|||
|
|||
table.align-center { |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
} |
|||
|
|||
.align-left { |
|||
text-align: left } |
|||
|
|||
.align-center { |
|||
clear: both ; |
|||
text-align: center } |
|||
|
|||
.align-right { |
|||
text-align: right } |
|||
|
|||
/* reset inner alignment in figures */ |
|||
div.align-right { |
|||
text-align: inherit } |
|||
|
|||
/* div.align-center * { */ |
|||
/* text-align: left } */ |
|||
|
|||
.align-top { |
|||
vertical-align: top } |
|||
|
|||
.align-middle { |
|||
vertical-align: middle } |
|||
|
|||
.align-bottom { |
|||
vertical-align: bottom } |
|||
|
|||
ol.simple, ul.simple { |
|||
margin-bottom: 1em } |
|||
|
|||
ol.arabic { |
|||
list-style: decimal } |
|||
|
|||
ol.loweralpha { |
|||
list-style: lower-alpha } |
|||
|
|||
ol.upperalpha { |
|||
list-style: upper-alpha } |
|||
|
|||
ol.lowerroman { |
|||
list-style: lower-roman } |
|||
|
|||
ol.upperroman { |
|||
list-style: upper-roman } |
|||
|
|||
p.attribution { |
|||
text-align: right ; |
|||
margin-left: 50% } |
|||
|
|||
p.caption { |
|||
font-style: italic } |
|||
|
|||
p.credits { |
|||
font-style: italic ; |
|||
font-size: smaller } |
|||
|
|||
p.label { |
|||
white-space: nowrap } |
|||
|
|||
p.rubric { |
|||
font-weight: bold ; |
|||
font-size: larger ; |
|||
color: maroon ; |
|||
text-align: center } |
|||
|
|||
p.sidebar-title { |
|||
font-family: sans-serif ; |
|||
font-weight: bold ; |
|||
font-size: larger } |
|||
|
|||
p.sidebar-subtitle { |
|||
font-family: sans-serif ; |
|||
font-weight: bold } |
|||
|
|||
p.topic-title { |
|||
font-weight: bold } |
|||
|
|||
pre.address { |
|||
margin-bottom: 0 ; |
|||
margin-top: 0 ; |
|||
font: inherit } |
|||
|
|||
pre.literal-block, pre.doctest-block, pre.math, pre.code { |
|||
margin-left: 2em ; |
|||
margin-right: 2em } |
|||
|
|||
pre.code .ln { color: grey; } /* line numbers */ |
|||
pre.code, code { background-color: #eeeeee } |
|||
pre.code .comment, code .comment { color: #5C6576 } |
|||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } |
|||
pre.code .literal.string, code .literal.string { color: #0C5404 } |
|||
pre.code .name.builtin, code .name.builtin { color: #352B84 } |
|||
pre.code .deleted, code .deleted { background-color: #DEB0A1} |
|||
pre.code .inserted, code .inserted { background-color: #A3D289} |
|||
|
|||
span.classifier { |
|||
font-family: sans-serif ; |
|||
font-style: oblique } |
|||
|
|||
span.classifier-delimiter { |
|||
font-family: sans-serif ; |
|||
font-weight: bold } |
|||
|
|||
span.interpreted { |
|||
font-family: sans-serif } |
|||
|
|||
span.option { |
|||
white-space: nowrap } |
|||
|
|||
span.pre { |
|||
white-space: pre } |
|||
|
|||
span.problematic { |
|||
color: red } |
|||
|
|||
span.section-subtitle { |
|||
/* font-size relative to parent (h1..h6 element) */ |
|||
font-size: 80% } |
|||
|
|||
table.citation { |
|||
border-left: solid 1px gray; |
|||
margin-left: 1px } |
|||
|
|||
table.docinfo { |
|||
margin: 2em 4em } |
|||
|
|||
table.docutils { |
|||
margin-top: 0.5em ; |
|||
margin-bottom: 0.5em } |
|||
|
|||
table.footnote { |
|||
border-left: solid 1px black; |
|||
margin-left: 1px } |
|||
|
|||
table.docutils td, table.docutils th, |
|||
table.docinfo td, table.docinfo th { |
|||
padding-left: 0.5em ; |
|||
padding-right: 0.5em ; |
|||
vertical-align: top } |
|||
|
|||
table.docutils th.field-name, table.docinfo th.docinfo-name { |
|||
font-weight: bold ; |
|||
text-align: left ; |
|||
white-space: nowrap ; |
|||
padding-left: 0 } |
|||
|
|||
/* "booktabs" style (no vertical lines) */ |
|||
table.docutils.booktabs { |
|||
border: 0px; |
|||
border-top: 2px solid; |
|||
border-bottom: 2px solid; |
|||
border-collapse: collapse; |
|||
} |
|||
table.docutils.booktabs * { |
|||
border: 0px; |
|||
} |
|||
table.docutils.booktabs th { |
|||
border-bottom: thin solid; |
|||
text-align: left; |
|||
} |
|||
|
|||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, |
|||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { |
|||
font-size: 100% } |
|||
|
|||
ul.auto-toc { |
|||
list-style-type: none } |
|||
|
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div class="document" id="pos-container"> |
|||
<h1 class="title">POS Container</h1> |
|||
|
|||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|||
!! This file is generated by oca-gen-addon-readme !! |
|||
!! changes will be overwritten. !! |
|||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> |
|||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="https://github.com/OCA/pos/tree/12.0/pos_container"><img alt="OCA/pos" src="https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/pos-12-0/pos-12-0-pos_container"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/184/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p> |
|||
<p>This module allows to handle use of reusable containers in POS, |
|||
this is useful to handle selling product in bulk without having to calculate |
|||
the tare of the container.</p> |
|||
<p>Each container is identified by a barcode, the weight is stored in Odoo.</p> |
|||
<p><strong>Table of contents</strong></p> |
|||
<div class="contents local topic" id="contents"> |
|||
<ul class="simple"> |
|||
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li> |
|||
<li><a class="reference internal" href="#bug-tracker" id="id2">Bug Tracker</a></li> |
|||
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul> |
|||
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li> |
|||
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li> |
|||
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li> |
|||
</ul> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="usage"> |
|||
<h1><a class="toc-backref" href="#id1">Usage</a></h1> |
|||
<p>You have to create a Barcode Nomenclature to handle containers before using the |
|||
module.</p> |
|||
</div> |
|||
<div class="section" id="bug-tracker"> |
|||
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1> |
|||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/pos/issues">GitHub Issues</a>. |
|||
In case of trouble, please check there if your issue has already been reported. |
|||
If you spotted it first, help us smashing it by providing a detailed and welcomed |
|||
<a class="reference external" href="https://github.com/OCA/pos/issues/new?body=module:%20pos_container%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p> |
|||
<p>Do not contact contributors directly about support or help with technical issues.</p> |
|||
</div> |
|||
<div class="section" id="credits"> |
|||
<h1><a class="toc-backref" href="#id3">Credits</a></h1> |
|||
<div class="section" id="authors"> |
|||
<h2><a class="toc-backref" href="#id4">Authors</a></h2> |
|||
<ul class="simple"> |
|||
<li>Coop IT Easy SCRLfs</li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="contributors"> |
|||
<h2><a class="toc-backref" href="#id5">Contributors</a></h2> |
|||
<blockquote> |
|||
<ul class="simple"> |
|||
<li>Pierrick Brun <<a class="reference external" href="mailto:pierrick.brun@akretion.com">pierrick.brun@akretion.com</a>></li> |
|||
<li>Robin Keunen <<a class="reference external" href="mailto:robin.keunen@coopiteasy.be">robin.keunen@coopiteasy.be</a>></li> |
|||
</ul> |
|||
</blockquote> |
|||
</div> |
|||
<div class="section" id="maintainers"> |
|||
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2> |
|||
<p>This module is maintained by the OCA.</p> |
|||
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a> |
|||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use.</p> |
|||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/pos/tree/12.0/pos_container">OCA/pos</a> project on GitHub.</p> |
|||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,191 @@ |
|||
/* firefox seems to ignore the relative positionning of the subwindow-container |
|||
* putting this inside subwindow-container fixes it. |
|||
*/ |
|||
.pos .containerlist-screen .window, |
|||
.pos .containerlist-screen .full-content .subwindow{ |
|||
display: block; |
|||
} |
|||
.pos .containerlist-screen .full-content .subwindow-container{ |
|||
display: block; |
|||
height: 100%; |
|||
} |
|||
.pos .containerlist-screen .full-content .subwindow.collapsed, |
|||
.pos .containerlist-screen .full-content .subwindow-container.collapsed{ |
|||
height: auto; |
|||
} |
|||
|
|||
/* The Scale Container Screen */ |
|||
.pos .scale-screen .add-container{ |
|||
text-align: center; |
|||
font-size: 32px; |
|||
background: rgb(110,200,155); |
|||
color: white; |
|||
border-radius: 3px; |
|||
padding: 16px; |
|||
margin: 16px; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.pos .scale-screen .container-name,.container-barcode{ |
|||
text-align: center; |
|||
font-size: 25px; |
|||
border-radius: 3px; |
|||
padding-top: 10px; |
|||
padding-bottom:10px; |
|||
margin-top: 5px; |
|||
} |
|||
|
|||
/* e) The Container List Screen */ |
|||
|
|||
.pos .containerlist-screen .container-list{ |
|||
font-size: 16px; |
|||
width: 100%; |
|||
line-height: 40px; |
|||
} |
|||
.pos .containerlist-screen .container-list th, |
|||
.pos .containerlist-screen .container-list td { |
|||
padding: 0px 8px; |
|||
} |
|||
.pos .containerlist-screen .container-list tr{ |
|||
transition: all 150ms linear; |
|||
background: rgb(230,230,230); |
|||
} |
|||
.pos .containerlist-screen .container-list thead > tr, |
|||
.pos .containerlist-screen .container-list tr:nth-child(even) { |
|||
background: rgb(247,247,247); |
|||
} |
|||
.pos .containerlist-screen .container-list tr.highlight{ |
|||
transition: all 150ms linear; |
|||
background: rgb(110,200,155) !important; |
|||
color: white; |
|||
} |
|||
.pos .containerlist-screen .container-list tr.lowlight{ |
|||
transition: all 150ms linear; |
|||
background: rgb(216, 238, 227); |
|||
} |
|||
.pos .containerlist-screen .container-list tr.lowlight:nth-child(even){ |
|||
transition: all 150ms linear; |
|||
background: rgb(227, 246, 237); |
|||
} |
|||
.pos .containerlist-screen .container-details{ |
|||
padding: 16px; |
|||
border-bottom: solid 5px rgb(110,200,155); |
|||
} |
|||
.pos .containerlist-screen .container-picture{ |
|||
height: 64px; |
|||
width: 64px; |
|||
border-radius: 32px; |
|||
overflow: hidden; |
|||
text-align: center; |
|||
float: left; |
|||
margin-right: 16px; |
|||
background: white; |
|||
position: relative; |
|||
} |
|||
.pos .containerlist-screen .container-picture > img { |
|||
position: absolute; |
|||
top: -9999px; |
|||
bottom: -9999px; |
|||
right: -9999px; |
|||
left: -9999px; |
|||
max-height: 64px; |
|||
margin: auto; |
|||
} |
|||
.pos .containerlist-screen .container-picture > .fa { |
|||
line-height: 64px; |
|||
font-size: 32px; |
|||
} |
|||
.pos .containerlist-screen .container-picture .image-uploader { |
|||
position: absolute; |
|||
z-index: 1000; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
opacity: 0; |
|||
cursor: pointer; |
|||
} |
|||
.pos .containerlist-screen .container-name { |
|||
font-size: 32px; |
|||
line-height: 64px; |
|||
margin-bottom:16px; |
|||
} |
|||
.pos .containerlist-screen .edit-buttons { |
|||
position: absolute; |
|||
right: 16px; |
|||
top: 10px; |
|||
} |
|||
.pos .containerlist-screen .edit-buttons .button{ |
|||
display: inline-block; |
|||
margin-left: 16px; |
|||
color: rgb(128,128,128); |
|||
cursor: pointer; |
|||
font-size: 36px; |
|||
} |
|||
.pos .containerlist-screen .container-details-box{ |
|||
position: relative; |
|||
font-size: 16px; |
|||
} |
|||
.pos .containerlist-screen .container-details-left{ |
|||
width: 50%; |
|||
float: left; |
|||
} |
|||
.pos .containerlist-screen .container-details-right{ |
|||
width: 50%; |
|||
float: left; |
|||
} |
|||
.pos .containerlist-screen .container-detail{ |
|||
line-height: 24px; |
|||
} |
|||
.pos .containerlist-screen .container-detail > .label{ |
|||
font-weight: bold; |
|||
display: inline-block; |
|||
width: 75px; |
|||
text-align: right; |
|||
margin-right: 8px; |
|||
} |
|||
.pos .containerlist-screen .container-details input, |
|||
.pos .containerlist-screen .container-details select |
|||
{ |
|||
padding: 4px; |
|||
border-radius: 3px; |
|||
border: solid 1px #cecbcb; |
|||
margin-bottom: 4px; |
|||
background: white; |
|||
font-family: "Lato","Lucida Grande", Helvetica, Verdana, Arial; |
|||
color: #555555; |
|||
width: 340px; |
|||
font-size: 14px; |
|||
box-sizing: border-box; |
|||
} |
|||
.pos .containerlist-screen .container-details input.container-name { |
|||
font-size: 24px; |
|||
line-height: 24px; |
|||
margin: 18px 6px; |
|||
width: 340px; |
|||
} |
|||
.pos .containerlist-screen .container-detail > .empty{ |
|||
opacity: 0.3; |
|||
} |
|||
.pos .containerlist-screen .searchbox{ |
|||
right: auto; |
|||
margin-left: -90px; |
|||
margin-top:8px; |
|||
left: 45%; |
|||
} |
|||
.pos .containerlist-screen .searchbox input{ |
|||
width: 120px; |
|||
} |
|||
.pos .containerlist-screen .button.delete-container { |
|||
left: 50%; |
|||
margin-left: 120px; |
|||
} |
|||
|
|||
/* Container Action buttons */ |
|||
.pos .control-button.main { |
|||
width: 75%; |
|||
} |
|||
.pos .control-button.second { |
|||
width: 15%; |
|||
flex-grow: 0; |
|||
} |
@ -0,0 +1,431 @@ |
|||
/* |
|||
Copyright 2019 Coop IT Easy SCRLfs |
|||
Robin Keunen <robin@coopiteasy.be> |
|||
Pierrick Brun <pierrick.brun@akretion.com> |
|||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|||
*/ |
|||
|
|||
|
|||
odoo.define('pos_container.container', function (require) { |
|||
"use strict"; |
|||
|
|||
var models_and_db = require('pos_container.models_and_db'); |
|||
|
|||
var screens = require('point_of_sale.screens'); |
|||
var gui = require('point_of_sale.gui'); |
|||
var models = require('point_of_sale.models'); |
|||
|
|||
var core = require('web.core'); |
|||
var rpc = require('web.rpc'); |
|||
var QWeb = core.qweb; |
|||
var _t = core._t; |
|||
|
|||
|
|||
var TareButton = screens.ActionButtonWidget.extend({ |
|||
template: 'TareButton', |
|||
}); |
|||
|
|||
screens.define_action_button({ |
|||
'name': 'tare', |
|||
'widget': TareButton, |
|||
}); |
|||
|
|||
screens.NumpadWidget.include({ |
|||
// to put selected-mode on tare button outside the numpadwidget
|
|||
changedMode: function() { |
|||
this._super(); |
|||
if (this.state.get('mode') === 'tare'){ |
|||
$('.mode-button[data-mode="tare"]').addClass('selected-mode'); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
var ContainerButton = screens.ActionButtonWidget.extend({ |
|||
template: 'ContainerButton', |
|||
button_click: function(){ |
|||
this.gui.show_screen('containerlist'); |
|||
} |
|||
}); |
|||
|
|||
screens.define_action_button({ |
|||
'name': 'container', |
|||
'widget': ContainerButton, |
|||
}); |
|||
|
|||
var ContainerListScreenWidget = screens.ScreenWidget.extend({ |
|||
template: 'ContainerListScreenWidget', |
|||
|
|||
init: function(parent, options){ |
|||
this._super(parent, options); |
|||
this.container_cache = new screens.DomCache(); |
|||
}, |
|||
|
|||
show: function(){ |
|||
var self = this; |
|||
this._super(); |
|||
|
|||
this.renderElement(); |
|||
this.$('.back').click(function(){ |
|||
self.gui.back(); |
|||
}); |
|||
|
|||
this.$('.next').click(function(){ |
|||
self.save_changes(); |
|||
self.gui.show_screen('products'); |
|||
}); |
|||
|
|||
this.$('.delete-container').click(function(){ |
|||
if (self.container){ |
|||
self.gui.show_popup('confirm', { |
|||
'title': _t('Container deletion'), |
|||
'body': _t( |
|||
'Do you want to delete this container ?\n').concat( |
|||
self.container.name), |
|||
confirm: function(){ |
|||
self.delete_selected_container(); |
|||
}, |
|||
}); |
|||
} |
|||
}); |
|||
|
|||
var containers = this.pos.db.get_containers_sorted(1000); |
|||
this.render_list(containers); |
|||
|
|||
this.reload_containers(); |
|||
|
|||
this.$('.container-list-contents').delegate('.container-line', |
|||
'click', function(event){ |
|||
self.line_select(event,$(this),$(this).data('id')); |
|||
}); |
|||
|
|||
var search_timeout = null; |
|||
|
|||
if(this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard){ |
|||
this.chrome.widget.keyboard.connect( |
|||
this.$('.searchbox input') |
|||
); |
|||
} |
|||
|
|||
this.$('.searchbox input').on('keyup',function(event){ |
|||
clearTimeout(search_timeout); |
|||
|
|||
var query = this.value; |
|||
|
|||
search_timeout = setTimeout(function(){ |
|||
self.perform_search(query,event.which === 13); |
|||
},70); |
|||
}); |
|||
|
|||
this.$('.searchbox .search-clear').click(function(){ |
|||
self.clear_search(); |
|||
}); |
|||
}, |
|||
delete_selected_container: function(){ |
|||
var self = this; |
|||
|
|||
if (!self.container.id){ |
|||
self.deleted_container(self.container.barcode) |
|||
} |
|||
else { |
|||
rpc.query({ |
|||
model: 'pos.container', |
|||
method: 'unlink', |
|||
args: [self.container.id], |
|||
}).then(function(){ |
|||
self.deleted_container(self.container.barcode); |
|||
},function(err,ev){ |
|||
ev.preventDefault(); |
|||
var error_body = _t('Your Internet connection is probably down.'); |
|||
if (err.data) { |
|||
var except = err.data; |
|||
error_body = except.arguments && except.arguments[0] || except.message || error_body; |
|||
} |
|||
self.gui.show_popup('error',{ |
|||
'title': _t('Error: Could not Save Changes'), |
|||
'body': error_body, |
|||
}); |
|||
} |
|||
); |
|||
} |
|||
}, |
|||
deleted_container: function(barcode){ |
|||
var self = this; |
|||
this.pos.db.remove_containers([barcode]); |
|||
this.$('.container-list .highlight').remove(); |
|||
this.container = null; |
|||
}, |
|||
perform_search: function(query, associate_result){ |
|||
if(query){ |
|||
var containers = this.pos.db.search_container(query); |
|||
if ( associate_result && containers.length === 1){ |
|||
this.container = containers[0]; |
|||
this.save_changes(); |
|||
this.gui.back(); |
|||
} |
|||
this.render_list(containers); |
|||
}else{ |
|||
var containers = this.pos.db.get_containers_sorted(); |
|||
this.render_list(containers); |
|||
} |
|||
}, |
|||
clear_search: function(){ |
|||
var containers = this.pos.db.get_containers_sorted(1000); |
|||
this.render_list(containers); |
|||
this.$('.searchbox input')[0].value = ''; |
|||
this.$('.searchbox input').focus(); |
|||
}, |
|||
render_list: function(containers) { |
|||
var contents = this.$el[0].querySelector('.container-list-contents'); |
|||
contents.innerHTML = ""; |
|||
for(var i = 0, len = Math.min(containers.length,1000); i < len; i++){ |
|||
var container = containers[i]; |
|||
var containerline_html = QWeb.render('ContainerLine',{widget: this, container:containers[i]}); |
|||
var containerline = document.createElement('tbody'); |
|||
containerline.innerHTML = containerline_html; |
|||
containerline = containerline.childNodes[1]; |
|||
|
|||
if(containers === this.container) { |
|||
containerline.classList.add('highlight'); |
|||
} else { |
|||
containerline.classList.remove('highlight'); |
|||
} |
|||
|
|||
contents.appendChild(containerline); |
|||
} |
|||
}, |
|||
save_changes: function(){ |
|||
this.pos.get_order().add_container(this.container); |
|||
}, |
|||
toggle_delete_button: function(){ |
|||
var $button = this.$('.button.delete-container'); |
|||
$button.toggleClass('oe_hidden', !this.container); |
|||
}, |
|||
toggle_save_button: function(){ |
|||
var $button = this.$('.button.next'); |
|||
if (this.container) { |
|||
$button.text('Set Container'); |
|||
} |
|||
$button.toggleClass('oe_hidden', !this.container); |
|||
}, |
|||
line_select: function(event,$line,barcode){ |
|||
var container = this.pos.db.get_container_by_barcode(barcode); |
|||
this.$('.container-list .lowlight').removeClass('lowlight'); |
|||
if ( $line.hasClass('highlight') ){ |
|||
$line.removeClass('highlight'); |
|||
$line.addClass('lowlight'); |
|||
this.toggle_delete_button(); |
|||
this.toggle_save_button(); |
|||
}else{ |
|||
this.$('.container-list .highlight').removeClass('highlight'); |
|||
$line.addClass('highlight'); |
|||
var y = event.pageY - $line.parent().offset().top; |
|||
this.container = container; |
|||
this.toggle_delete_button(); |
|||
this.toggle_save_button(); |
|||
} |
|||
}, |
|||
|
|||
// This fetches container changes on the server, and in case of changes,
|
|||
// rerenders the affected views
|
|||
reload_containers: function(){ |
|||
var self = this; |
|||
return this.pos.load_new_containers().then(function(){ |
|||
// containers may have changed in the backend
|
|||
self.container_cache = new screens.DomCache(); |
|||
|
|||
self.render_list(self.pos.db.get_containers_sorted(1000)); |
|||
|
|||
var last_orderline = self.pos.get_order().get_last_orderline(); |
|||
|
|||
if(last_orderline) { |
|||
// update the currently assigned container if it has been changed in db.
|
|||
var curr_container = last_orderline.get_container(); |
|||
} |
|||
|
|||
if (curr_container) { |
|||
last_orderline.set_container( |
|||
self.pos.db.get_container_by_barcode(curr_container.barcode)); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
close: function(){ |
|||
this._super(); |
|||
if (this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard) { |
|||
this.chrome.widget.keyboard.hide(); |
|||
} |
|||
}, |
|||
|
|||
auto_back: true, |
|||
}); |
|||
|
|||
gui.define_screen({ |
|||
name:'containerlist', |
|||
widget: ContainerListScreenWidget |
|||
}); |
|||
|
|||
// add container barcode scan
|
|||
screens.ScreenWidget.include({ |
|||
barcode_container_action: function(code){ |
|||
var self = this; |
|||
if (self.pos.scan_container(code)) { |
|||
// nothing to do now, the container is added
|
|||
// as an orderline if found.
|
|||
} else { |
|||
self.gui.show_screen('containerscale', {barcode: code.base_code}); |
|||
} |
|||
}, |
|||
show: function(){ |
|||
var self = this; |
|||
this._super(); |
|||
this.pos.barcode_reader.set_action_callback({ |
|||
'container': _.bind(self.barcode_container_action, self), |
|||
}); |
|||
}, |
|||
}); |
|||
|
|||
screens.ProductScreenWidget.include({ |
|||
// to use Tare Button from outside the NumpadWidget
|
|||
start: function(){ |
|||
this._super(); |
|||
var tare_button = $('.mode-button[data-mode="tare"]'); |
|||
tare_button.click(_.bind(this.numpad.clickChangeMode, this.numpad)); |
|||
}, |
|||
// to add a product to a container orderline
|
|||
click_product: function(product) { |
|||
var order = this.pos.get_order(); |
|||
var selected_orderline = order.get_selected_orderline(); |
|||
if (product.to_weight && selected_orderline && |
|||
selected_orderline.product === this.pos.get_container_product()){ |
|||
var container = selected_orderline.get_container(); |
|||
this.gui.show_screen( |
|||
'scale', |
|||
{product: product, |
|||
container: container, |
|||
old_orderline: selected_orderline}); |
|||
} else { |
|||
this._super(product); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
screens.ScaleScreenWidget.include({ |
|||
order_product: function(){ |
|||
// Replace the orderline if the product is the placeholder
|
|||
// container product.
|
|||
var container = this.gui.get_current_screen_param('container'); |
|||
if (container){ |
|||
var order = this.pos.get_order(); |
|||
order.add_product(this.get_product(),{ quantity: this.weight, price: this.price }); |
|||
var orderline = order.get_selected_orderline(); |
|||
orderline.set_container(container); |
|||
var old_orderline = this.gui.get_current_screen_param( |
|||
'old_orderline'); |
|||
if (old_orderline){ |
|||
order.remove_orderline(old_orderline); |
|||
} |
|||
orderline.set_quantity(this.weight); |
|||
orderline.set_gross_weight(this.weight + container.weight); |
|||
orderline.set_tare_mode('AUTO'); |
|||
orderline.trigger('change', orderline); |
|||
} else { |
|||
this._super(); |
|||
var orderline = this.pos.get_order().get_selected_orderline(); |
|||
orderline.set_tare_mode('AUTO'); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
var ContainerScaleScreenWidget = screens.ScaleScreenWidget.extend({ |
|||
template: 'ContainerScaleScreenWidget', |
|||
|
|||
next_screen: 'products', |
|||
previous_screen: 'products', |
|||
|
|||
init: function(parent, options){ |
|||
this._super(parent, options); |
|||
}, |
|||
|
|||
show: function(){ |
|||
this._super(); |
|||
var self = this; |
|||
|
|||
this.$('.next,.add-container').click(function(){ |
|||
self.create_container(); |
|||
}); |
|||
|
|||
if(this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard){ |
|||
this.chrome.widget.keyboard.connect($(this.el.querySelector('.container-name input'))); |
|||
} |
|||
}, |
|||
get_product: function(){ |
|||
return this.pos.get_container_product(); |
|||
}, |
|||
create_container: function(){ |
|||
var self = this; |
|||
var fields = {}; |
|||
|
|||
fields['weight'] = this.weight; |
|||
|
|||
this.$('.container-name .detail').each(function(idx,el){ |
|||
fields['name'] = el.value; |
|||
|
|||
}); |
|||
|
|||
fields.barcode = this.gui.get_current_screen_param('barcode') || false; |
|||
fields.name = fields.name || _t('Container'); |
|||
|
|||
this.pos.push_container(fields).then( |
|||
this.pushed_container(fields["barcode"]) |
|||
); |
|||
}, |
|||
pushed_container: function(barcode){ |
|||
var self = this; |
|||
self.gui.show_screen(self.next_screen); |
|||
}, |
|||
close: function(){ |
|||
this._super(); |
|||
if (this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard) { |
|||
this.chrome.widget.keyboard.hide(); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
gui.define_screen({ |
|||
name:'containerscale', |
|||
widget: ContainerScaleScreenWidget, |
|||
}); |
|||
|
|||
screens.ProductListWidget.include({ |
|||
render_product: function(product){ |
|||
if(product.barcode != 'CONTAINER'){ |
|||
return this._super(product); |
|||
} else { |
|||
return document.createElement('div'); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
screens.OrderWidget.include({ |
|||
set_value: function(val) { |
|||
this._super(val); |
|||
var order = this.pos.get_order(); |
|||
if (order.get_selected_orderline()) { |
|||
var oline = order.get_selected_orderline(); |
|||
var mode = this.numpad_state.get('mode'); |
|||
if( mode === 'tare'){ |
|||
oline.set_tare(val); |
|||
} |
|||
if( mode === 'quantity' && oline.container) { |
|||
oline.set_gross_weight(parseFloat(val) + oline.container.weight); |
|||
} |
|||
oline.set_tare_mode('MAN'); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
return { |
|||
ContainerScaleScreenWidget: ContainerScaleScreenWidget, |
|||
}; |
|||
|
|||
}); |
@ -0,0 +1,619 @@ |
|||
/* |
|||
Copyright 2019 Coop IT Easy SCRLfs |
|||
Pierrick Brun <pierrick.brun@akretion.com> |
|||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|||
*/ |
|||
|
|||
|
|||
odoo.define('pos_container.models_and_db', function (require) { |
|||
"use strict"; |
|||
|
|||
var PosDB = require('point_of_sale.DB'); |
|||
var models = require('point_of_sale.models'); |
|||
var rpc = require('web.rpc'); |
|||
var config = require('web.config'); |
|||
|
|||
var core = require('web.core'); |
|||
var QWeb = core.qweb; |
|||
|
|||
var utils = require('web.utils'); |
|||
var field_utils = require('web.field_utils'); |
|||
var round_di = utils.round_decimals; |
|||
var round_pr = utils.round_precision; |
|||
|
|||
// include not available => extend
|
|||
models.PosModel = models.PosModel.extend({ |
|||
get_container_product: function(){ |
|||
// assign value if not already assigned.
|
|||
// Avoids rewriting init function
|
|||
if (!this.container_product){ |
|||
this.container_product = this.db.get_product_by_barcode( |
|||
'CONTAINER'); |
|||
} |
|||
return this.container_product |
|||
}, |
|||
scan_container: function(parsed_code){ |
|||
var selected_order = this.get_order(); |
|||
var container = this.db.get_container_by_barcode( |
|||
parsed_code.base_code); |
|||
|
|||
if(!container){ |
|||
return false; |
|||
} |
|||
|
|||
selected_order.add_container(container); |
|||
return true; |
|||
}, |
|||
// reload the list of container, returns as a deferred that resolves if there were
|
|||
// updated containers, and fails if not
|
|||
load_new_containers: function(){ |
|||
var self = this; |
|||
var def = new $.Deferred(); |
|||
var fields = _.find(this.models,function(model){ |
|||
return model.model === 'pos.container'; |
|||
}).fields; |
|||
var domain = []; |
|||
rpc.query({ |
|||
model: 'pos.container', |
|||
method: 'search_read', |
|||
args: [domain, fields], |
|||
}, { |
|||
timeout: 3000, |
|||
shadow: true, |
|||
}) |
|||
.then(function(containers){ |
|||
if (self.db.add_containers(containers)) { |
|||
// check if the partners we got were real updates
|
|||
def.resolve(); |
|||
} else { |
|||
def.reject(); |
|||
} |
|||
}, function(type,err){ def.reject(); }); |
|||
return def; |
|||
}, |
|||
// load placeholder product for containers.
|
|||
// it is done here to load it even if inactivated.
|
|||
load_placeholder_product: function(){ |
|||
var self = this; |
|||
var fields = _.find(this.models,function(model){ |
|||
return model.model === 'product.product'; |
|||
}).fields; |
|||
var domain = [['barcode', '=', 'CONTAINER'], ['active', '=', false]]; |
|||
// no need to load it when active because it is already done in standard
|
|||
return rpc.query({ |
|||
model: 'product.product', |
|||
method: 'search_read', |
|||
args: [domain, fields], |
|||
}).then(function(products){ |
|||
self.db.add_products(_.map(products, function (product) { |
|||
return new models.Product({}, product); |
|||
})); |
|||
}); |
|||
}, |
|||
|
|||
// saves the container locally and try to send it to the backend.
|
|||
// it returns a deferred that succeeds after having tried to send the container and all the other pending containers.
|
|||
push_container: function(container, opts) { |
|||
opts = opts || {}; |
|||
var self = this; |
|||
|
|||
if(container){ |
|||
this.db.add_containers([container]); |
|||
} |
|||
|
|||
var pushed = new $.Deferred(); |
|||
|
|||
this.flush_mutex.exec(function(){ |
|||
var flushed = self._save_containers_to_server(self.db.get_containers_sorted(), opts); |
|||
|
|||
flushed.always(function(ids){ |
|||
pushed.resolve(); |
|||
}); |
|||
|
|||
return flushed; |
|||
}); |
|||
return pushed; |
|||
}, |
|||
|
|||
// send an array of containers to the server
|
|||
// available options:
|
|||
// - timeout: timeout for the rpc call in ms
|
|||
// returns a deferred that resolves with the list of
|
|||
// server generated ids for the sent containers
|
|||
_save_containers_to_server: function (containers, options) { |
|||
var self = this; |
|||
var containers= containers.filter(container => !( "id" in container)) |
|||
if (!containers || !containers.length) { |
|||
var result = $.Deferred(); |
|||
result.resolve([]); |
|||
return result; |
|||
} |
|||
|
|||
options = options || {}; |
|||
var timeout = typeof options.timeout === 'number' ? options.timeout : 7500 * containers.length; |
|||
|
|||
return rpc.query({ |
|||
model: 'pos.container', |
|||
method: 'create_from_ui', |
|||
args: containers, |
|||
}, { |
|||
timeout: timeout, |
|||
}) |
|||
.then(function (server_ids) { |
|||
//self.db.remove_containers(containers);
|
|||
_.each(containers, function(container, key){ |
|||
container["id"] = server_ids[key] |
|||
}); |
|||
//self.db.add_containers(containers)
|
|||
self.set('failed',false); |
|||
return server_ids; |
|||
}).fail(function (type, error){ |
|||
if(error.code === 200 ){ // Business Logic Error, not a connection problem
|
|||
//if warning do not need to display traceback!!
|
|||
if (error.data.exception_type == 'warning') { |
|||
delete error.data.debug; |
|||
} |
|||
|
|||
// Hide error if already shown before ...
|
|||
if ((!self.get('failed') || options.show_error) && !options.to_invoice) { |
|||
self.gui.show_popup('error-traceback',{ |
|||
'title': error.data.message, |
|||
'body': error.data.debug |
|||
}); |
|||
} |
|||
self.set('failed',error); |
|||
} |
|||
console.error('Failed to send containers:', containers); |
|||
}); |
|||
}, |
|||
|
|||
// wrapper around the _save_to_server that updates the synch status widget
|
|||
// it is modified to send containers before orders
|
|||
_flush_orders: function(orders, options) { |
|||
var self = this; |
|||
this.set('synch',{ state: 'connecting', pending: orders.length}); |
|||
|
|||
return self._save_containers_to_server(self.db.get_containers_sorted()) |
|||
.then(function(container_ids) { |
|||
for (var i=0; i < orders.length; i++){ |
|||
if (orders[i].data.lines) { |
|||
for (var j=0; j < orders[i].data.lines[0].length; j++){ |
|||
var orderline = orders[i].data.lines[0][j] |
|||
if ( !orderline.container_id && orderline.container_barcode) { |
|||
orderline.container_id = self.db.get_container_by_barcode(orderline.container_barcode).id; |
|||
delete orderline["container_barcode"] |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return self._save_to_server(orders, options) |
|||
}).done(function (server_ids) { |
|||
var pending = self.db.get_orders().length; |
|||
|
|||
self.set('synch', { |
|||
state: pending ? 'connecting' : 'connected', |
|||
pending: pending |
|||
}); |
|||
|
|||
return server_ids; |
|||
}).fail(function(error, event){ |
|||
var pending = self.db.get_orders().length; |
|||
if (self.get('failed')) { |
|||
self.set('synch', { state: 'error', pending: pending }); |
|||
} else { |
|||
self.set('synch', { state: 'disconnected', pending: pending }); |
|||
} |
|||
}); |
|||
}, |
|||
}); |
|||
|
|||
models.Order = models.Order.extend({ |
|||
add_container: function(container, options){ |
|||
if(this._printed){ |
|||
this.destroy(); |
|||
return this.pos.get_order().add_container(container, options); |
|||
} |
|||
options = options || {}; |
|||
var attr = JSON.parse(JSON.stringify(container)); |
|||
attr.pos = this.pos; |
|||
attr.order = this; |
|||
var product = this.pos.get_container_product(); |
|||
var line = new models.Orderline({}, { |
|||
pos: this.pos, order: this, product: product}); |
|||
|
|||
line.set_container(container); |
|||
line.set_quantity(0); |
|||
this.orderlines.add(line); |
|||
|
|||
this.select_orderline(this.get_last_orderline()); |
|||
}, |
|||
has_tare_line: function(mode){ |
|||
var orderlines = this.orderlines.models |
|||
for(var i=0; i < orderlines.length; i++){ |
|||
var line = orderlines[i]; |
|||
if(line && line.get_tare_mode() === mode){ |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
}, |
|||
export_for_printing: function(){ |
|||
var orderlines = []; |
|||
var self = this; |
|||
|
|||
this.orderlines.each(function(orderline){ |
|||
orderlines.push(orderline.export_for_printing()); |
|||
}); |
|||
|
|||
var paymentlines = []; |
|||
this.paymentlines.each(function(paymentline){ |
|||
paymentlines.push(paymentline.export_for_printing()); |
|||
}); |
|||
var client = this.get('client'); |
|||
var cashier = this.pos.get_cashier(); |
|||
var company = this.pos.company; |
|||
var shop = this.pos.shop; |
|||
var date = new Date(); |
|||
|
|||
function is_xml(subreceipt){ |
|||
return subreceipt ? (subreceipt.split('\n')[0].indexOf('<!DOCTYPE QWEB') >= 0) : false; |
|||
} |
|||
|
|||
function render_xml(subreceipt){ |
|||
if (!is_xml(subreceipt)) { |
|||
return subreceipt; |
|||
} else { |
|||
subreceipt = subreceipt.split('\n').slice(1).join('\n'); |
|||
var qweb = new QWeb2.Engine(); |
|||
qweb.debug = config.debug; |
|||
qweb.default_dict = _.clone(QWeb.default_dict); |
|||
qweb.add_template('<templates><t t-name="subreceipt">'+subreceipt+'</t></templates>'); |
|||
|
|||
return qweb.render('subreceipt',{'pos':self.pos,'widget':self.pos.chrome,'order':self, 'receipt': receipt}) ; |
|||
} |
|||
} |
|||
|
|||
var receipt = { |
|||
orderlines: orderlines, |
|||
paymentlines: paymentlines, |
|||
subtotal: this.get_subtotal(), |
|||
total_with_tax: this.get_total_with_tax(), |
|||
total_without_tax: this.get_total_without_tax(), |
|||
total_tax: this.get_total_tax(), |
|||
total_paid: this.get_total_paid(), |
|||
total_discount: this.get_total_discount(), |
|||
tax_details: this.get_tax_details(), |
|||
change: this.get_change(), |
|||
name : this.get_name(), |
|||
client: client ? client.name : null , |
|||
invoice_id: null, //TODO
|
|||
cashier: cashier ? cashier.name : null, |
|||
precision: { |
|||
price: 2, |
|||
money: 2, |
|||
quantity: 3, |
|||
}, |
|||
date: { |
|||
year: date.getFullYear(), |
|||
month: date.getMonth(), |
|||
date: date.getDate(), // day of the month
|
|||
day: date.getDay(), // day of the week
|
|||
hour: date.getHours(), |
|||
minute: date.getMinutes() , |
|||
isostring: date.toISOString(), |
|||
localestring: date.toLocaleString(), |
|||
}, |
|||
company:{ |
|||
email: company.email, |
|||
website: company.website, |
|||
company_registry: company.company_registry, |
|||
contact_address: company.partner_id[1], |
|||
vat: company.vat, |
|||
vat_label: company.country && company.country.vat_label || '', |
|||
name: company.name, |
|||
phone: company.phone, |
|||
logo: this.pos.company_logo_base64, |
|||
}, |
|||
shop:{ |
|||
name: shop.name, |
|||
}, |
|||
currency: this.pos.currency, |
|||
//custom here
|
|||
has_tare_mode: { |
|||
auto: this.has_tare_line('AUTO'), |
|||
manual: this.has_tare_line('MAN'), |
|||
} |
|||
//custom end
|
|||
}; |
|||
|
|||
if (is_xml(this.pos.config.receipt_header)){ |
|||
receipt.header = ''; |
|||
receipt.header_xml = render_xml(this.pos.config.receipt_header); |
|||
} else { |
|||
receipt.header = this.pos.config.receipt_header || ''; |
|||
} |
|||
|
|||
if (is_xml(this.pos.config.receipt_footer)){ |
|||
receipt.footer = ''; |
|||
receipt.footer_xml = render_xml(this.pos.config.receipt_footer); |
|||
} else { |
|||
receipt.footer = this.pos.config.receipt_footer || ''; |
|||
} |
|||
|
|||
return receipt; |
|||
}, |
|||
}); |
|||
|
|||
// Add container to order line
|
|||
models.Orderline = models.Orderline.extend({ |
|||
get_container: function(){ |
|||
return this.container; |
|||
}, |
|||
set_container: function(container){ |
|||
this.container = container; |
|||
}, |
|||
set_tare_mode: function(mode){ |
|||
if (['MAN', 'AUTO'].indexOf(mode) != -1){ |
|||
this.tare_mode = mode; |
|||
this.trigger('change', this); |
|||
} |
|||
}, |
|||
get_tare_mode: function() { |
|||
return this.tare_mode; |
|||
}, |
|||
set_tare: function(tare){ |
|||
this.tare = this.get_value_rounded(tare).toFixed(3); |
|||
this.container = null; |
|||
if (this.gross_weight && this.gross_weight != 'NaN'){ |
|||
this.set_quantity(this.gross_weight - parseFloat(this.tare)); |
|||
} |
|||
else{ |
|||
this.set_gross_weight(this.quantity); |
|||
this.set_quantity(this.quantity - parseFloat(this.tare)); |
|||
} |
|||
this.trigger('change', this); |
|||
}, |
|||
get_tare: function(){ |
|||
return this.tare || 0; |
|||
}, |
|||
get_gross_weight: function(){ |
|||
return this.gross_weight; |
|||
}, |
|||
set_gross_weight: function(weight){ |
|||
this.gross_weight = this.get_value_rounded(weight).toFixed(3); |
|||
this.trigger('change', this); |
|||
}, |
|||
set_quantity: function(quantity, keep_price){ |
|||
// copied from odoo core
|
|||
this.order.assert_editable(); |
|||
if(quantity === 'remove'){ |
|||
this.order.remove_orderline(this); |
|||
return; |
|||
}else{ |
|||
var quant = parseFloat(quantity) || 0; |
|||
var unit = this.get_unit(); |
|||
if(unit){ |
|||
if (unit.rounding) { |
|||
this.quantity = round_pr(quant, unit.rounding); |
|||
var decimals = this.pos.dp['Product Unit of Measure']; |
|||
this.quantity = round_di(this.quantity, decimals) |
|||
this.quantityStr = field_utils.format.float(this.quantity, {digits: [69, decimals]}); |
|||
} else { |
|||
this.quantity = round_pr(quant, 1); |
|||
this.quantityStr = this.quantity.toFixed(0); |
|||
} |
|||
}else{ |
|||
this.quantity = quant; |
|||
this.quantityStr = '' + this.quantity; |
|||
} |
|||
} |
|||
// just like in sale.order changing the quantity will recompute the unit price
|
|||
if(! keep_price && ! this.price_manually_set){ |
|||
this.set_unit_price(this.product.get_price(this.order.pricelist, this.get_quantity())); |
|||
this.order.fix_tax_included_price(this); |
|||
} |
|||
|
|||
// surcharge starts here
|
|||
if (this.tare){ |
|||
this.set_gross_weight(this.quantity + parseFloat(this.tare)); |
|||
} |
|||
this.trigger('change', this); |
|||
}, |
|||
get_value_rounded: function(value){ |
|||
var value = parseFloat(value) || 0; |
|||
var unit = this.get_unit(); |
|||
if(unit){ |
|||
if (unit.rounding) { |
|||
value = round_pr(value, unit.rounding); |
|||
var decimals = this.pos.dp['Product Unit of Measure']; |
|||
value = round_di(value, decimals) |
|||
} else { |
|||
value = round_pr(value, 1); |
|||
} |
|||
} |
|||
return value; |
|||
}, |
|||
export_as_JSON: function(){ |
|||
var pack_lot_ids = []; |
|||
if (this.has_product_lot){ |
|||
this.pack_lot_lines.each(_.bind( function(item) { |
|||
return pack_lot_ids.push([0, 0, item.export_as_JSON()]); |
|||
}, this)); |
|||
} |
|||
return { |
|||
qty: this.get_quantity(), |
|||
price_unit: this.get_unit_price(), |
|||
price_subtotal: this.get_price_without_tax(), |
|||
price_subtotal_incl: this.get_price_with_tax(), |
|||
discount: this.get_discount(), |
|||
product_id: this.get_product().id, |
|||
tax_ids: [[6, false, _.map(this.get_applicable_taxes(), function(tax){ return tax.id; })]], |
|||
id: this.id, |
|||
pack_lot_ids: pack_lot_ids, |
|||
//custom starts here
|
|||
tare: this.get_tare() ? this.get_tare() : null, |
|||
container_id: this.get_container() ? this.get_container().id : null, |
|||
container_barcode: this.get_container() ? this.get_container().barcode : null, |
|||
container_weight: this.get_container() ? this.get_container().weight : null, |
|||
}; |
|||
}, |
|||
//used to create a json of the ticket, to be sent to the printer
|
|||
export_for_printing: function(){ |
|||
return { |
|||
quantity: this.get_quantity(), |
|||
unit_name: this.get_unit().name, |
|||
price: this.get_unit_price(), |
|||
discount: this.get_discount(), |
|||
product_name: this.get_product().display_name, |
|||
product_name_wrapped: this.generate_wrapped_product_name(), |
|||
price_display : this.get_display_price(), |
|||
price_with_tax : this.get_price_with_tax(), |
|||
price_without_tax: this.get_price_without_tax(), |
|||
tax: this.get_tax(), |
|||
product_description: this.get_product().description, |
|||
product_description_sale: this.get_product().description_sale, |
|||
// extension starts here
|
|||
container: this.get_container(), |
|||
tare: this.get_tare(), |
|||
tare_mode: this.get_tare_mode(), |
|||
gross_weight: this.get_gross_weight(), |
|||
product_barcode: this.get_product().barcode, |
|||
}; |
|||
}, |
|||
}); |
|||
|
|||
PosDB.include({ |
|||
init: function(parent, options) { |
|||
this._super(parent, options); |
|||
|
|||
this.container_sorted = []; |
|||
this.container_by_id = {}; |
|||
this.container_by_barcode = {}; |
|||
this.container_search_string = ""; |
|||
this.container_write_date = null; |
|||
}, |
|||
_container_search_string: function(container){ |
|||
|
|||
var str = ''; |
|||
|
|||
if(container.barcode){ |
|||
str += '|' + container.barcode; |
|||
} |
|||
if(container.name) { |
|||
str += '|' + container.name; |
|||
} |
|||
var id = container.id || 0; |
|||
str = '' + id + ':' + str.replace(':','') + '\n'; |
|||
|
|||
return str; |
|||
}, |
|||
add_containers: function(containers) { |
|||
var updated_count = 0; |
|||
var new_write_date = ''; |
|||
for(var i = 0, len = containers.length; i < len; i++) { |
|||
var container = containers[i]; |
|||
|
|||
if (this.container_write_date && |
|||
this.container_by_barcode[container.barcode] && |
|||
new Date(this.container_write_date).getTime() + 1000 >= |
|||
new Date(container.write_date).getTime() ) { |
|||
// FIXME: The write_date is stored with milisec precision in the database
|
|||
// but the dates we get back are only precise to the second. This means when
|
|||
// you read containers modified strictly after time X, you get back containers that were
|
|||
// modified X - 1 sec ago.
|
|||
continue; |
|||
} else if ( new_write_date < container.write_date ) { |
|||
new_write_date = container.write_date; |
|||
} |
|||
if (!this.container_by_barcode[container.barcode]) { |
|||
this.container_sorted.push(container.barcode); |
|||
} |
|||
this.container_by_barcode[container.barcode] = container; |
|||
|
|||
updated_count += 1; |
|||
} |
|||
|
|||
this.container_write_date = new_write_date || this.container_write_date; |
|||
|
|||
if (updated_count) { |
|||
// If there were updates, we need to completely
|
|||
// rebuild the search string and the id indexing
|
|||
|
|||
this.container_search_string = ""; |
|||
this.container_by_id = {}; |
|||
|
|||
for (var barcode in this.container_by_barcode) { |
|||
var container = this.container_by_barcode[barcode]; |
|||
|
|||
if(container.id){ |
|||
this.container_by_id[container.id] = container; |
|||
} |
|||
this.container_search_string += this._container_search_string(container); |
|||
} |
|||
} |
|||
return updated_count; |
|||
}, |
|||
remove_containers: function(barcodes){ |
|||
for(var i = 0; i < barcodes.length; i++) { |
|||
var container = this.container_by_barcode[barcodes[i]]; |
|||
if (container){ |
|||
var index_s = this.container_sorted.indexOf(container.barcode); |
|||
this.container_sorted.splice(index_s, 1); |
|||
delete this.container_by_id[container.id]; |
|||
delete this.container_by_barcode[container.barcode]; |
|||
} |
|||
} |
|||
}, |
|||
get_container_write_date: function(){ |
|||
return this.container_write_date; |
|||
}, |
|||
get_container_by_id: function(id){ |
|||
return this.container_by_id[id]; |
|||
}, |
|||
get_container_by_barcode: function(barcode){ |
|||
return this.container_by_barcode[barcode]; |
|||
}, |
|||
get_containers_sorted: function(max_count){ |
|||
max_count = max_count ? Math.min(this.container_sorted.length, max_count) : this.container_sorted.length; |
|||
var containers = []; |
|||
for (var i = 0; i < max_count; i++) { |
|||
containers.push(this.container_by_barcode[this.container_sorted[i]]); |
|||
} |
|||
|
|||
return containers; |
|||
}, |
|||
search_container: function(query) { |
|||
try { |
|||
query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.'); |
|||
query = query.replace(' ','.+'); |
|||
var re = RegExp("([0-9]+):\\|([0-9]*"+query+"[0-9]*\\|\|[0-9]*\\|.*?"+query+")","gi"); |
|||
} catch(e) { |
|||
return []; |
|||
} |
|||
var results = []; |
|||
for(var i = 0; i < this.limit; i++) { |
|||
var r = re.exec(this.container_search_string); |
|||
if(r) { |
|||
// r[1] = id, r[2] = barcode
|
|||
var barcode = r[2].substring(0, r[2].indexOf("\|")); |
|||
results.push(this.get_container_by_barcode(barcode)); |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return results; |
|||
}, |
|||
|
|||
}); |
|||
|
|||
models.load_models({ |
|||
model: 'pos.container', |
|||
fields: ['name','barcode', 'weight'], |
|||
loaded: function(self, containers){ |
|||
self.db.add_containers(containers); |
|||
return self.load_placeholder_product(); |
|||
}, |
|||
}); |
|||
|
|||
}); |
@ -0,0 +1,244 @@ |
|||
odoo.define('pos_container.tour.tare', function (require) { |
|||
"use strict"; |
|||
|
|||
var Tour = require('web_tour.tour'); |
|||
|
|||
function click_numpad(num) { |
|||
return { |
|||
content: "click on numpad button '" + num + "'", |
|||
trigger: ".input-button.number-char:contains('"+num+"')" |
|||
} |
|||
} |
|||
|
|||
function scan(barcode) { |
|||
return { |
|||
content: "Scanning barcode " + barcode, |
|||
trigger: "input.ean", |
|||
run: "text " + barcode |
|||
} |
|||
} |
|||
|
|||
function confirm_scan() { |
|||
return { |
|||
content: "Confirm barcode", |
|||
trigger: ".button.barcode", |
|||
} |
|||
} |
|||
|
|||
function set_weight(weight) { |
|||
return { |
|||
content: "Setting weight " + weight, |
|||
trigger: "input.weight", |
|||
run: "text " + weight |
|||
} |
|||
} |
|||
|
|||
function confirm_weight() { |
|||
return { |
|||
content: "Confirm weight", |
|||
trigger: ".button.set_weight", |
|||
} |
|||
} |
|||
|
|||
function check_selected_orderline(message, check) { |
|||
return { |
|||
content: message, |
|||
trigger: ".orderline.selected " + check, |
|||
run: function () {}, // it's a check
|
|||
} |
|||
} |
|||
|
|||
|
|||
var steps = [{ |
|||
content: 'waiting for loading to finish', |
|||
trigger: '.o_main_content:has(.loader:hidden)', |
|||
run: function () {}, |
|||
}, |
|||
scan('0499999999998'), |
|||
confirm_scan(), |
|||
set_weight(0.1), |
|||
confirm_weight(), |
|||
{ |
|||
content: "Click on save", |
|||
trigger: ".add-container", |
|||
}, |
|||
// Test a second time with a custom name
|
|||
scan('0499999999981'), |
|||
confirm_scan(), |
|||
set_weight(0.2), |
|||
confirm_weight(), |
|||
{ |
|||
content: "Set a custom name", |
|||
trigger:"input.container-name", |
|||
run:"text TOTO", |
|||
}, { |
|||
content: "Click on save", |
|||
trigger: ".add-container", |
|||
}, |
|||
// Scan du premier contenant
|
|||
scan('0499999999998'), |
|||
confirm_scan(), |
|||
check_selected_orderline("Check: empty container in the orderline", ".product-name:contains('Container without product')"), |
|||
check_selected_orderline("Check: the name is 'Container'", ".info:contains('Container')"), |
|||
check_selected_orderline("Check: the quantity is 0", ".info em:contains('0.000')"), |
|||
{ |
|||
content: "select product", |
|||
trigger: ".product:contains('Whiteboard Pen')", //UoM = kg
|
|||
}, |
|||
set_weight(0.2), |
|||
confirm_weight(), |
|||
{ |
|||
content: "validate weight", |
|||
trigger: ".buy-product", |
|||
}, |
|||
check_selected_orderline("Check: the name is 'Container'", ".info:contains('Container')"), |
|||
check_selected_orderline("Check: orderline in AUTO tare mode", ".pos-right-align:contains('AUTO')"), |
|||
check_selected_orderline("Check: orderline's product is the Pen", ".product-name:contains('Whiteboard Pen')"), |
|||
check_selected_orderline("Check: the quantity is the tared weight", ".info:contains('0.200')"), |
|||
{ |
|||
content: "click container button", |
|||
trigger: ".control-button.o_container_button", |
|||
}, { |
|||
content: "Search Container TOTO", |
|||
trigger: ".searchbox input", |
|||
run: "text TOTO", |
|||
}, { |
|||
content: "Select container TOTO", |
|||
trigger: ".container-line:contains('TOTO')", |
|||
}, { |
|||
content: "Click delete", |
|||
trigger: ".button.delete-container", |
|||
}, { |
|||
content: "Click cancel", |
|||
trigger: ".button.cancel", |
|||
}, { |
|||
content: "Click delete", |
|||
trigger: ".button.delete-container", |
|||
}, { |
|||
content: "Click confirm", |
|||
trigger: ".button.confirm", |
|||
}, { |
|||
content: "Search by barcode", |
|||
trigger: ".searchbox input", |
|||
run: "text 0499999999998", |
|||
}, { |
|||
content: "select the searched container", |
|||
trigger: ".container-line:contains('Container')", |
|||
}, { |
|||
content: "confirm selection", |
|||
trigger: ".containerlist-screen .next", |
|||
}, { |
|||
content: "remove orderline quantity", |
|||
trigger: ".input-button.numpad-backspace", |
|||
}, { |
|||
content: "delete orderline", |
|||
trigger: ".input-button.numpad-backspace", |
|||
}, { |
|||
content: "select another product", |
|||
trigger: ".product:contains('Desk Organizer')", //UoM = kg
|
|||
}, |
|||
set_weight(0.5), |
|||
confirm_weight(), |
|||
{ |
|||
content: "confirm purchase", |
|||
trigger: ".buy-product", |
|||
}, { |
|||
content: "switch numpad to tare mode", |
|||
trigger: ".control-button.o_tare_button", |
|||
}, |
|||
click_numpad(0), |
|||
click_numpad('.'), |
|||
click_numpad(2), |
|||
check_selected_orderline("Check: orderline in MAN tare mode", ".pos-right-align:contains('MAN')"), |
|||
check_selected_orderline("Check: orderline's product is the Organizer", ".product-name:contains('Desk Organizer')"), |
|||
check_selected_orderline("Check: the quantity is the tared weight", ".info:contains('0.300')"), |
|||
{ |
|||
content: "click orderline auto", |
|||
trigger: ".orderline .pos-right-align:contains('AUTO')", |
|||
}, { |
|||
content: "switch numpad to tare mode", |
|||
trigger: ".control-button.o_tare_button", |
|||
}, |
|||
click_numpad(0), |
|||
click_numpad('.'), |
|||
click_numpad(2), |
|||
check_selected_orderline("Check: orderline in MAN tare mode", ".pos-right-align:contains('MAN')"), |
|||
check_selected_orderline("Check: orderline's product is the Pen", ".product-name:contains('Whiteboard Pen')"), |
|||
check_selected_orderline("Check: the quantity is the tared weight", ".info em:contains('0.100')"), |
|||
{ |
|||
content: "switch numpad to quantity mode", |
|||
trigger: ".mode-button[data-mode='quantity']", |
|||
}, |
|||
click_numpad(0), |
|||
click_numpad('.'), |
|||
click_numpad(6), |
|||
check_selected_orderline("Check: orderline in MAN tare mode", ".pos-right-align:contains('MAN')"), |
|||
check_selected_orderline("Check: orderline's product is the Pen", ".product-name:contains('Whiteboard Pen')"), |
|||
check_selected_orderline("Check: the quantity is 0.6", ".info em:contains('0.600')"), |
|||
check_selected_orderline("Check: the tare is unchanged", ".info:contains('0.2')"), |
|||
check_selected_orderline("Check: the gross weight is the tare + the quantity", ".info:contains('Gross : 0.8 kg')"), |
|||
{ |
|||
content: "Add a unit product", |
|||
trigger: ".product:contains('Large Cabinet')", |
|||
}, { |
|||
content: "click discount", |
|||
trigger: ".mode-button[data-mode='discount']", |
|||
}, |
|||
click_numpad(1), |
|||
click_numpad(0), |
|||
check_selected_orderline("Check: orderline in MAN tare mode", ".pos-right-align:contains('MAN')"), |
|||
check_selected_orderline("Check: the undiscounted price is still 320", ".info:contains('320.00')"), |
|||
{ |
|||
content: "Add a unit product", |
|||
trigger: ".product:contains('Large Cabinet')", |
|||
}, { |
|||
content: "click price change", |
|||
trigger: ".mode-button[data-mode='price']", |
|||
}, |
|||
click_numpad(2), |
|||
click_numpad(0), |
|||
click_numpad(0), |
|||
check_selected_orderline("Check: orderline in MAN tare mode", ".pos-right-align:contains('MAN')"), |
|||
// Ajouter une ligne en AUTO
|
|||
scan('0499999999998'), |
|||
confirm_scan(), |
|||
{ |
|||
content: "select product", |
|||
trigger: ".product:contains('Whiteboard Pen')", //UoM = kg
|
|||
}, |
|||
set_weight(0.3), |
|||
confirm_weight(), |
|||
{ |
|||
content: "validate weight", |
|||
trigger: ".buy-product", |
|||
}, |
|||
{ |
|||
content: "pay", |
|||
trigger: ".button.pay", |
|||
}, |
|||
click_numpad(2), |
|||
click_numpad(0), |
|||
click_numpad(0), |
|||
click_numpad(0), |
|||
{ |
|||
content: "validate", |
|||
trigger: ".button.next", |
|||
}]; |
|||
|
|||
|
|||
var autre = [{ |
|||
content: "relancer POS", |
|||
trigger: "", |
|||
}, { |
|||
content: "click commandes", |
|||
trigger: "", |
|||
}, { |
|||
content: "sélectionner dernière commande", |
|||
trigger: "", |
|||
}, { |
|||
content: "click reprint", |
|||
trigger: "", |
|||
}]; |
|||
|
|||
Tour.register('pos_container', { test: true, url: '/pos/web' }, steps); |
|||
}); |
@ -0,0 +1,442 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<templates id="template" xml:space="preserve"> |
|||
|
|||
<t t-name="ContainerButton"> |
|||
<button class="control-button main o_container_button"> |
|||
<i class="fa fa-beer" role="img" aria-label="Container" title="Container"/> |
|||
Container |
|||
</button> |
|||
</t> |
|||
|
|||
<t t-name="TareButton"> |
|||
<button class="control-button second mode-button o_tare_button" data-mode="tare"> |
|||
Tare |
|||
</button> |
|||
</t> |
|||
|
|||
<t t-name="ContainerListScreenWidget"> |
|||
<div class="containerlist-screen screen"> |
|||
<div class="screen-content"> |
|||
<section class="top-content"> |
|||
<span class="button back"> |
|||
<i class="fa fa-angle-double-left"/> |
|||
Cancel |
|||
</span> |
|||
<span class="searchbox"> |
|||
<input placeholder="Search Containers"/> |
|||
<span class="search-clear"/> |
|||
</span> |
|||
<span class="searchbox"/> |
|||
<span class="button delete-container oe_hidden" role="img" aria-label="Delete container" title="Delete container"> |
|||
<i class="fa fa-beer"/> |
|||
<i class="fa fa-trash"/> |
|||
</span> |
|||
<span class="button next oe_hidden highlight"> |
|||
Select a container |
|||
<i class="fa fa-angle-double-right"/> |
|||
</span> |
|||
</section> |
|||
<section class="full-content"> |
|||
<div class="window"> |
|||
<section class="subwindow"> |
|||
<div class="subwindow-container"> |
|||
<div class="subwindow-container-fix touch-scrollable scrollable-y"> |
|||
<table class="container-list"> |
|||
<thead> |
|||
<tr> |
|||
<th>Name</th> |
|||
<th>Barcode</th> |
|||
<th>Weight</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody class="container-list-contents"> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
</div> |
|||
</section> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
|
|||
<t t-name="ContainerLine"> |
|||
<tr class="container-line" t-att-data-id="container.barcode"> |
|||
<td> |
|||
<t t-esc="container.name"/> |
|||
</td> |
|||
<td> |
|||
<t t-esc="container.barcode"/> |
|||
</td> |
|||
<td> |
|||
<t t-esc="container.weight"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
|
|||
<t t-extend='Orderline'> |
|||
<t t-jquery='.info-list:last-child' t-operation='prepend'> |
|||
<t t-if="line.get_container()"> |
|||
<ul class="info-list"> |
|||
<ul class="info-list"> |
|||
<i><t t-if="line.get_gross_weight()"> |
|||
<li class="info"> |
|||
Gross : <t t-esc="line.get_gross_weight()"/> kg |
|||
</li> |
|||
</t> |
|||
<li class="info"> |
|||
<i class='fa fa-beer'/> : |
|||
<t t-esc="line.get_container().weight"/> kg |
|||
- |
|||
<t t-esc="line.get_container().name"/> |
|||
</li></i> |
|||
</ul> |
|||
</ul> |
|||
</t> |
|||
<t t-if="line.get_tare()"> |
|||
<ul class="info-list"> |
|||
<ul class="info-list"> |
|||
<i><t t-if="line.get_gross_weight()"> |
|||
<li class="info"> |
|||
Gross : <t t-esc="line.get_gross_weight()"/> kg |
|||
</li> |
|||
</t> |
|||
<li class="info"> |
|||
<i class='fa fa-beer'/> : |
|||
<t t-esc="line.get_tare()"/> kg |
|||
- Manual tare |
|||
</li></i> |
|||
</ul> |
|||
</ul> |
|||
</t> |
|||
<div class="pos-right-align"> |
|||
<t t-esc="line.get_tare_mode()"/> |
|||
</div> |
|||
</t> |
|||
</t> |
|||
|
|||
<t t-name="ContainerScaleScreenWidget"> |
|||
<div class="scale-screen screen"> |
|||
<div class="screen-content"> |
|||
<div class="top-content"> |
|||
<span class='button back'> |
|||
<i class='fa fa-angle-double-left'></i> |
|||
Back |
|||
</span> |
|||
<h1 class='product-name'>Add a container</h1> |
|||
</div> |
|||
<div class="centered-content"> |
|||
<div class='weight js-weight'> |
|||
<t t-esc="widget.get_product_weight_string()" /> |
|||
</div> |
|||
<div class='container-name'> |
|||
<span class='label'>Container name: </span><br/> |
|||
<input class='detail container-name' name='container_name' placeholder="Container"></input> |
|||
</div> |
|||
<div class='add-container'> |
|||
Save |
|||
<i class='fa fa-angle-double-right'></i> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
|
|||
<t t-extend="PosTicket"> |
|||
<t t-jquery='.receipt-orderlines' t-operation='inner'> |
|||
<table> |
|||
<colgroup> |
|||
<col width='50%' /> |
|||
<col width='25%' /> |
|||
<col width='25%' /> |
|||
</colgroup> |
|||
<tr t-foreach="orderlines" t-as="orderline"> |
|||
<t t-if="orderline.get_tare_mode() == undefined |
|||
and orderline.product.barcode != 'CONTAINER'"> |
|||
<td> |
|||
<t t-esc="orderline.get_product().display_name"/> |
|||
<t t-if="orderline.get_discount() > 0"> |
|||
<div class="pos-disc-font"> |
|||
With a <t t-esc="orderline.get_discount()"/>% discount |
|||
</div> |
|||
</t> |
|||
</td> |
|||
<td class="pos-right-align"> |
|||
<t t-esc="orderline.get_quantity_str_with_unit()"/> |
|||
</td> |
|||
<td class="pos-right-align"> |
|||
<t t-esc="widget.format_currency(orderline.get_display_price())"/> |
|||
</td> |
|||
</t> |
|||
</tr> |
|||
</table> |
|||
<t t-if="order.has_tare_line('AUTO')"> |
|||
<table> |
|||
<colgroup> |
|||
<col width='20%' /> |
|||
<col width='60%' /> |
|||
<col width='20%' /> |
|||
</colgroup> |
|||
<td></td> |
|||
<td> |
|||
<center>------------------------</center> |
|||
<center>Automatic Weighing</center> |
|||
<center>------------------------</center> |
|||
</td> |
|||
</table> |
|||
</t> |
|||
<table> |
|||
<colgroup> |
|||
<col width='50%' /> |
|||
<col width='25%' /> |
|||
<col width='25%' /> |
|||
</colgroup> |
|||
<tr t-foreach="orderlines" t-as="orderline"> |
|||
<t t-if="orderline.get_tare_mode() == 'AUTO'"> |
|||
<tr> |
|||
<td> |
|||
<t t-esc="orderline.get_product().display_name"/> |
|||
<t t-if="orderline.get_discount() > 0"> |
|||
<div class="pos-disc-font"> |
|||
With a <t t-esc="orderline.get_discount()"/>% discount |
|||
</div> |
|||
</t> |
|||
</td> |
|||
<td/> |
|||
<td class="pos-right-align"> |
|||
<t t-esc="widget.format_currency(orderline.get_display_price())"/> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td> |
|||
<div style="margin-left:5%"> |
|||
<t t-esc="orderline.get_quantity_str_with_unit()"/> |
|||
x |
|||
<t t-esc="widget.format_currency(orderline.get_unit_display_price())"/>/<t t-esc="orderline.get_unit().name"/> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
<t t-if="orderline.get_container()"> |
|||
<tr> |
|||
<td> |
|||
<div style="margin-left:5%"> |
|||
Tare : <t t-esc="orderline.get_container().weight"/> |
|||
<t t-esc="orderline.get_unit().name"/> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
<t t-if="orderline.get_tare()"> |
|||
<tr> |
|||
<td> |
|||
<div style="margin-left:5%"> |
|||
Tare : <t t-esc="orderline.get_tare()"/> |
|||
<t t-esc="orderline.get_unit().name"/> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</t> |
|||
</tr> |
|||
</table> |
|||
<t t-if="order.has_tare_line('MAN')"> |
|||
<table> |
|||
<colgroup> |
|||
<col width='20%' /> |
|||
<col width='60%' /> |
|||
<col width='20%' /> |
|||
</colgroup> |
|||
<td></td> |
|||
<td> |
|||
<center>------------------------</center> |
|||
<center>Manual Input</center> |
|||
<center>------------------------</center> |
|||
</td> |
|||
</table> |
|||
</t> |
|||
<table> |
|||
<colgroup> |
|||
<col width='50%' /> |
|||
<col width='25%' /> |
|||
<col width='25%' /> |
|||
</colgroup> |
|||
<tr t-foreach="orderlines" t-as="orderline"> |
|||
<t t-if="orderline.get_tare_mode() == 'MAN'"> |
|||
<tr> |
|||
<td> |
|||
<t t-esc="orderline.get_product().display_name"/> |
|||
<t t-if="orderline.get_discount() > 0"> |
|||
<div class="pos-disc-font"> |
|||
With a <t t-esc="orderline.get_discount()"/>% discount |
|||
</div> |
|||
</t> |
|||
</td> |
|||
<td class="pos-right-align"> |
|||
<t t-esc="orderline.get_quantity_str_with_unit()"/> |
|||
</td> |
|||
<td class="pos-right-align"> |
|||
<t t-esc="widget.format_currency(orderline.get_display_price())"/> |
|||
</td> |
|||
</tr> |
|||
<t t-if="orderline.get_tare()"> |
|||
<tr> |
|||
<td> |
|||
<div style="margin-left:5%"> |
|||
Tare : <t t-esc="orderline.tare"/> |
|||
<t t-esc="orderline.get_unit().name"/> |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</t> |
|||
</tr> |
|||
</table> |
|||
</t> |
|||
</t> |
|||
|
|||
<t t-extend="XmlReceipt"> |
|||
<t t-jquery='.orderlines' t-operation='inner'> |
|||
<t t-foreach='receipt.orderlines' t-as='line'> |
|||
<t t-if="line.tare_mode == undefined and line.product_barcode != 'CONTAINER'"> |
|||
<t t-set='simple' t-value='line.discount === 0 and line.unit_name === "Unit(s)" and line.quantity === 1' /> |
|||
<t t-if='simple'> |
|||
<line> |
|||
<left><t t-esc='line.product_name_wrapped[0]' /></left> |
|||
<right><value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value></right> |
|||
</line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
</t> |
|||
<t t-if='!simple'> |
|||
<line><left><t t-esc='line.product_name_wrapped[0]' /></left></line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
<t t-if='line.discount !== 0'> |
|||
<line indent='1'><left>Discount: <t t-esc='line.discount' />%</left></line> |
|||
</t> |
|||
<line indent='1'> |
|||
<left> |
|||
<value t-att-value-decimals='pos.dp["Product Unit of Measure"]' value-autoint='on'> |
|||
<t t-esc='line.quantity' /> |
|||
</value> |
|||
<t t-if='line.unit_name !== "Unit(s)"'> |
|||
<t t-esc='line.unit_name' /> |
|||
</t> |
|||
x |
|||
<value t-att-value-decimals='pos.dp["Product Price"]'> |
|||
<t t-esc='line.price' /> |
|||
</value> |
|||
</left> |
|||
<right> |
|||
<value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value> |
|||
</right> |
|||
</line> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
<div>------------------</div> |
|||
<t t-if="receipt.has_tare_mode.auto"> |
|||
Automatic Weighing |
|||
<div>------------------</div> |
|||
<t t-foreach='receipt.orderlines' t-as='line'> |
|||
<t t-if="line.tare_mode == 'AUTO'"> |
|||
<t t-set='simple' t-value='line.discount === 0 and line.unit_name === "Unit(s)" and line.quantity === 1' /> |
|||
<t t-if='simple'> |
|||
<line> |
|||
<left><t t-esc='line.product_name_wrapped[0]' /></left> |
|||
<right><value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value></right> |
|||
</line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
</t> |
|||
<t t-if='!simple'> |
|||
<line><left><t t-esc='line.product_name_wrapped[0]' /></left></line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
<t t-if='line.discount !== 0'> |
|||
<line indent='1'><left>Discount: <t t-esc='line.discount' />%</left></line> |
|||
</t> |
|||
<line indent='1'> |
|||
<left> |
|||
<value t-att-value-decimals='pos.dp["Product Unit of Measure"]' value-autoint='on'> |
|||
<t t-esc='line.quantity' /> |
|||
</value> |
|||
<t t-if='line.unit_name !== "Unit(s)"'> |
|||
<t t-esc='line.unit_name' /> |
|||
</t> |
|||
x |
|||
<value t-att-value-decimals='pos.dp["Product Price"]'> |
|||
<t t-esc='line.price' /> |
|||
</value> |
|||
</left> |
|||
<right> |
|||
<value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value> |
|||
</right> |
|||
</line> |
|||
<t t-if='line.container'> |
|||
<line indent='1'> |
|||
<left> |
|||
Tare : <t t-esc='line.container.weight' /><t t-esc='line.unit_name' /> |
|||
</left> |
|||
</line> |
|||
</t> |
|||
<t t-if='line.tare'> |
|||
<line indent='1'> |
|||
<left> |
|||
Tare : <t t-esc='line.tare' /><t t-esc='line.unit_name' /> |
|||
</left> |
|||
</line> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
<div>------------------</div> |
|||
</t> |
|||
<t t-if="receipt.has_tare_mode.manual"> |
|||
Manual Input |
|||
<div>------------------</div> |
|||
<t t-foreach='receipt.orderlines' t-as='line'> |
|||
<t t-if="line.tare_mode == 'MAN'"> |
|||
<t t-set='simple' t-value='line.discount === 0 and line.unit_name === "Unit(s)" and line.quantity === 1' /> |
|||
<t t-if='simple'> |
|||
<line> |
|||
<left><t t-esc='line.product_name_wrapped[0]' /></left> |
|||
<right><value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value></right> |
|||
</line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
</t> |
|||
<t t-if='!simple'> |
|||
<line><left><t t-esc='line.product_name_wrapped[0]' /></left></line> |
|||
<t t-call="XmlReceiptWrappedProductNameLines"/> |
|||
<t t-if='line.discount !== 0'> |
|||
<line indent='1'><left>Discount: <t t-esc='line.discount' />%</left></line> |
|||
</t> |
|||
<line indent='1'> |
|||
<left> |
|||
<value t-att-value-decimals='pos.dp["Product Unit of Measure"]' value-autoint='on'> |
|||
<t t-esc='line.quantity' /> |
|||
</value> |
|||
<t t-if='line.unit_name !== "Unit(s)"'> |
|||
<t t-esc='line.unit_name' /> |
|||
</t> |
|||
x |
|||
<value t-att-value-decimals='pos.dp["Product Price"]'> |
|||
<t t-esc='line.price' /> |
|||
</value> |
|||
</left> |
|||
<right> |
|||
<value t-att-value-decimals='pos.currency.decimals'><t t-esc='line.price_display' /></value> |
|||
</right> |
|||
</line> |
|||
<t t-if='line.tare'> |
|||
<line indent='1'> |
|||
<left> |
|||
Tare : <t t-esc='line.tare' /><t t-esc='line.unit_name' /> |
|||
</left> |
|||
</line> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
|
|||
</templates> |
@ -0,0 +1,17 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
Copyright 2019 Coop IT Easy SCRLfs |
|||
Robin Keunen <robin@coopiteasy.be> |
|||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
--> |
|||
|
|||
<odoo> |
|||
<template id="assets_frontend" inherit_id="point_of_sale.assets"> |
|||
<xpath expr="." position="inside"> |
|||
<link href="/pos_container/static/src/css/container.css" rel="stylesheet"/> |
|||
<script type="text/javascript" src="/pos_container/static/src/js/models_and_db.js"/> |
|||
<script type="text/javascript" src="/pos_container/static/src/js/container.js"/> |
|||
<script type="text/javascript" src="/pos_container/static/src/js/tests.js"/> |
|||
</xpath> |
|||
</template> |
|||
</odoo> |
@ -0,0 +1 @@ |
|||
from . import test_pos_container |
@ -0,0 +1,14 @@ |
|||
import odoo.tests |
|||
|
|||
|
|||
@odoo.tests.tagged('pos_install', '-at-install') |
|||
class TestUi(odoo.tests.HttpCase): |
|||
|
|||
def test_01_pos_container_tour(self): |
|||
self.phantom_js( |
|||
"/web", |
|||
"odoo.__DEBUG__.services['web_tour.tour']" + |
|||
".run('pos_container_tour')", |
|||
"odoo.__DEBUG__.services['web_tour.tour']" + |
|||
".tours.pos_container_tour.ready", |
|||
login="admin") |
@ -0,0 +1,43 @@ |
|||
<odoo> |
|||
<record id="pos_container_tree_view" model="ir.ui.view"> |
|||
<field name="name">pos.container.tree</field> |
|||
<field name="model">pos.container</field> |
|||
<field name="arch" type="xml"> |
|||
<tree> |
|||
<field name="name"/> |
|||
<field name="barcode"/> |
|||
<field name="weight"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="pos_container_form_view" model="ir.ui.view"> |
|||
<field name="name">pos.container.form</field> |
|||
<field name="model">pos.container</field> |
|||
<field name="arch" type="xml"> |
|||
<form> |
|||
<sheet> |
|||
<group> |
|||
<field name="name"/> |
|||
<field name="barcode"/> |
|||
<field name="weight"/> |
|||
</group> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="pos_container_action_window" model="ir.actions.act_window" > |
|||
<field name="name">Containers</field> |
|||
<field name="res_model">pos.container</field> |
|||
<field name="view_mode">tree,form</field> |
|||
</record> |
|||
|
|||
<menuitem id="pos_container_menu" |
|||
name="Containers" |
|||
parent="point_of_sale.pos_config_menu_catalog" |
|||
sequence="13" |
|||
action="pos_container_action_window" |
|||
groups="point_of_sale.group_pos_manager,point_of_sale.group_pos_user" |
|||
/> |
|||
</odoo> |
@ -0,0 +1,3 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
from . import models |
@ -0,0 +1,33 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Vincent Van Rossem <vvrossem@gmail.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
{ |
|||
'name': 'Currency Management Extension to POS Customer Display', |
|||
|
|||
'version': '12.0.1.0.0', |
|||
|
|||
'category': 'Point Of Sale', |
|||
|
|||
'summary': """ |
|||
Add Currency Management to POS Customer Display device""", |
|||
|
|||
"author": "Coop IT Easy SCRLfs, " |
|||
"Odoo Community Association (OCA)", |
|||
|
|||
'license': 'AGPL-3', |
|||
|
|||
'depends': [ |
|||
'pos_customer_display' |
|||
], |
|||
|
|||
'data': [ |
|||
'templates/templates.xml', |
|||
'views/pos_config_view.xml', |
|||
], |
|||
|
|||
'demo': [], |
|||
|
|||
'installable': True, |
|||
} |
@ -0,0 +1,3 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
from . import pos_customer_display_currency |
@ -0,0 +1,30 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# Vincent Van Rossem <vvrossem@gmail.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import models, fields, api, _ |
|||
from odoo.exceptions import ValidationError |
|||
|
|||
|
|||
class PosConfig(models.Model): |
|||
_inherit = 'pos.config' |
|||
|
|||
# TODO(Vincent) add field customer_display_currency_code or use/fetch Odoo's default currency |
|||
customer_display_currency_char = fields.Char( |
|||
size=1, |
|||
required=True, |
|||
string="User-defined Currency Character", default="$", |
|||
help="The ASCII character to use for drawing the default currency symbol on the device.") |
|||
|
|||
@api.constrains('customer_display_currency_char') |
|||
def _check_currency_char(self): |
|||
""" |
|||
char_code can only be defined between character hex codes \x20 and \xFF |
|||
(or decimal codes 32 and 255) |
|||
|
|||
""" |
|||
self.ensure_one() |
|||
if not 32 <= ord(self.customer_display_currency_char) <= 255: |
|||
raise ValidationError("User-defined Currency Character must be defined between " |
|||
"character codes 20h (32) and FFh (255)") |
@ -0,0 +1,310 @@ |
|||
/* |
|||
Copyright 2019 Coop IT Easy SCRLfs |
|||
Vincent Van Rossem <vvrossem@gmail.com> |
|||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|||
*/ |
|||
|
|||
odoo.define('pos_customer_display_currency.pos_customer_display_currency', function (require) { |
|||
"use strict"; |
|||
var chrome = require('point_of_sale.chrome'); |
|||
var core = require('web.core'); |
|||
var utils = require('web.utils') |
|||
var devices = require('point_of_sale.devices'); |
|||
var gui = require('point_of_sale.gui'); |
|||
var models = require('point_of_sale.models'); |
|||
var screens = require('point_of_sale.screens'); |
|||
var _t = core._t; |
|||
|
|||
var timerID_total; |
|||
var timerID_updated; |
|||
|
|||
var round_pr = utils.round_precision; |
|||
|
|||
models.PosModel = models.PosModel.extend({ |
|||
/** |
|||
* Prepare and send the currency data to the customer display device |
|||
*/ |
|||
prepare_currency_data_customer_display: function () { |
|||
var currency_data = { |
|||
'currency_code': this.currency.name || this.company_currency.name, |
|||
'currency_char': this.config.customer_display_currency_char |
|||
}; |
|||
this.proxy.send_currency_data_customer_display(currency_data); |
|||
}, |
|||
|
|||
/** |
|||
* Prepare and send a text to the customer display device according to param type |
|||
* @override prepare_text_customer_display from customer_display.js (module pos_customer_display) |
|||
* @param {string} type |
|||
* @param data |
|||
*/ |
|||
prepare_text_customer_display: function (type, data) { |
|||
if (!this.config.iface_customer_display) |
|||
return; |
|||
var line_length = this.config.customer_display_line_length || 20; |
|||
var currency_rounding = this.currency.decimals; |
|||
var currency_char = this.config.customer_display_currency_char; |
|||
var previous_lines_to_send, updated_lines_to_send, lines_to_send, total_lines = []; |
|||
var line, product, container = {}; |
|||
var l21 = ""; |
|||
var l22 = ""; |
|||
var total = ""; |
|||
var mode = $('.selected-mode').attr('data-mode'); // numpad selected mode
|
|||
|
|||
if (timerID_updated){ |
|||
clearTimeout(timerID_updated); |
|||
} |
|||
if (timerID_total){ |
|||
clearTimeout(timerID_total); |
|||
} |
|||
|
|||
switch(type) { |
|||
case 'add_update_line': |
|||
line = data['line']; |
|||
if ((line.qty_manually_set && mode === 'quantity') || mode === 'tare' || mode === 'price') { |
|||
// first display "Manual Entry"
|
|||
l21 = line.get_quantity_str_with_uom() |
|||
+ 'x' |
|||
+ line.get_unit_price_with_uom_currency(currency_char, currency_rounding); |
|||
previous_lines_to_send = [ |
|||
this.proxy.align_left(_t('Manual Entry'), line_length), |
|||
this.proxy.align_right(l21, line_length) |
|||
]; |
|||
this.proxy.send_text_customer_display(previous_lines_to_send, line_length); |
|||
|
|||
// then display updated product after 3s
|
|||
l22 = ' ' + line.get_display_price().toFixed(currency_rounding) + currency_char; |
|||
updated_lines_to_send = [ |
|||
this.proxy.align_left(line.get_product().display_name, line_length - l22.length) + l22, |
|||
this.proxy.align_left(l21, line_length) |
|||
]; |
|||
timerID_updated = setTimeout(function () { |
|||
this.proxy.send_text_customer_display(updated_lines_to_send, line_length); |
|||
}.bind(this),3000); |
|||
|
|||
} else if (!line.qty_manually_set && mode === 'quantity') { |
|||
// first display added product
|
|||
l21 = line.get_quantity_str_with_uom() |
|||
+ 'x' |
|||
+ line.get_unit_price_with_uom_currency(currency_char, currency_rounding); |
|||
l22 = ' ' + line.get_display_price().toFixed(currency_rounding) + currency_char; |
|||
previous_lines_to_send = [ |
|||
this.proxy.align_left(line.get_product().display_name, line_length - l22.length) + l22, |
|||
this.proxy.align_left(l21, line_length) |
|||
]; |
|||
this.proxy.send_text_customer_display(previous_lines_to_send, line_length); |
|||
|
|||
} else if (mode === 'discount') { |
|||
// first display discount information
|
|||
var discount = line.get_discount(); |
|||
l22 = ' ' + line.get_base_price().toFixed(currency_rounding) + currency_char; |
|||
previous_lines_to_send = [ |
|||
this.proxy.align_left(line.get_product().display_name, line_length - l22.length) + l22, |
|||
this.proxy.align_left(_t("Discount:") + discount + '%', line_length) |
|||
]; |
|||
this.proxy.send_text_customer_display(previous_lines_to_send, line_length); |
|||
|
|||
// then display updated product after 3s
|
|||
l21 = line.get_quantity_str_with_uom() |
|||
+ 'x' |
|||
+ line.get_base_price().toFixed(currency_rounding) + currency_char; |
|||
updated_lines_to_send = [ |
|||
this.proxy.align_left(line.get_product().display_name, line_length - l22.length) + l22, |
|||
this.proxy.align_left(l21, line_length) |
|||
]; |
|||
timerID_updated = setTimeout(function () { |
|||
this.proxy.send_text_customer_display(updated_lines_to_send, line_length); |
|||
}.bind(this),3000); |
|||
} |
|||
break; |
|||
|
|||
case 'add_container': |
|||
line = data['line']; |
|||
container = line.get_container(); |
|||
previous_lines_to_send = [ |
|||
this.proxy.align_left(container.name, line_length), |
|||
this.proxy.align_right(container.weight.toString() + ' kg', line_length) |
|||
]; |
|||
this.proxy.send_text_customer_display(previous_lines_to_send, line_length); |
|||
break; |
|||
|
|||
case 'remove_orderline': |
|||
// first click on the backspace button set the amount to 0
|
|||
// => we can't precise the deleted quantity and price
|
|||
line = data['line']; |
|||
product = line.get_product(); |
|||
if (line.container) { |
|||
lines_to_send = [ |
|||
this.proxy.align_left(_t("Delete Container"), line_length), |
|||
this.proxy.align_right(line.container.name, line_length) |
|||
]; |
|||
|
|||
} else if (product) { |
|||
lines_to_send = [ |
|||
this.proxy.align_left(_t("Delete Item"), line_length), |
|||
this.proxy.align_right(product.display_name, line_length) |
|||
]; |
|||
} |
|||
this.proxy.send_text_customer_display(lines_to_send, line_length); |
|||
break; |
|||
|
|||
case 'add_paymentline': |
|||
total = this.get('selectedOrder').get_total_with_tax().toFixed(currency_rounding) + currency_char; |
|||
lines_to_send = [ |
|||
this.proxy.align_left(_t("TOTAL:"), line_length), |
|||
this.proxy.align_right(total, line_length) |
|||
]; |
|||
this.proxy.send_text_customer_display(lines_to_send, line_length); |
|||
break; |
|||
|
|||
case 'remove_paymentline': |
|||
line = data['line']; |
|||
var amount = line.get_amount().toFixed(currency_rounding) + currency_char; |
|||
lines_to_send = [ |
|||
this.proxy.align_left(_t("Cancel Payment"), line_length), |
|||
this.proxy.align_right(line.cashregister.journal_id[1], line_length - 1 - amount.length) + ' ' + amount |
|||
]; |
|||
this.proxy.send_text_customer_display(lines_to_send, line_length); |
|||
break; |
|||
|
|||
case 'update_payment': |
|||
var change = data['change'] + currency_char; |
|||
lines_to_send = [ |
|||
this.proxy.align_left(_t("Your Change:"), line_length), |
|||
this.proxy.align_right(change, line_length) |
|||
]; |
|||
this.proxy.send_text_customer_display(lines_to_send, line_length); |
|||
break; |
|||
|
|||
// same display for both types
|
|||
case 'push_order': |
|||
case 'openPOS': |
|||
lines_to_send = [ |
|||
this.proxy.align_center(this.config.customer_display_msg_next_l1, line_length), |
|||
this.proxy.align_center(this.config.customer_display_msg_next_l2, line_length) |
|||
]; |
|||
this.proxy.send_text_customer_display(lines_to_send, line_length); |
|||
break; |
|||
|
|||
case 'closePOS': |
|||
lines_to_send = [ |
|||
this.proxy.align_center(this.config.customer_display_msg_closed_l1, line_length), |
|||
this.proxy.align_center(this.config.customer_display_msg_closed_l2, line_length) |
|||
]; |
|||
this.proxy.send_text_customer_display(lines_to_send, line_length); |
|||
break; |
|||
|
|||
default: |
|||
console.warn('Unknown message type'); |
|||
return; |
|||
} |
|||
|
|||
if ((type === 'add_update_line' && mode === 'quantity' && !line.qty_manually_set) |
|||
|| type === 'add_container' || type === 'remove_orderline') { |
|||
// display total after 3s
|
|||
var order = this.get('selectedOrder'); |
|||
if (order){ |
|||
total = order.get_total_with_tax().toFixed(currency_rounding) + currency_char; |
|||
total_lines = [ |
|||
this.proxy.align_left(_t("TOTAL:"), line_length), |
|||
this.proxy.align_right(total, line_length) |
|||
]; |
|||
timerID_total = setTimeout(function() {this.proxy.send_text_customer_display(total_lines, line_length); }.bind(this), 3000); |
|||
} |
|||
} else if ((type === 'add_update_line' && mode === 'quantity' && line.qty_manually_set) |
|||
|| mode === 'price' || mode === 'discount' || mode === 'tare') { |
|||
// then display total after 6s
|
|||
total = this.get('selectedOrder').get_total_with_tax().toFixed(currency_rounding) + currency_char; |
|||
total_lines = [ |
|||
this.proxy.align_left(_t("TOTAL:"), line_length), |
|||
this.proxy.align_right(total, line_length) |
|||
]; |
|||
timerID_total = setTimeout(function() {this.proxy.send_text_customer_display(total_lines, line_length); }.bind(this), 6000); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
devices.ProxyDevice = devices.ProxyDevice.extend({ |
|||
/** |
|||
* Send currency data to the customer display device |
|||
* @param currency_data |
|||
* @returns {*} |
|||
*/ |
|||
send_currency_data_customer_display: function (currency_data) { |
|||
return this.message('send_currency_data_customer_display', |
|||
{ |
|||
'currency_data': JSON.stringify(currency_data) |
|||
}); |
|||
}, |
|||
}); |
|||
|
|||
models.Orderline = models.Orderline.extend({ |
|||
/** |
|||
* This function is for the Bixolon only |
|||
* Returns quantityStr with or without the name of the unit of measure (uom) |
|||
*/ |
|||
get_quantity_str_with_uom: function(){ |
|||
var unit = this.get_unit(); |
|||
if(unit && !unit.is_pos_groupable){ |
|||
return this.quantityStr + '' + unit.name; |
|||
}else{ |
|||
return round_pr(this.quantity, 0.1); |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* This function is for the Bixolon display device only. |
|||
* Returns the unit price with the user-defined currency symbol (default: '$') |
|||
*/ |
|||
get_unit_price_with_uom_currency: function(currency_char, currency_rounding){ |
|||
var unit = this.get_unit(); |
|||
if(unit && !unit.is_pos_groupable){ |
|||
return this.get_unit_price().toFixed(currency_rounding) + currency_char + '/' + unit.name; |
|||
}else{ |
|||
return this.get_unit_price().toFixed(currency_rounding) + currency_char; |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
var OrderSuper = models.Order; |
|||
|
|||
models.Order = models.Order.extend({ |
|||
/** |
|||
* @extends add_container from models_and_db.js (module: pos_container) |
|||
*/ |
|||
add_container: function(container, options){ |
|||
// invoke parent object's implementation
|
|||
var res = OrderSuper.prototype.add_container.call(this, container, options); |
|||
if (container){ |
|||
// parent ends with this.select_orderline(this.get_last_orderline());
|
|||
var line = this.get_last_orderline(); |
|||
this.pos.prepare_text_customer_display('add_container', {'line': line }); |
|||
} |
|||
return res; |
|||
}, |
|||
}); |
|||
|
|||
/** |
|||
* Fetch and set currency data when the ProxyStatusWidget starts |
|||
*/ |
|||
chrome.ProxyStatusWidget.include({ |
|||
start: function () { |
|||
this._super(); |
|||
this.pos.prepare_currency_data_customer_display(); |
|||
}, |
|||
}); |
|||
|
|||
screens.OrderWidget.include({ |
|||
set_value: function(val) { |
|||
var order = this.pos.get_order(); |
|||
var selected_orderline = order.get_selected_orderline(); |
|||
if (selected_orderline) { |
|||
var mode = this.numpad_state.get('mode'); |
|||
if( mode === 'quantity' ) { |
|||
selected_orderline.qty_manually_set = true; |
|||
} |
|||
} |
|||
this._super(val); |
|||
}, |
|||
}); |
|||
}); |
@ -0,0 +1,10 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
|
|||
<template id="assets" name="pos_customer_display_currency assets" inherit_id="point_of_sale.assets"> |
|||
<xpath expr="." position="inside"> |
|||
<script type="text/javascript" src="/pos_customer_display_currency/static/src/js/pos_customer_display_currency.js"></script> |
|||
</xpath> |
|||
</template> |
|||
|
|||
</odoo> |
@ -0,0 +1,17 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<record id="view_pos_config_form" model="ir.ui.view"> |
|||
<field name="name">pos_customer_display_currency.pos.config.form</field> |
|||
<field name="model">pos.config</field> |
|||
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//field[@name='customer_display_line_length']/.." position="before"> |
|||
<div class="row"> |
|||
<label for="customer_display_currency_char" class="col-lg-3 o_light_label"/> |
|||
<field name="customer_display_currency_char"/> |
|||
</div> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
|
|||
</odoo> |
@ -0,0 +1 @@ |
|||
from . import models |
@ -0,0 +1,14 @@ |
|||
{ |
|||
"name": "POS Hash Certification", |
|||
"summary": "Module de certification ", |
|||
"version": "12.0.1.0.1", |
|||
"category": "POS", |
|||
"website": "https://vracoop.fr/", |
|||
"author": " Coop IT Easy SCRLfs", |
|||
"license": "AGPL-3", |
|||
"application": False, |
|||
"installable": True, |
|||
"external_dependencies": {"python": ["checksumdir"]}, |
|||
"depends": [], |
|||
"data": ["views/ir_module.xml"], |
|||
} |
@ -0,0 +1 @@ |
|||
from . import pos_hash_cert |
@ -0,0 +1,63 @@ |
|||
import logging |
|||
import os |
|||
|
|||
from odoo import api, fields, models |
|||
from odoo.tools.config import config |
|||
|
|||
from checksumdir import dirhash |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
CERT_DIR = config.get('certified_modules_directory', 'pos_certified_modules') |
|||
USER_DIR = os.path.expanduser("~") |
|||
|
|||
|
|||
class ModuleHash(models.Model): |
|||
_inherit = 'ir.module.module' |
|||
|
|||
hash = fields.Char(compute='_compute_hash', |
|||
help='Module hash') |
|||
|
|||
@api.multi |
|||
def _compute_hash(self): |
|||
|
|||
_logger.debug("[pos_hash_cert] USER_DIR = %s" % USER_DIR) |
|||
start_dir = os.path.dirname(os.path.realpath(__file__)) |
|||
_logger.debug("[pos_hash_cert] start_DIR = %s" % start_dir) |
|||
|
|||
last_root = start_dir |
|||
current_root = start_dir |
|||
found_cert_dir = None |
|||
|
|||
while found_cert_dir is None and current_root != os.path.dirname(USER_DIR): |
|||
pruned = False |
|||
for root, dirs, files in os.walk(current_root): |
|||
if not pruned: |
|||
try: |
|||
# Remove the part of the tree we already searched |
|||
del dirs[dirs.index(os.path.basename(last_root))] |
|||
pruned = True |
|||
except ValueError: |
|||
pass |
|||
if CERT_DIR in dirs: |
|||
# found the directory, stop |
|||
found_cert_dir = os.path.join(root, CERT_DIR) |
|||
break |
|||
# Otherwise, pop up a level, search again |
|||
last_root = current_root |
|||
current_root = os.path.dirname(last_root) |
|||
|
|||
if found_cert_dir: |
|||
_logger.debug("[pos_hash_cert] found_cert_dir = %s" % found_cert_dir) |
|||
certified_modules = [ |
|||
name |
|||
for name in os.listdir(found_cert_dir) |
|||
if os.path.isdir(os.path.join(found_cert_dir, name)) |
|||
] |
|||
|
|||
for record in self: |
|||
if record.name in certified_modules: |
|||
record.hash = dirhash(found_cert_dir, 'sha256', excluded_extensions=['pyc']) |
|||
else: |
|||
_logger.debug("[pos_hash_cert] no certified modules directory found") |
|||
pass |
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<odoo> |
|||
<data> |
|||
<record model="ir.ui.view" id="view_hash_module"> |
|||
<field name="name">ir.module.module.form.hashview</field> |
|||
<field name="model">ir.module.module</field> |
|||
<field name="inherit_id" ref="base.module_form"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//field[@name='installed_version']" position="after"> |
|||
<field name="hash" attrs="{'invisible':[('hash', '=', False)]}"/> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,105 @@ |
|||
=============== |
|||
Loyalty Program |
|||
=============== |
|||
|
|||
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|||
!! This file is generated by oca-gen-addon-readme !! |
|||
!! changes will be overwritten. !! |
|||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|||
|
|||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png |
|||
:target: https://odoo-community.org/page/development-status |
|||
:alt: Beta |
|||
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png |
|||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github |
|||
:target: https://github.com/OCA/pos/tree/11.0/pos_loyalty |
|||
:alt: OCA/pos |
|||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png |
|||
:target: https://translation.odoo-community.org/projects/pos-11-0/pos-11-0-pos_loyalty |
|||
:alt: Translate me on Weblate |
|||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png |
|||
:target: https://runbot.odoo-community.org/runbot/184/11.0 |
|||
:alt: Try me on Runbot |
|||
|
|||
|badge1| |badge2| |badge3| |badge4| |badge5| |
|||
|
|||
This module allows you to define a loyalty program in the point of sale, |
|||
where the customers earn loyalty points and get rewards. |
|||
|
|||
This module is a forward-port to v10 of the pos_loyalty module from Odoo's |
|||
saas-6 branch. |
|||
The functionality was moved to the Enterprise edition in later versions. |
|||
|
|||
**Table of contents** |
|||
|
|||
.. contents:: |
|||
:local: |
|||
|
|||
Configuration |
|||
============= |
|||
|
|||
To use this module, you need to: |
|||
|
|||
* Go to *Point of Sale > Configuration > Loyalty Programs* and define a new loyalty program with specific rules and rewards. |
|||
* Assign the loyalty program to the desired Point of Sale. |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
The Loyalty Program defines rules for acquiring points and rewards on which they can be spent. |
|||
|
|||
Rules can be defined globally for all products (fields on loyalty.program) and / or rules that are applied only on specific product or PoS category (loyalty.rule records) on a *points per product sold* or *points per currency spent* basis. The specific rules (loyalty.rule) can be defined as cumulative, which means that they will be aggregated with other matching rules (loyalty.rule records and loyalty.program fields). In the case of non-cumulative rules only the points from that one matching rule are used. Additionally, *fixed points per order* can be added which are applied regardless of whether or not cumulative or non-cumulative rules were applied also. |
|||
|
|||
Rewards can be of three types: |
|||
|
|||
* *Gift* - give a single unit of product for free |
|||
* *Discount* - give a discount to the whole order. It should be added at the end of the order so that the correct total price is used. |
|||
* *Resale* - allow for customer to sell back his earned points. These are calculated by setting the price on the Resale product (*resale_product.list_price* * *customer.loyalty_points*) |
|||
|
|||
All rewards can define how many points they cost (point_cost) and how many are needed so that the customer can become eligable for the reward (minimum_points). for Gift and Discount rewards minimum_points are considered only if they are greater then the point_cost for that reward (minimum_points > point_cost). For Resale products only minimum_points can be used. |
|||
|
|||
Bug Tracker |
|||
=========== |
|||
|
|||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/pos/issues>`_. |
|||
In case of trouble, please check there if your issue has already been reported. |
|||
If you spotted it first, help us smashing it by providing a detailed and welcomed |
|||
`feedback <https://github.com/OCA/pos/issues/new?body=module:%20pos_loyalty%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. |
|||
|
|||
Do not contact contributors directly about support or help with technical issues. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Authors |
|||
~~~~~~~ |
|||
|
|||
* OpenERP SA |
|||
* RGB Consulting SL |
|||
* Lambda IS |
|||
|
|||
Contributors |
|||
~~~~~~~~~~~~ |
|||
|
|||
* RGB Consulting SL (http://www.rgbconsulting.com) |
|||
* Forward-port from Odoo SA saas-6 branch |
|||
* Kiril Vangelovski <kiril@lambda-is.com> |
|||
|
|||
Maintainers |
|||
~~~~~~~~~~~ |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use. |
|||
|
|||
This module is part of the `OCA/pos <https://github.com/OCA/pos/tree/11.0/pos_loyalty>`_ project on GitHub. |
|||
|
|||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. |
@ -0,0 +1,3 @@ |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from . import models |
@ -0,0 +1,36 @@ |
|||
# Copyright 2004-2010 OpenERP SA |
|||
# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) |
|||
# Copyright 2018 Lambda IS DOOEL <https://www.lambda-is.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
{ |
|||
'name': 'Loyalty Program', |
|||
'version': '12.0.1.0.0', |
|||
'category': 'Point of Sale', |
|||
'license': 'AGPL-3', |
|||
'author': "OpenERP SA, " |
|||
"RGB Consulting SL, " |
|||
"Lambda IS, " |
|||
"Odoo Community Association (OCA)", |
|||
'website': "https://odoo-community.org/", |
|||
'depends': ['point_of_sale'], |
|||
'demo': [ |
|||
'demo/templates.xml', |
|||
], |
|||
'data': [ |
|||
'security/ir.model.access.csv', |
|||
'views/templates.xml', |
|||
'views/loyalty_program_view.xml', |
|||
'views/loyalty_reward_view.xml', |
|||
'views/loyalty_rule_view.xml', |
|||
'views/pos_config_view.xml', |
|||
'views/pos_order_view.xml', |
|||
'views/res_partner_view.xml', |
|||
], |
|||
|
|||
'qweb': [ |
|||
'static/src/xml/pos.xml', |
|||
], |
|||
|
|||
'installable': True, |
|||
} |
@ -0,0 +1,8 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<template id="assets_demo" inherit_id="point_of_sale.assets"> |
|||
<xpath expr="." position="inside"> |
|||
<script type="text/javascript" src="/pos_loyalty/static/src/js/tests.js"></script> |
|||
</xpath> |
|||
</template> |
|||
</odoo> |
@ -0,0 +1,523 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_loyalty |
|||
# |
|||
# Translators: |
|||
# OCA Transbot <transbot@odoo-community.org>, 2017 |
|||
# enjolras <yo@miguelrevilla.com>, 2017 |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 10.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2018-03-01 02:01+0000\n" |
|||
"PO-Revision-Date: 2018-03-01 02:01+0000\n" |
|||
"Last-Translator: enjolras <yo@miguelrevilla.com>, 2017\n" |
|||
"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" |
|||
"Language: es\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: nplurals=2; plural=(n != 1);\n" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "" |
|||
"Amount of loyalty points given to the customer for each point of sale order" |
|||
msgstr "" |
|||
"Cantidad de puntos de fidelidad añadidos al cliente por cada pedido del " |
|||
"punto de venta" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_product |
|||
msgid "Amount of loyalty points given to the customer per product sold" |
|||
msgstr "" |
|||
"Cantidad de puntos de fidelidad añadidos al cliente por producto vendido" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_currency |
|||
msgid "Amount of loyalty points given to the customer per sold currency" |
|||
msgstr "Cantidad de puntos de fidelidad añadidos al cliente por moneda vendida" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Amount of points earned per currency" |
|||
msgstr "Cantidad de puntos ganados por moneda" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Amount of points earned per product" |
|||
msgstr "Cantidad de puntos ganados por producto" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Category" |
|||
msgstr "Categoría" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "Click create to define a Loyalty Program." |
|||
msgstr "Haga clic en crear para definir un Programa de Fidelización" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_res_partner |
|||
msgid "Contact" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Cost of the reward per monetary unit discounted" |
|||
msgstr "Coste de la recompensa por unidad monetaria descontada" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_uid |
|||
msgid "Created by" |
|||
msgstr "Creado por" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_date |
|||
msgid "Created on" |
|||
msgstr "Creado el" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "Cumulative" |
|||
msgstr "Acumulativo" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Discount" |
|||
msgstr "Descuento" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "Discount Product" |
|||
msgstr "Producto de descuento" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Discount limit" |
|||
msgstr "Límite de descuento" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:64 |
|||
#, python-format |
|||
msgid "Discount product field is mandatory for discount rewards" |
|||
msgstr "" |
|||
"El campo Producto de descuento es obligatorio para las recompensas de tipo " |
|||
"descuento." |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_display_name |
|||
msgid "Display Name" |
|||
msgstr "Nombre a mostrar" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Gift" |
|||
msgstr "Regalo" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "Gift Product" |
|||
msgstr "Producto de regalo" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:57 |
|||
#, python-format |
|||
msgid "Gift product field is mandatory for gift rewards" |
|||
msgstr "" |
|||
"El campo Producto de regalo es obligatorio para las recompensas de tipo " |
|||
"regalo" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_id |
|||
msgid "ID" |
|||
msgstr "ID" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule___last_update |
|||
msgid "Last Modified on" |
|||
msgstr "Última modificación el" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "Última actualización por" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_date |
|||
msgid "Last Updated on" |
|||
msgstr "Última actualización el" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order_line |
|||
#, fuzzy |
|||
msgid "Lines of Point of Sale Orders" |
|||
msgstr "Pedidos del TPV" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_order_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "Loyalty Points" |
|||
msgstr "Puntos de fidelidad" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_config_loyalty_id |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Loyalty Program" |
|||
msgstr "Programa de fidelización" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
#, fuzzy |
|||
msgid "Loyalty Program (OCA)" |
|||
msgstr "Programa de fidelización" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_name |
|||
msgid "Loyalty Program Name" |
|||
msgstr "Nombre programa de fidelización" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,name:pos_loyalty.loyalty_program_action |
|||
#: model:ir.ui.menu,name:pos_loyalty.loyalty_program_menu |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_tree_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.partner_property_form_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_order_form_view |
|||
msgid "Loyalty Programs" |
|||
msgstr "Programas de fidelización" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "" |
|||
"Loyalty Programs allow you customers to earn points\n" |
|||
" and rewards when purchasing from your shops." |
|||
msgstr "" |
|||
"Los programas de fidelización le permiten a los clientes ganar puntos\n" |
|||
" y recompensas al comprar en tus tiendas." |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_reward_form_view |
|||
msgid "Loyalty Reward" |
|||
msgstr "Recompensa de fidelización" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_rule_form_view |
|||
msgid "Loyalty Rule" |
|||
msgstr "Regla de fidelización" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Loyalty point amounts will be rounded to multiples of this value" |
|||
msgstr "Los puntos de fidelidad se redondearán a múltiplos de este valor" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
#, fuzzy |
|||
msgid "Loyalty program that will be available in this PoS" |
|||
msgstr "El programa de fidelización al que pertenece esta regla" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Maximum discounted amount allowed forthis discount reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "Minimum Points" |
|||
msgstr "Puntos mínimos" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "" |
|||
"Minimum amount of points the customer must have to qualify for this reward" |
|||
msgstr "Cantidad mínima de puntos de cliente para acceder a esta recompensa" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:379 |
|||
#, python-format |
|||
msgid "No Rewards Available" |
|||
msgstr "No hay recompensas disponibles" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:393 |
|||
#, python-format |
|||
msgid "Please select a reward" |
|||
msgstr "Selecciona una recompensa" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Point Cost" |
|||
msgstr "Coste Puntos" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Point Product" |
|||
msgstr "Producto Punto" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order |
|||
msgid "Point of Sale Orders" |
|||
msgstr "Pedidos del TPV" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:72 |
|||
#, python-format |
|||
msgid "Point product field is mandatory for point resale rewards" |
|||
msgstr "" |
|||
"El campo Producto Punto es obligatorio para recompensas de reventa de puntos" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:6 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:71 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:81 |
|||
#, python-format |
|||
msgid "Points" |
|||
msgstr "Puntos" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Points Rounding" |
|||
msgstr "Redondeo Puntos" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:57 |
|||
#, python-format |
|||
msgid "Points Spent" |
|||
msgstr "Puntos Gastados" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:54 |
|||
#, python-format |
|||
msgid "Points Won" |
|||
msgstr "Puntos Ganados" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_currency |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Points per currency" |
|||
msgstr "Puntos por moneda" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "Points per order" |
|||
msgstr "Puntos por venta" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_product |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Points per product" |
|||
msgstr "Puntos por producto" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Product" |
|||
msgstr "Producto" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Product that represents a point that is sold by the customer" |
|||
msgstr "Producto que representa un punto vendido por el cliente" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Resale" |
|||
msgstr "Reventa" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_name |
|||
msgid "Reward Name" |
|||
msgstr "Nombre recompensa" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Reward the customer with gifts or discounts for loyalty points" |
|||
msgstr "" |
|||
"Recompensar al cliente con regalos o descuentos por puntos de fidelidad" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:25 |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_reward_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
#, python-format |
|||
msgid "Rewards" |
|||
msgstr "Recompensas" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_name |
|||
msgid "Rule Name" |
|||
msgstr "Nombre de regla" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rule_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Rules" |
|||
msgstr "Reglas" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "" |
|||
"Rules define how loyalty points are earned for specific products or " |
|||
"categories" |
|||
msgstr "" |
|||
"Las reglas definen cómo se obtienen puntos de fidelidad para productos o " |
|||
"categorías específicos" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "Target Category" |
|||
msgstr "Categoría afectada" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "Target Product" |
|||
msgstr "Producto afectado" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
msgid "The Loyalty Program this reward belongs to" |
|||
msgstr "El programa de fidelización al que pertenece esta recompensa" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
msgid "The Loyalty Program this rule belongs to" |
|||
msgstr "El programa de fidelización al que pertenece esta regla" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_order_loyalty_points |
|||
msgid "The amount of Loyalty points awarded to the customer with this order" |
|||
msgstr "" |
|||
"La cantidad de puntos de fidelidad que el cliente obtiene con este pedido" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "The category affected by this rule" |
|||
msgstr "La categoría afectada por esta regla" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_type |
|||
msgid "The concept this rule applies to" |
|||
msgstr "El concepto al que aplica esta regla" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount |
|||
msgid "The discount percentage" |
|||
msgstr "El porcentaje de descuento" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "The loyalty points the user won as part of a Loyalty Program" |
|||
msgstr "" |
|||
"Los puntos de fidelidad que el usuario ganó como parte de un Programa de " |
|||
"Fidelización" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_config_loyalty_id |
|||
msgid "The loyalty program used by this Point of Sale" |
|||
msgstr "El programa de fidelización utilizado en este Punto de Venta" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "" |
|||
"The points from this rule will be added to points won from other rules with " |
|||
"the same concept" |
|||
msgstr "" |
|||
"Los puntos de esta regla se añadirán a los puntos ganados en otras reglas " |
|||
"con el mismo concepto" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "The product affected by this rule" |
|||
msgstr "El producto afectado por esta regla" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "The product given as a reward" |
|||
msgstr "El producto utilizado como recompensa" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "The product used to apply discounts" |
|||
msgstr "El producto utilizado para aplicar descuentos" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:380 |
|||
#, python-format |
|||
msgid "" |
|||
"There are no rewards available for this customer as part of the loyalty " |
|||
"program" |
|||
msgstr "" |
|||
"No hay recompensas disponibles para este cliente como parte de este programa " |
|||
"de fidelización" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:60 |
|||
#, python-format |
|||
msgid "Total Points" |
|||
msgstr "Total Puntos" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_type |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_type |
|||
msgid "Type" |
|||
msgstr "Tipo" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_type |
|||
msgid "Type of the reward" |
|||
msgstr "Tipo de recompensa" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "abc" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_program |
|||
msgid "loyalty.program" |
|||
msgstr "loyalty.program" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_reward |
|||
msgid "loyalty.reward" |
|||
msgstr "loyalty.reward" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_rule |
|||
msgid "loyalty.rule" |
|||
msgstr "loyalty.rule" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_config |
|||
msgid "pos.config" |
|||
msgstr "pos.config" |
|||
|
|||
#~ msgid "Partner" |
|||
#~ msgstr "Empresa" |
@ -0,0 +1,504 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_loyalty |
|||
# |
|||
# Translators: |
|||
# leemannd <denis.leemann@camptocamp.com>, 2017 |
|||
# OCA Transbot <transbot@odoo-community.org>, 2018 |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 10.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2018-03-01 02:01+0000\n" |
|||
"PO-Revision-Date: 2018-03-01 02:01+0000\n" |
|||
"Last-Translator: OCA Transbot <transbot@odoo-community.org>, 2018\n" |
|||
"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n" |
|||
"Language: fr\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: nplurals=2; plural=(n > 1);\n" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "" |
|||
"Amount of loyalty points given to the customer for each point of sale order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_product |
|||
msgid "Amount of loyalty points given to the customer per product sold" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_currency |
|||
msgid "Amount of loyalty points given to the customer per sold currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Amount of points earned per currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Amount of points earned per product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Category" |
|||
msgstr "Catégorie" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "Click create to define a Loyalty Program." |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_res_partner |
|||
msgid "Contact" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Cost of the reward per monetary unit discounted" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_uid |
|||
msgid "Created by" |
|||
msgstr "Créé par" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_date |
|||
msgid "Created on" |
|||
msgstr "Créé le" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "Cumulative" |
|||
msgstr "Cumulatif" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Discount" |
|||
msgstr "Rabaias" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "Discount Product" |
|||
msgstr "Produit Soldé" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Discount limit" |
|||
msgstr "Limite de Rabais" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:64 |
|||
#, python-format |
|||
msgid "Discount product field is mandatory for discount rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_display_name |
|||
msgid "Display Name" |
|||
msgstr "Nom Affiché" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Gift" |
|||
msgstr "Cadeau" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "Gift Product" |
|||
msgstr "Produit Cadeau" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:57 |
|||
#, python-format |
|||
msgid "Gift product field is mandatory for gift rewards" |
|||
msgstr "" |
|||
"Le champs 'produit cadeau' est nécessaire pour les cadeau de récompense" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_id |
|||
msgid "ID" |
|||
msgstr "ID" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule___last_update |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_date |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order_line |
|||
msgid "Lines of Point of Sale Orders" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_order_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "Loyalty Points" |
|||
msgstr "Points de fidélité" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_config_loyalty_id |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Loyalty Program" |
|||
msgstr "Programme de Fidélité" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
#, fuzzy |
|||
msgid "Loyalty Program (OCA)" |
|||
msgstr "Programme de Fidélité" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_name |
|||
msgid "Loyalty Program Name" |
|||
msgstr "Nom du Programme de Fidélité" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,name:pos_loyalty.loyalty_program_action |
|||
#: model:ir.ui.menu,name:pos_loyalty.loyalty_program_menu |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_tree_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.partner_property_form_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_order_form_view |
|||
msgid "Loyalty Programs" |
|||
msgstr "Progammes de Fidélité" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "" |
|||
"Loyalty Programs allow you customers to earn points\n" |
|||
" and rewards when purchasing from your shops." |
|||
msgstr "" |
|||
"Les programmes de fidélité permettent aux clients de gagner des points\n" |
|||
"et des cadeaux lors de leurs achats sur vos sites internet." |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_reward_form_view |
|||
msgid "Loyalty Reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_rule_form_view |
|||
msgid "Loyalty Rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Loyalty point amounts will be rounded to multiples of this value" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "Loyalty program that will be available in this PoS" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Maximum discounted amount allowed forthis discount reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "Minimum Points" |
|||
msgstr "Points Minimum" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "" |
|||
"Minimum amount of points the customer must have to qualify for this reward" |
|||
msgstr "Le minimum de points requis pour avoir le droit à cette récompense" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:379 |
|||
#, python-format |
|||
msgid "No Rewards Available" |
|||
msgstr "Pas de récompenses disponible" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:393 |
|||
#, python-format |
|||
msgid "Please select a reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Point Cost" |
|||
msgstr "Coût en points" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Point Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order |
|||
msgid "Point of Sale Orders" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:72 |
|||
#, python-format |
|||
msgid "Point product field is mandatory for point resale rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:6 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:71 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:81 |
|||
#, python-format |
|||
msgid "Points" |
|||
msgstr "Points" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Points Rounding" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:57 |
|||
#, python-format |
|||
msgid "Points Spent" |
|||
msgstr "Points dépensés" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:54 |
|||
#, python-format |
|||
msgid "Points Won" |
|||
msgstr "Points Gagnés" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_currency |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Points per currency" |
|||
msgstr "Points par devise" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "Points per order" |
|||
msgstr "Points par commande" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_product |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Points per product" |
|||
msgstr "Points par produit" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Product" |
|||
msgstr "Produit" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Product that represents a point that is sold by the customer" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Resale" |
|||
msgstr "Revente" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_name |
|||
msgid "Reward Name" |
|||
msgstr "Nom de Récompense" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Reward the customer with gifts or discounts for loyalty points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:25 |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_reward_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
#, python-format |
|||
msgid "Rewards" |
|||
msgstr "Récompenses" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_name |
|||
msgid "Rule Name" |
|||
msgstr "Nom de Règle" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rule_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Rules" |
|||
msgstr "Règles" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "" |
|||
"Rules define how loyalty points are earned for specific products or " |
|||
"categories" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "Target Category" |
|||
msgstr "Catégorie cible" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "Target Product" |
|||
msgstr "Produit Cible" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
msgid "The Loyalty Program this reward belongs to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
msgid "The Loyalty Program this rule belongs to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_order_loyalty_points |
|||
msgid "The amount of Loyalty points awarded to the customer with this order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "The category affected by this rule" |
|||
msgstr "La catégorie affectée par la règle" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_type |
|||
msgid "The concept this rule applies to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount |
|||
msgid "The discount percentage" |
|||
msgstr "Le pourcentage de rabais" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "The loyalty points the user won as part of a Loyalty Program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_config_loyalty_id |
|||
msgid "The loyalty program used by this Point of Sale" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "" |
|||
"The points from this rule will be added to points won from other rules with " |
|||
"the same concept" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "The product affected by this rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "The product given as a reward" |
|||
msgstr "Le produit est donné en guise de récompense" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "The product used to apply discounts" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:380 |
|||
#, python-format |
|||
msgid "" |
|||
"There are no rewards available for this customer as part of the loyalty " |
|||
"program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:60 |
|||
#, python-format |
|||
msgid "Total Points" |
|||
msgstr "Points Totaux" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_type |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_type |
|||
msgid "Type" |
|||
msgstr "Type" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_type |
|||
msgid "Type of the reward" |
|||
msgstr "Type de récompense" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "abc" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_program |
|||
msgid "loyalty.program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_reward |
|||
msgid "loyalty.reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_rule |
|||
msgid "loyalty.rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_config |
|||
msgid "pos.config" |
|||
msgstr "pos.config" |
|||
|
|||
#~ msgid "Partner" |
|||
#~ msgstr "Partner" |
@ -0,0 +1,498 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_loyalty |
|||
# |
|||
# Translators: |
|||
# Bole <bole@dajmi5.com>, 2017 |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 10.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2017-07-12 02:44+0000\n" |
|||
"PO-Revision-Date: 2017-07-12 02:44+0000\n" |
|||
"Last-Translator: Bole <bole@dajmi5.com>, 2017\n" |
|||
"Language-Team: Croatian (Croatia) (https://www.transifex.com/oca/teams/23907/" |
|||
"hr_HR/)\n" |
|||
"Language: hr_HR\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" |
|||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "" |
|||
"Amount of loyalty points given to the customer for each point of sale order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_product |
|||
msgid "Amount of loyalty points given to the customer per product sold" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_currency |
|||
msgid "Amount of loyalty points given to the customer per sold currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Amount of points earned per currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Amount of points earned per product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Category" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "Click create to define a Loyalty Program." |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_res_partner |
|||
msgid "Contact" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Cost of the reward per monetary unit discounted" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_uid |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_date |
|||
msgid "Created on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "Cumulative" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Discount" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "Discount Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Discount limit" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:64 |
|||
#, python-format |
|||
msgid "Discount product field is mandatory for discount rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_display_name |
|||
msgid "Display Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Gift" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "Gift Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:57 |
|||
#, python-format |
|||
msgid "Gift product field is mandatory for gift rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_id |
|||
msgid "ID" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule___last_update |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_date |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order_line |
|||
msgid "Lines of Point of Sale Orders" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_order_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "Loyalty Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_config_loyalty_id |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Loyalty Program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "Loyalty Program (OCA)" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_name |
|||
msgid "Loyalty Program Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,name:pos_loyalty.loyalty_program_action |
|||
#: model:ir.ui.menu,name:pos_loyalty.loyalty_program_menu |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_tree_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.partner_property_form_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_order_form_view |
|||
msgid "Loyalty Programs" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "" |
|||
"Loyalty Programs allow you customers to earn points\n" |
|||
" and rewards when purchasing from your shops." |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_reward_form_view |
|||
msgid "Loyalty Reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_rule_form_view |
|||
msgid "Loyalty Rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Loyalty point amounts will be rounded to multiples of this value" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "Loyalty program that will be available in this PoS" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Maximum discounted amount allowed forthis discount reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "Minimum Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "" |
|||
"Minimum amount of points the customer must have to qualify for this reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:379 |
|||
#, python-format |
|||
msgid "No Rewards Available" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:393 |
|||
#, python-format |
|||
msgid "Please select a reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Point Cost" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Point Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order |
|||
msgid "Point of Sale Orders" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:72 |
|||
#, python-format |
|||
msgid "Point product field is mandatory for point resale rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:6 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:71 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:81 |
|||
#, python-format |
|||
msgid "Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Points Rounding" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:57 |
|||
#, python-format |
|||
msgid "Points Spent" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:54 |
|||
#, python-format |
|||
msgid "Points Won" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_currency |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Points per currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "Points per order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_product |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Points per product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Product that represents a point that is sold by the customer" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Resale" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_name |
|||
msgid "Reward Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Reward the customer with gifts or discounts for loyalty points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:25 |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_reward_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
#, python-format |
|||
msgid "Rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_name |
|||
msgid "Rule Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rule_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Rules" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "" |
|||
"Rules define how loyalty points are earned for specific products or " |
|||
"categories" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "Target Category" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "Target Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
msgid "The Loyalty Program this reward belongs to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
msgid "The Loyalty Program this rule belongs to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_order_loyalty_points |
|||
msgid "The amount of Loyalty points awarded to the customer with this order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "The category affected by this rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_type |
|||
msgid "The concept this rule applies to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount |
|||
msgid "The discount percentage" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "The loyalty points the user won as part of a Loyalty Program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_config_loyalty_id |
|||
msgid "The loyalty program used by this Point of Sale" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "" |
|||
"The points from this rule will be added to points won from other rules with " |
|||
"the same concept" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "The product affected by this rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "The product given as a reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "The product used to apply discounts" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:380 |
|||
#, python-format |
|||
msgid "" |
|||
"There are no rewards available for this customer as part of the loyalty " |
|||
"program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:60 |
|||
#, python-format |
|||
msgid "Total Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_type |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_type |
|||
msgid "Type" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_type |
|||
msgid "Type of the reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "abc" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_program |
|||
msgid "loyalty.program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_reward |
|||
msgid "loyalty.reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_rule |
|||
msgid "loyalty.rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_config |
|||
msgid "pos.config" |
|||
msgstr "pos.config" |
@ -0,0 +1,497 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_loyalty |
|||
# |
|||
# Translators: |
|||
# Francesco Fresta <franco.fresta@gmail.com>, 2018 |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 10.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2018-03-01 02:01+0000\n" |
|||
"PO-Revision-Date: 2018-03-01 02:01+0000\n" |
|||
"Last-Translator: Francesco Fresta <franco.fresta@gmail.com>, 2018\n" |
|||
"Language-Team: Italian (https://www.transifex.com/oca/teams/23907/it/)\n" |
|||
"Language: it\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: nplurals=2; plural=(n != 1);\n" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "" |
|||
"Amount of loyalty points given to the customer for each point of sale order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_product |
|||
msgid "Amount of loyalty points given to the customer per product sold" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_currency |
|||
msgid "Amount of loyalty points given to the customer per sold currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Amount of points earned per currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Amount of points earned per product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Category" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "Click create to define a Loyalty Program." |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_res_partner |
|||
msgid "Contact" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Cost of the reward per monetary unit discounted" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_uid |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_date |
|||
msgid "Created on" |
|||
msgstr "Creato il" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "Cumulative" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Discount" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "Discount Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Discount limit" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:64 |
|||
#, python-format |
|||
msgid "Discount product field is mandatory for discount rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_display_name |
|||
msgid "Display Name" |
|||
msgstr "Mostra il nome" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Gift" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "Gift Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:57 |
|||
#, python-format |
|||
msgid "Gift product field is mandatory for gift rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_id |
|||
msgid "ID" |
|||
msgstr "ID" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule___last_update |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_date |
|||
msgid "Last Updated on" |
|||
msgstr "Ultimo aggiornamento il" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order_line |
|||
#, fuzzy |
|||
msgid "Lines of Point of Sale Orders" |
|||
msgstr "Punto di riordino" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_order_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "Loyalty Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_config_loyalty_id |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Loyalty Program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "Loyalty Program (OCA)" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_name |
|||
msgid "Loyalty Program Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,name:pos_loyalty.loyalty_program_action |
|||
#: model:ir.ui.menu,name:pos_loyalty.loyalty_program_menu |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_tree_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.partner_property_form_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_order_form_view |
|||
msgid "Loyalty Programs" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "" |
|||
"Loyalty Programs allow you customers to earn points\n" |
|||
" and rewards when purchasing from your shops." |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_reward_form_view |
|||
msgid "Loyalty Reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_rule_form_view |
|||
msgid "Loyalty Rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Loyalty point amounts will be rounded to multiples of this value" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "Loyalty program that will be available in this PoS" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Maximum discounted amount allowed forthis discount reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "Minimum Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "" |
|||
"Minimum amount of points the customer must have to qualify for this reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:379 |
|||
#, python-format |
|||
msgid "No Rewards Available" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:393 |
|||
#, python-format |
|||
msgid "Please select a reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Point Cost" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Point Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order |
|||
msgid "Point of Sale Orders" |
|||
msgstr "Punto di riordino" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:72 |
|||
#, python-format |
|||
msgid "Point product field is mandatory for point resale rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:6 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:71 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:81 |
|||
#, python-format |
|||
msgid "Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Points Rounding" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:57 |
|||
#, python-format |
|||
msgid "Points Spent" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:54 |
|||
#, python-format |
|||
msgid "Points Won" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_currency |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Points per currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "Points per order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_product |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Points per product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Product that represents a point that is sold by the customer" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Resale" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_name |
|||
msgid "Reward Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Reward the customer with gifts or discounts for loyalty points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:25 |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_reward_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
#, python-format |
|||
msgid "Rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_name |
|||
msgid "Rule Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rule_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Rules" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "" |
|||
"Rules define how loyalty points are earned for specific products or " |
|||
"categories" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "Target Category" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "Target Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
msgid "The Loyalty Program this reward belongs to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
msgid "The Loyalty Program this rule belongs to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_order_loyalty_points |
|||
msgid "The amount of Loyalty points awarded to the customer with this order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "The category affected by this rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_type |
|||
msgid "The concept this rule applies to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount |
|||
msgid "The discount percentage" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "The loyalty points the user won as part of a Loyalty Program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_config_loyalty_id |
|||
msgid "The loyalty program used by this Point of Sale" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "" |
|||
"The points from this rule will be added to points won from other rules with " |
|||
"the same concept" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "The product affected by this rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "The product given as a reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "The product used to apply discounts" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:380 |
|||
#, python-format |
|||
msgid "" |
|||
"There are no rewards available for this customer as part of the loyalty " |
|||
"program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:60 |
|||
#, python-format |
|||
msgid "Total Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_type |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_type |
|||
msgid "Type" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_type |
|||
msgid "Type of the reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "abc" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_program |
|||
msgid "loyalty.program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_reward |
|||
msgid "loyalty.reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_rule |
|||
msgid "loyalty.rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_config |
|||
msgid "pos.config" |
|||
msgstr "pos.config" |
@ -0,0 +1,498 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_loyalty |
|||
# |
|||
# Translators: |
|||
# Peter Hageman <hageman.p@gmail.com>, 2017 |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 10.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2017-07-12 02:44+0000\n" |
|||
"PO-Revision-Date: 2017-07-12 02:44+0000\n" |
|||
"Last-Translator: Peter Hageman <hageman.p@gmail.com>, 2017\n" |
|||
"Language-Team: Dutch (Netherlands) (https://www.transifex.com/oca/" |
|||
"teams/23907/nl_NL/)\n" |
|||
"Language: nl_NL\n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: nplurals=2; plural=(n != 1);\n" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "" |
|||
"Amount of loyalty points given to the customer for each point of sale order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_product |
|||
msgid "Amount of loyalty points given to the customer per product sold" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_currency |
|||
msgid "Amount of loyalty points given to the customer per sold currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Amount of points earned per currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Amount of points earned per product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Category" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "Click create to define a Loyalty Program." |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_res_partner |
|||
msgid "Contact" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Cost of the reward per monetary unit discounted" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_uid |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_date |
|||
msgid "Created on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "Cumulative" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Discount" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "Discount Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Discount limit" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:64 |
|||
#, python-format |
|||
msgid "Discount product field is mandatory for discount rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_display_name |
|||
msgid "Display Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Gift" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "Gift Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:57 |
|||
#, python-format |
|||
msgid "Gift product field is mandatory for gift rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_id |
|||
msgid "ID" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule___last_update |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_date |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order_line |
|||
#, fuzzy |
|||
msgid "Lines of Point of Sale Orders" |
|||
msgstr "Kassaorders" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_order_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "Loyalty Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_config_loyalty_id |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Loyalty Program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "Loyalty Program (OCA)" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_name |
|||
msgid "Loyalty Program Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,name:pos_loyalty.loyalty_program_action |
|||
#: model:ir.ui.menu,name:pos_loyalty.loyalty_program_menu |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_tree_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.partner_property_form_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_order_form_view |
|||
msgid "Loyalty Programs" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "" |
|||
"Loyalty Programs allow you customers to earn points\n" |
|||
" and rewards when purchasing from your shops." |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_reward_form_view |
|||
msgid "Loyalty Reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_rule_form_view |
|||
msgid "Loyalty Rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Loyalty point amounts will be rounded to multiples of this value" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "Loyalty program that will be available in this PoS" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Maximum discounted amount allowed forthis discount reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "Minimum Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "" |
|||
"Minimum amount of points the customer must have to qualify for this reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:379 |
|||
#, python-format |
|||
msgid "No Rewards Available" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:393 |
|||
#, python-format |
|||
msgid "Please select a reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Point Cost" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Point Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order |
|||
msgid "Point of Sale Orders" |
|||
msgstr "Kassaorders" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:72 |
|||
#, python-format |
|||
msgid "Point product field is mandatory for point resale rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:6 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:71 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:81 |
|||
#, python-format |
|||
msgid "Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Points Rounding" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:57 |
|||
#, python-format |
|||
msgid "Points Spent" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:54 |
|||
#, python-format |
|||
msgid "Points Won" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_currency |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Points per currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "Points per order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_product |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Points per product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Product that represents a point that is sold by the customer" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Resale" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_name |
|||
msgid "Reward Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Reward the customer with gifts or discounts for loyalty points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:25 |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_reward_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
#, python-format |
|||
msgid "Rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_name |
|||
msgid "Rule Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rule_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Rules" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "" |
|||
"Rules define how loyalty points are earned for specific products or " |
|||
"categories" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "Target Category" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "Target Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
msgid "The Loyalty Program this reward belongs to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
msgid "The Loyalty Program this rule belongs to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_order_loyalty_points |
|||
msgid "The amount of Loyalty points awarded to the customer with this order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "The category affected by this rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_type |
|||
msgid "The concept this rule applies to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount |
|||
msgid "The discount percentage" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "The loyalty points the user won as part of a Loyalty Program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_config_loyalty_id |
|||
msgid "The loyalty program used by this Point of Sale" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "" |
|||
"The points from this rule will be added to points won from other rules with " |
|||
"the same concept" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "The product affected by this rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "The product given as a reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "The product used to apply discounts" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:380 |
|||
#, python-format |
|||
msgid "" |
|||
"There are no rewards available for this customer as part of the loyalty " |
|||
"program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:60 |
|||
#, python-format |
|||
msgid "Total Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_type |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_type |
|||
msgid "Type" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_type |
|||
msgid "Type of the reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "abc" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_program |
|||
msgid "loyalty.program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_reward |
|||
msgid "loyalty.reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_rule |
|||
msgid "loyalty.rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_config |
|||
msgid "pos.config" |
|||
msgstr "pos.config" |
@ -0,0 +1,483 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_loyalty |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 11.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "Amount of loyalty points given to the customer for each point of sale order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_product |
|||
msgid "Amount of loyalty points given to the customer per product sold" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_pp_currency |
|||
msgid "Amount of loyalty points given to the customer per sold currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Amount of points earned per currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Amount of points earned per product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Category" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "Click create to define a Loyalty Program." |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_res_partner |
|||
msgid "Contact" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Cost of the reward per monetary unit discounted" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_uid |
|||
msgid "Created by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_create_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_create_date |
|||
msgid "Created on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "Cumulative" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Discount" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "Discount Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Discount limit" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:64 |
|||
#, python-format |
|||
msgid "Discount product field is mandatory for discount rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_display_name |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_display_name |
|||
msgid "Display Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Gift" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "Gift Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:57 |
|||
#, python-format |
|||
msgid "Gift product field is mandatory for gift rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_id |
|||
msgid "ID" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward___last_update |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule___last_update |
|||
msgid "Last Modified on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_uid |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_uid |
|||
msgid "Last Updated by" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_write_date |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_write_date |
|||
msgid "Last Updated on" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order_line |
|||
msgid "Lines of Point of Sale Orders" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_order_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "Loyalty Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_pos_config_loyalty_id |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Loyalty Program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "Loyalty Program (OCA)" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_name |
|||
msgid "Loyalty Program Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,name:pos_loyalty.loyalty_program_action |
|||
#: model:ir.ui.menu,name:pos_loyalty.loyalty_program_menu |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_tree_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.partner_property_form_view |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_order_form_view |
|||
msgid "Loyalty Programs" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.actions.act_window,help:pos_loyalty.loyalty_program_action |
|||
msgid "Loyalty Programs allow you customers to earn points\n" |
|||
" and rewards when purchasing from your shops." |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_reward_form_view |
|||
msgid "Loyalty Reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_rule_form_view |
|||
msgid "Loyalty Rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Loyalty point amounts will be rounded to multiples of this value" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "Loyalty program that will be available in this PoS" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_max |
|||
msgid "Maximum discounted amount allowed forthis discount reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "Minimum Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_minimum_points |
|||
msgid "Minimum amount of points the customer must have to qualify for this reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:379 |
|||
#, python-format |
|||
msgid "No Rewards Available" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:393 |
|||
#, python-format |
|||
msgid "Please select a reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_cost |
|||
msgid "Point Cost" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Point Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_order |
|||
msgid "Point of Sale Orders" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: code:addons/pos_loyalty/models/loyalty_reward.py:72 |
|||
#, python-format |
|||
msgid "Point product field is mandatory for point resale rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:6 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:71 |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:81 |
|||
#, python-format |
|||
msgid "Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rounding |
|||
msgid "Points Rounding" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:57 |
|||
#, python-format |
|||
msgid "Points Spent" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:54 |
|||
#, python-format |
|||
msgid "Points Won" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_currency |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_currency |
|||
msgid "Points per currency" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_order |
|||
msgid "Points per order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_pp_product |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_pp_product |
|||
msgid "Points per product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.rule,type:0 |
|||
msgid "Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_point_product_id |
|||
msgid "Product that represents a point that is sold by the customer" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: selection:loyalty.reward,type:0 |
|||
msgid "Resale" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_name |
|||
msgid "Reward Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Reward the customer with gifts or discounts for loyalty points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:25 |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_reward_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
#, python-format |
|||
msgid "Rewards" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_name |
|||
msgid "Rule Name" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_program_rule_ids |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Rules" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.loyalty_program_form_view |
|||
msgid "Rules define how loyalty points are earned for specific products or categories" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "Target Category" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "Target Product" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_loyalty_program_id |
|||
msgid "The Loyalty Program this reward belongs to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_loyalty_program_id |
|||
msgid "The Loyalty Program this rule belongs to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_order_loyalty_points |
|||
msgid "The amount of Loyalty points awarded to the customer with this order" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_category_id |
|||
msgid "The category affected by this rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_type |
|||
msgid "The concept this rule applies to" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount |
|||
msgid "The discount percentage" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_partner_loyalty_points |
|||
#: model:ir.model.fields,help:pos_loyalty.field_res_users_loyalty_points |
|||
msgid "The loyalty points the user won as part of a Loyalty Program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_pos_config_loyalty_id |
|||
msgid "The loyalty program used by this Point of Sale" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_cumulative |
|||
msgid "The points from this rule will be added to points won from other rules with the same concept" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_rule_product_id |
|||
msgid "The product affected by this rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_gift_product_id |
|||
msgid "The product given as a reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_discount_product_id |
|||
msgid "The product used to apply discounts" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/js/pos.js:380 |
|||
#, python-format |
|||
msgid "There are no rewards available for this customer as part of the loyalty program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#. openerp-web |
|||
#: code:addons/pos_loyalty/static/src/xml/pos.xml:60 |
|||
#, python-format |
|||
msgid "Total Points" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_reward_type |
|||
#: model:ir.model.fields,field_description:pos_loyalty.field_loyalty_rule_type |
|||
msgid "Type" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model.fields,help:pos_loyalty.field_loyalty_reward_type |
|||
msgid "Type of the reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.ui.view,arch_db:pos_loyalty.pos_config_view_form |
|||
msgid "abc" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_program |
|||
msgid "loyalty.program" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_reward |
|||
msgid "loyalty.reward" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_loyalty_rule |
|||
msgid "loyalty.rule" |
|||
msgstr "" |
|||
|
|||
#. module: pos_loyalty |
|||
#: model:ir.model,name:pos_loyalty.model_pos_config |
|||
msgid "pos.config" |
|||
msgstr "" |
|||
|
@ -0,0 +1,9 @@ |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from . import loyalty_program |
|||
from . import loyalty_reward |
|||
from . import loyalty_rule |
|||
from . import pos_config |
|||
from . import pos_order |
|||
from . import pos_order_line |
|||
from . import res_partner |
@ -0,0 +1,30 @@ |
|||
# Copyright 2004-2010 OpenERP SA |
|||
# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from odoo import fields, models |
|||
|
|||
|
|||
class LoyaltyProgram(models.Model): |
|||
_name = 'loyalty.program' |
|||
|
|||
name = fields.Char(string='Loyalty Program Name', size=32, index=True, |
|||
required=True) |
|||
pp_currency = fields.Float(string='Points per currency', |
|||
help='Amount of loyalty points given to the ' |
|||
'customer per sold currency') |
|||
pp_product = fields.Float(string='Points per product', |
|||
help='Amount of loyalty points given to the ' |
|||
'customer per product sold') |
|||
pp_order = fields.Float(string='Points per order', |
|||
help='Amount of loyalty points given to the ' |
|||
'customer for each point of sale order') |
|||
rounding = fields.Float(string='Points Rounding', default=1, |
|||
help='Loyalty point amounts will be rounded to ' |
|||
'multiples of this value') |
|||
rule_ids = fields.One2many(comodel_name='loyalty.rule', |
|||
inverse_name='loyalty_program_id', |
|||
string='Rules') |
|||
reward_ids = fields.One2many(comodel_name='loyalty.reward', |
|||
inverse_name='loyalty_program_id', |
|||
string='Rewards') |
@ -0,0 +1,73 @@ |
|||
# Copyright 2004-2010 OpenERP SA |
|||
# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) |
|||
# Copyright 2018 Lambda IS DOOEL <https://www.lambda-is.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from odoo import fields, models, api, _ |
|||
from odoo.exceptions import ValidationError |
|||
|
|||
|
|||
class LoyaltyReward(models.Model): |
|||
_name = 'loyalty.reward' |
|||
|
|||
name = fields.Char(string='Reward Name', size=32, index=True, |
|||
required=True) |
|||
type = fields.Selection(selection=[('gift', 'Gift'), |
|||
('discount', 'Discount'), |
|||
('resale', 'Resale')], |
|||
string='Type', required=True, |
|||
help='Type of the reward') |
|||
minimum_points = fields.Float(string='Minimum Points', |
|||
help='Minimum amount of points the customer' |
|||
' must have to qualify for this reward') |
|||
point_cost = fields.Float(string='Point Cost', |
|||
help='Cost of the reward per monetary unit ' |
|||
'discounted') |
|||
discount = fields.Float(help='The discount percentage') |
|||
discount_max = fields.Float(string='Discount limit', |
|||
help='Maximum discounted amount allowed for' |
|||
'this discount reward') |
|||
loyalty_program_id = fields.Many2one(comodel_name='loyalty.program', |
|||
string='Loyalty Program', |
|||
help='The Loyalty Program this reward' |
|||
' belongs to') |
|||
gift_product_id = fields.Many2one(comodel_name='product.product', |
|||
domain=[('available_in_pos', '=', True)], |
|||
string='Gift Product', |
|||
help='The product given as a reward') |
|||
discount_product_id = fields.Many2one(comodel_name='product.product', |
|||
domain=[ |
|||
('available_in_pos', '=', True)], |
|||
string='Discount Product', |
|||
help='The product used to apply ' |
|||
'discounts') |
|||
point_product_id = fields.Many2one(comodel_name='product.product', |
|||
domain=[ |
|||
('available_in_pos', '=', True)], |
|||
string='Point Product', |
|||
help='Product that represents a point ' |
|||
'that is sold by the customer') |
|||
|
|||
@api.multi |
|||
@api.constrains('type', 'gift_product_id') |
|||
def _check_gift_product(self): |
|||
for reward in self: |
|||
if reward.type == 'gift' and not reward.gift_product_id: |
|||
raise ValidationError( |
|||
_('Gift product field is mandatory for gift rewards')) |
|||
|
|||
@api.multi |
|||
@api.constrains('type', 'discount_product_id') |
|||
def _check_discount_product(self): |
|||
for reward in self: |
|||
if reward.type == 'discount' and not reward.discount_product_id: |
|||
raise ValidationError(_('Discount product field is ' |
|||
'mandatory for discount rewards')) |
|||
|
|||
@api.multi |
|||
@api.constrains('type', 'point_product_id') |
|||
def _check_point_product(self): |
|||
for reward in self: |
|||
if reward.type == 'resale' and not reward.point_product_id: |
|||
raise ValidationError(_('Point product field is ' |
|||
'mandatory for point resale rewards')) |
@ -0,0 +1,34 @@ |
|||
# Copyright 2004-2010 OpenERP SA |
|||
# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) |
|||
# Copyright 2018 Lambda IS DOOEL <https://www.lambda-is.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from odoo import fields, models |
|||
|
|||
|
|||
class LoyaltyRule(models.Model): |
|||
_name = 'loyalty.rule' |
|||
|
|||
name = fields.Char(string='Rule Name', size=32, index=True, required=True) |
|||
type = fields.Selection(selection=[('product', 'Product'), |
|||
('category', 'Category')], |
|||
string='Type', required=True, default='product', |
|||
help='The concept this rule applies to') |
|||
cumulative = fields.Boolean(help='The points from this rule will be added ' |
|||
'to points won from other rules with ' |
|||
'the same concept') |
|||
pp_product = fields.Float(string='Points per product', |
|||
help='Amount of points earned per product') |
|||
pp_currency = fields.Float(string='Points per currency', |
|||
help='Amount of points earned per currency') |
|||
loyalty_program_id = fields.Many2one(comodel_name='loyalty.program', |
|||
string='Loyalty Program', |
|||
help='The Loyalty Program this rule ' |
|||
'belongs to') |
|||
product_id = fields.Many2one(comodel_name='product.product', |
|||
domain=[('available_in_pos', '=', True)], |
|||
string='Target Product', |
|||
help='The product affected by this rule') |
|||
category_id = fields.Many2one(comodel_name='pos.category', |
|||
string='Target Category', |
|||
help='The category affected by this rule') |
@ -0,0 +1,14 @@ |
|||
# Copyright 2004-2010 OpenERP SA |
|||
# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from odoo import fields, models |
|||
|
|||
|
|||
class PosConfig(models.Model): |
|||
_inherit = 'pos.config' |
|||
|
|||
loyalty_id = fields.Many2one(comodel_name='loyalty.program', |
|||
string='Loyalty Program', |
|||
help='The loyalty program used by this ' |
|||
'Point of Sale') |
@ -0,0 +1,30 @@ |
|||
# Copyright 2004-2010 OpenERP SA |
|||
# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from odoo import fields, models, api |
|||
|
|||
|
|||
class PosOrder(models.Model): |
|||
_inherit = 'pos.order' |
|||
|
|||
loyalty_points = fields.Float(string='Loyalty Points', |
|||
help='The amount of Loyalty points awarded ' |
|||
'to the customer with this order') |
|||
|
|||
@api.model |
|||
def _order_fields(self, ui_order): |
|||
res = super(PosOrder, self)._order_fields(ui_order) |
|||
res['loyalty_points'] = ui_order.get('loyalty_points', 0) |
|||
return res |
|||
|
|||
@api.model |
|||
def create_from_ui(self, orders): |
|||
res = super(PosOrder, self).create_from_ui(orders) |
|||
for order in orders: |
|||
order_partner = order['data']['partner_id'] |
|||
order_points = order['data'].get('loyalty_points', 0) |
|||
if order_points != 0 and order_partner: |
|||
partner = self.env['res.partner'].browse(order_partner) |
|||
partner.loyalty_points += order_points |
|||
return res |
@ -0,0 +1,20 @@ |
|||
# Copyright 2018 Lambda IS DOOEL <https://www.lambda-is.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from odoo import api, models |
|||
|
|||
|
|||
class PosOrderLine(models.Model): |
|||
_inherit = 'pos.order.line' |
|||
|
|||
@api.model |
|||
def _order_line_fields(self, line, session_id=None): |
|||
line = super(PosOrderLine, self)._order_line_fields( |
|||
line, session_id=session_id) |
|||
if line and 'reward_id' in line[2]: |
|||
# Delete the key since field doesn't exist |
|||
# and raises a warning in the logs. |
|||
# TODO: add field and remove this if data will be |
|||
# used on server, example in report / widget. |
|||
del line[2]['reward_id'] |
|||
return line |
@ -0,0 +1,13 @@ |
|||
# Copyright 2004-2010 OpenERP SA |
|||
# Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from odoo import fields, models |
|||
|
|||
|
|||
class ResPartner(models.Model): |
|||
_inherit = 'res.partner' |
|||
|
|||
loyalty_points = fields.Float(string='Loyalty Points', |
|||
help='The loyalty points the user won as ' |
|||
'part of a Loyalty Program') |
@ -0,0 +1,4 @@ |
|||
To use this module, you need to: |
|||
|
|||
* Go to *Point of Sale > Configuration > Loyalty Programs* and define a new loyalty program with specific rules and rewards. |
|||
* Assign the loyalty program to the desired Point of Sale. |
@ -0,0 +1,3 @@ |
|||
* RGB Consulting SL (http://www.rgbconsulting.com) |
|||
* Forward-port from Odoo SA saas-6 branch |
|||
* Kiril Vangelovski <kiril@lambda-is.com> |
@ -0,0 +1,6 @@ |
|||
This module allows you to define a loyalty program in the point of sale, |
|||
where the customers earn loyalty points and get rewards. |
|||
|
|||
This module is a forward-port to v12 of the pos_loyalty module from Odoo's |
|||
saas-6 branch. |
|||
The functionality was moved to the Enterprise edition in later versions. |
@ -0,0 +1,11 @@ |
|||
The Loyalty Program defines rules for acquiring points and rewards on which they can be spent. |
|||
|
|||
Rules can be defined globally for all products (fields on loyalty.program) and / or rules that are applied only on specific product or PoS category (loyalty.rule records) on a *points per product sold* or *points per currency spent* basis. The specific rules (loyalty.rule) can be defined as cumulative, which means that they will be aggregated with other matching rules (loyalty.rule records and loyalty.program fields). In the case of non-cumulative rules only the points from that one matching rule are used. Additionally, *fixed points per order* can be added which are applied regardless of whether or not cumulative or non-cumulative rules were applied also. |
|||
|
|||
Rewards can be of three types: |
|||
|
|||
* *Gift* - give a single unit of product for free |
|||
* *Discount* - give a discount to the whole order. It should be added at the end of the order so that the correct total price is used. |
|||
* *Resale* - allow for customer to sell back his earned points. These are calculated by setting the price on the Resale product (*resale_product.list_price* * *customer.loyalty_points*) |
|||
|
|||
All rewards can define how many points they cost (point_cost) and how many are needed so that the customer can become eligable for the reward (minimum_points). for Gift and Discount rewards minimum_points are considered only if they are greater then the point_cost for that reward (minimum_points > point_cost). For Resale products only minimum_points can be used. |
@ -0,0 +1,7 @@ |
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|||
access_loyalty_program,loyalty.program.user,model_loyalty_program,point_of_sale.group_pos_user,1,0,0,0 |
|||
access_loyalty_program_manager,loyalty.program.manager,model_loyalty_program,point_of_sale.group_pos_manager,1,1,1,1 |
|||
access_loyalty_rule,loyalty.rule.user,model_loyalty_rule,point_of_sale.group_pos_user,1,0,0,0 |
|||
access_loyalty_rule_manager,loyalty.rule.manager,model_loyalty_rule,point_of_sale.group_pos_manager,1,1,1,1 |
|||
access_loyalty_reward,loyalty.reward.user,model_loyalty_reward,point_of_sale.group_pos_user,1,0,0,0 |
|||
access_loyalty_reward_manager,loyalty.reward.manager,model_loyalty_reward,point_of_sale.group_pos_manager,1,1,1,1 |
After Width: 128 | Height: 128 | Size: 9.2 KiB |
@ -0,0 +1,449 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
|||
<head> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
|||
<meta name="generator" content="Docutils 0.14: http://docutils.sourceforge.net/" /> |
|||
<title>Loyalty Program</title> |
|||
<style type="text/css"> |
|||
|
|||
/* |
|||
:Author: David Goodger (goodger@python.org) |
|||
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $ |
|||
:Copyright: This stylesheet has been placed in the public domain. |
|||
|
|||
Default cascading style sheet for the HTML output of Docutils. |
|||
|
|||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to |
|||
customize this style sheet. |
|||
*/ |
|||
|
|||
/* used to remove borders from tables and images */ |
|||
.borderless, table.borderless td, table.borderless th { |
|||
border: 0 } |
|||
|
|||
table.borderless td, table.borderless th { |
|||
/* Override padding for "table.docutils td" with "! important". |
|||
The right padding separates the table cells. */ |
|||
padding: 0 0.5em 0 0 ! important } |
|||
|
|||
.first { |
|||
/* Override more specific margin styles with "! important". */ |
|||
margin-top: 0 ! important } |
|||
|
|||
.last, .with-subtitle { |
|||
margin-bottom: 0 ! important } |
|||
|
|||
.hidden { |
|||
display: none } |
|||
|
|||
.subscript { |
|||
vertical-align: sub; |
|||
font-size: smaller } |
|||
|
|||
.superscript { |
|||
vertical-align: super; |
|||
font-size: smaller } |
|||
|
|||
a.toc-backref { |
|||
text-decoration: none ; |
|||
color: black } |
|||
|
|||
blockquote.epigraph { |
|||
margin: 2em 5em ; } |
|||
|
|||
dl.docutils dd { |
|||
margin-bottom: 0.5em } |
|||
|
|||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { |
|||
overflow: hidden; |
|||
} |
|||
|
|||
/* Uncomment (and remove this text!) to get bold-faced definition list terms |
|||
dl.docutils dt { |
|||
font-weight: bold } |
|||
*/ |
|||
|
|||
div.abstract { |
|||
margin: 2em 5em } |
|||
|
|||
div.abstract p.topic-title { |
|||
font-weight: bold ; |
|||
text-align: center } |
|||
|
|||
div.admonition, div.attention, div.caution, div.danger, div.error, |
|||
div.hint, div.important, div.note, div.tip, div.warning { |
|||
margin: 2em ; |
|||
border: medium outset ; |
|||
padding: 1em } |
|||
|
|||
div.admonition p.admonition-title, div.hint p.admonition-title, |
|||
div.important p.admonition-title, div.note p.admonition-title, |
|||
div.tip p.admonition-title { |
|||
font-weight: bold ; |
|||
font-family: sans-serif } |
|||
|
|||
div.attention p.admonition-title, div.caution p.admonition-title, |
|||
div.danger p.admonition-title, div.error p.admonition-title, |
|||
div.warning p.admonition-title, .code .error { |
|||
color: red ; |
|||
font-weight: bold ; |
|||
font-family: sans-serif } |
|||
|
|||
/* Uncomment (and remove this text!) to get reduced vertical space in |
|||
compound paragraphs. |
|||
div.compound .compound-first, div.compound .compound-middle { |
|||
margin-bottom: 0.5em } |
|||
|
|||
div.compound .compound-last, div.compound .compound-middle { |
|||
margin-top: 0.5em } |
|||
*/ |
|||
|
|||
div.dedication { |
|||
margin: 2em 5em ; |
|||
text-align: center ; |
|||
font-style: italic } |
|||
|
|||
div.dedication p.topic-title { |
|||
font-weight: bold ; |
|||
font-style: normal } |
|||
|
|||
div.figure { |
|||
margin-left: 2em ; |
|||
margin-right: 2em } |
|||
|
|||
div.footer, div.header { |
|||
clear: both; |
|||
font-size: smaller } |
|||
|
|||
div.line-block { |
|||
display: block ; |
|||
margin-top: 1em ; |
|||
margin-bottom: 1em } |
|||
|
|||
div.line-block div.line-block { |
|||
margin-top: 0 ; |
|||
margin-bottom: 0 ; |
|||
margin-left: 1.5em } |
|||
|
|||
div.sidebar { |
|||
margin: 0 0 0.5em 1em ; |
|||
border: medium outset ; |
|||
padding: 1em ; |
|||
background-color: #ffffee ; |
|||
width: 40% ; |
|||
float: right ; |
|||
clear: right } |
|||
|
|||
div.sidebar p.rubric { |
|||
font-family: sans-serif ; |
|||
font-size: medium } |
|||
|
|||
div.system-messages { |
|||
margin: 5em } |
|||
|
|||
div.system-messages h1 { |
|||
color: red } |
|||
|
|||
div.system-message { |
|||
border: medium outset ; |
|||
padding: 1em } |
|||
|
|||
div.system-message p.system-message-title { |
|||
color: red ; |
|||
font-weight: bold } |
|||
|
|||
div.topic { |
|||
margin: 2em } |
|||
|
|||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, |
|||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { |
|||
margin-top: 0.4em } |
|||
|
|||
h1.title { |
|||
text-align: center } |
|||
|
|||
h2.subtitle { |
|||
text-align: center } |
|||
|
|||
hr.docutils { |
|||
width: 75% } |
|||
|
|||
img.align-left, .figure.align-left, object.align-left, table.align-left { |
|||
clear: left ; |
|||
float: left ; |
|||
margin-right: 1em } |
|||
|
|||
img.align-right, .figure.align-right, object.align-right, table.align-right { |
|||
clear: right ; |
|||
float: right ; |
|||
margin-left: 1em } |
|||
|
|||
img.align-center, .figure.align-center, object.align-center { |
|||
display: block; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
} |
|||
|
|||
table.align-center { |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
} |
|||
|
|||
.align-left { |
|||
text-align: left } |
|||
|
|||
.align-center { |
|||
clear: both ; |
|||
text-align: center } |
|||
|
|||
.align-right { |
|||
text-align: right } |
|||
|
|||
/* reset inner alignment in figures */ |
|||
div.align-right { |
|||
text-align: inherit } |
|||
|
|||
/* div.align-center * { */ |
|||
/* text-align: left } */ |
|||
|
|||
.align-top { |
|||
vertical-align: top } |
|||
|
|||
.align-middle { |
|||
vertical-align: middle } |
|||
|
|||
.align-bottom { |
|||
vertical-align: bottom } |
|||
|
|||
ol.simple, ul.simple { |
|||
margin-bottom: 1em } |
|||
|
|||
ol.arabic { |
|||
list-style: decimal } |
|||
|
|||
ol.loweralpha { |
|||
list-style: lower-alpha } |
|||
|
|||
ol.upperalpha { |
|||
list-style: upper-alpha } |
|||
|
|||
ol.lowerroman { |
|||
list-style: lower-roman } |
|||
|
|||
ol.upperroman { |
|||
list-style: upper-roman } |
|||
|
|||
p.attribution { |
|||
text-align: right ; |
|||
margin-left: 50% } |
|||
|
|||
p.caption { |
|||
font-style: italic } |
|||
|
|||
p.credits { |
|||
font-style: italic ; |
|||
font-size: smaller } |
|||
|
|||
p.label { |
|||
white-space: nowrap } |
|||
|
|||
p.rubric { |
|||
font-weight: bold ; |
|||
font-size: larger ; |
|||
color: maroon ; |
|||
text-align: center } |
|||
|
|||
p.sidebar-title { |
|||
font-family: sans-serif ; |
|||
font-weight: bold ; |
|||
font-size: larger } |
|||
|
|||
p.sidebar-subtitle { |
|||
font-family: sans-serif ; |
|||
font-weight: bold } |
|||
|
|||
p.topic-title { |
|||
font-weight: bold } |
|||
|
|||
pre.address { |
|||
margin-bottom: 0 ; |
|||
margin-top: 0 ; |
|||
font: inherit } |
|||
|
|||
pre.literal-block, pre.doctest-block, pre.math, pre.code { |
|||
margin-left: 2em ; |
|||
margin-right: 2em } |
|||
|
|||
pre.code .ln { color: grey; } /* line numbers */ |
|||
pre.code, code { background-color: #eeeeee } |
|||
pre.code .comment, code .comment { color: #5C6576 } |
|||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } |
|||
pre.code .literal.string, code .literal.string { color: #0C5404 } |
|||
pre.code .name.builtin, code .name.builtin { color: #352B84 } |
|||
pre.code .deleted, code .deleted { background-color: #DEB0A1} |
|||
pre.code .inserted, code .inserted { background-color: #A3D289} |
|||
|
|||
span.classifier { |
|||
font-family: sans-serif ; |
|||
font-style: oblique } |
|||
|
|||
span.classifier-delimiter { |
|||
font-family: sans-serif ; |
|||
font-weight: bold } |
|||
|
|||
span.interpreted { |
|||
font-family: sans-serif } |
|||
|
|||
span.option { |
|||
white-space: nowrap } |
|||
|
|||
span.pre { |
|||
white-space: pre } |
|||
|
|||
span.problematic { |
|||
color: red } |
|||
|
|||
span.section-subtitle { |
|||
/* font-size relative to parent (h1..h6 element) */ |
|||
font-size: 80% } |
|||
|
|||
table.citation { |
|||
border-left: solid 1px gray; |
|||
margin-left: 1px } |
|||
|
|||
table.docinfo { |
|||
margin: 2em 4em } |
|||
|
|||
table.docutils { |
|||
margin-top: 0.5em ; |
|||
margin-bottom: 0.5em } |
|||
|
|||
table.footnote { |
|||
border-left: solid 1px black; |
|||
margin-left: 1px } |
|||
|
|||
table.docutils td, table.docutils th, |
|||
table.docinfo td, table.docinfo th { |
|||
padding-left: 0.5em ; |
|||
padding-right: 0.5em ; |
|||
vertical-align: top } |
|||
|
|||
table.docutils th.field-name, table.docinfo th.docinfo-name { |
|||
font-weight: bold ; |
|||
text-align: left ; |
|||
white-space: nowrap ; |
|||
padding-left: 0 } |
|||
|
|||
/* "booktabs" style (no vertical lines) */ |
|||
table.docutils.booktabs { |
|||
border: 0px; |
|||
border-top: 2px solid; |
|||
border-bottom: 2px solid; |
|||
border-collapse: collapse; |
|||
} |
|||
table.docutils.booktabs * { |
|||
border: 0px; |
|||
} |
|||
table.docutils.booktabs th { |
|||
border-bottom: thin solid; |
|||
text-align: left; |
|||
} |
|||
|
|||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, |
|||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { |
|||
font-size: 100% } |
|||
|
|||
ul.auto-toc { |
|||
list-style-type: none } |
|||
|
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div class="document" id="loyalty-program"> |
|||
<h1 class="title">Loyalty Program</h1> |
|||
|
|||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|||
!! This file is generated by oca-gen-addon-readme !! |
|||
!! changes will be overwritten. !! |
|||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> |
|||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/pos/tree/11.0/pos_loyalty"><img alt="OCA/pos" src="https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/pos-11-0/pos-11-0-pos_loyalty"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/184/11.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p> |
|||
<p>This module allows you to define a loyalty program in the point of sale, |
|||
where the customers earn loyalty points and get rewards.</p> |
|||
<p>This module is a forward-port to v10 of the pos_loyalty module from Odoo’s |
|||
saas-6 branch. |
|||
The functionality was moved to the Enterprise edition in later versions.</p> |
|||
<p><strong>Table of contents</strong></p> |
|||
<div class="contents local topic" id="contents"> |
|||
<ul class="simple"> |
|||
<li><a class="reference internal" href="#configuration" id="id1">Configuration</a></li> |
|||
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li> |
|||
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li> |
|||
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul> |
|||
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li> |
|||
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li> |
|||
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li> |
|||
</ul> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="configuration"> |
|||
<h1><a class="toc-backref" href="#id1">Configuration</a></h1> |
|||
<p>To use this module, you need to:</p> |
|||
<ul class="simple"> |
|||
<li>Go to <em>Point of Sale > Configuration > Loyalty Programs</em> and define a new loyalty program with specific rules and rewards.</li> |
|||
<li>Assign the loyalty program to the desired Point of Sale.</li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="usage"> |
|||
<h1><a class="toc-backref" href="#id2">Usage</a></h1> |
|||
<p>The Loyalty Program defines rules for acquiring points and rewards on which they can be spent.</p> |
|||
<p>Rules can be defined globally for all products (fields on loyalty.program) and / or rules that are applied only on specific product or PoS category (loyalty.rule records) on a <em>points per product sold</em> or <em>points per currency spent</em> basis. The specific rules (loyalty.rule) can be defined as cumulative, which means that they will be aggregated with other matching rules (loyalty.rule records and loyalty.program fields). In the case of non-cumulative rules only the points from that one matching rule are used. Additionally, <em>fixed points per order</em> can be added which are applied regardless of whether or not cumulative or non-cumulative rules were applied also.</p> |
|||
<p>Rewards can be of three types:</p> |
|||
<ul class="simple"> |
|||
<li><em>Gift</em> - give a single unit of product for free</li> |
|||
<li><em>Discount</em> - give a discount to the whole order. It should be added at the end of the order so that the correct total price is used.</li> |
|||
<li><em>Resale</em> - allow for customer to sell back his earned points. These are calculated by setting the price on the Resale product (<em>resale_product.list_price</em> * <em>customer.loyalty_points</em>)</li> |
|||
</ul> |
|||
<p>All rewards can define how many points they cost (point_cost) and how many are needed so that the customer can become eligable for the reward (minimum_points). for Gift and Discount rewards minimum_points are considered only if they are greater then the point_cost for that reward (minimum_points > point_cost). For Resale products only minimum_points can be used.</p> |
|||
</div> |
|||
<div class="section" id="bug-tracker"> |
|||
<h1><a class="toc-backref" href="#id3">Bug Tracker</a></h1> |
|||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/pos/issues">GitHub Issues</a>. |
|||
In case of trouble, please check there if your issue has already been reported. |
|||
If you spotted it first, help us smashing it by providing a detailed and welcomed |
|||
<a class="reference external" href="https://github.com/OCA/pos/issues/new?body=module:%20pos_loyalty%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p> |
|||
<p>Do not contact contributors directly about support or help with technical issues.</p> |
|||
</div> |
|||
<div class="section" id="credits"> |
|||
<h1><a class="toc-backref" href="#id4">Credits</a></h1> |
|||
<div class="section" id="authors"> |
|||
<h2><a class="toc-backref" href="#id5">Authors</a></h2> |
|||
<ul class="simple"> |
|||
<li>OpenERP SA</li> |
|||
<li>RGB Consulting SL</li> |
|||
<li>Lambda IS</li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="contributors"> |
|||
<h2><a class="toc-backref" href="#id6">Contributors</a></h2> |
|||
<ul class="simple"> |
|||
<li>RGB Consulting SL (<a class="reference external" href="http://www.rgbconsulting.com">http://www.rgbconsulting.com</a>)</li> |
|||
<li>Forward-port from Odoo SA saas-6 branch</li> |
|||
<li>Kiril Vangelovski <<a class="reference external" href="mailto:kiril@lambda-is.com">kiril@lambda-is.com</a>></li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="maintainers"> |
|||
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2> |
|||
<p>This module is maintained by the OCA.</p> |
|||
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a> |
|||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use.</p> |
|||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/pos/tree/11.0/pos_loyalty">OCA/pos</a> project on GitHub.</p> |
|||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,21 @@ |
|||
|
|||
.pos .order .summary .loyalty-points{ |
|||
margin-left: 20px; |
|||
float: left; |
|||
padding: 10px; |
|||
max-width: 216px; |
|||
text-align: left; |
|||
color: #6EC89B; |
|||
background: rgba(110, 200, 155, 0.17); |
|||
border-radius: 3px; |
|||
} |
|||
.pos .order .summary .loyalty-points.negative{ |
|||
color: #C86E6E; |
|||
background: rgba(200, 110, 110, 0.17); |
|||
} |
|||
.pos .order .summary .loyalty-points-total { |
|||
border-top: solid 2px; |
|||
text-align: center; |
|||
padding-top: 4px; |
|||
margin-top: 4px; |
|||
} |
@ -0,0 +1,477 @@ |
|||
/* Copyright 2004-2010 OpenERP SA |
|||
* Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com)
|
|||
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
|||
|
|||
odoo.define('pos_loyalty.loyalty_program', function(require) { |
|||
"use strict" |
|||
|
|||
var models = require('point_of_sale.models'); |
|||
var screens = require('point_of_sale.screens'); |
|||
|
|||
var utils = require('web.utils'); |
|||
var round_pr = utils.round_precision; |
|||
|
|||
var core = require('web.core'); |
|||
var QWeb = core.qweb; |
|||
var _t = core._t; |
|||
|
|||
models.load_fields('res.partner', 'loyalty_points'); |
|||
|
|||
models.load_models([{ |
|||
model: 'loyalty.program', |
|||
condition: function(self) { |
|||
return !!self.config.loyalty_id[0]; |
|||
}, |
|||
fields: ['name', 'pp_currency', 'pp_product', 'pp_order', 'rounding'], |
|||
domain: function(self) { |
|||
return [ |
|||
['id', '=', self.config.loyalty_id[0]] |
|||
]; |
|||
}, |
|||
loaded: function(self, loyalties) { |
|||
self.loyalty = loyalties[0]; |
|||
}, |
|||
}, { |
|||
model: 'loyalty.rule', |
|||
condition: function(self) { |
|||
return !!self.loyalty; |
|||
}, |
|||
fields: ['name', 'type', 'product_id', 'category_id', 'cumulative', 'pp_product', 'pp_currency'], |
|||
domain: function(self) { |
|||
return [ |
|||
['loyalty_program_id', '=', self.loyalty.id] |
|||
]; |
|||
}, |
|||
loaded: function(self, rules) { |
|||
|
|||
self.loyalty.rules = rules; |
|||
self.loyalty.rules_by_product_id = {}; |
|||
self.loyalty.rules_by_category_id = {}; |
|||
|
|||
function update_rules(rules, rule, id) { |
|||
if (!rules[id]) { |
|||
rules[id] = [rule]; |
|||
} else if (rule.cumulative) { |
|||
rules[id].unshift(rule); |
|||
} else { |
|||
rules[id].push(rule); |
|||
} |
|||
} |
|||
|
|||
_.each(rules, function(rule) { |
|||
if (rule.type === 'product') |
|||
update_rules(self.loyalty.rules_by_product_id, rule, rule.product_id[0]) |
|||
else if (rule.type === 'category') |
|||
update_rules(self.loyalty.rules_by_category_id, rule, rule.category_id[0]); |
|||
}); |
|||
}, |
|||
}, { |
|||
model: 'loyalty.reward', |
|||
condition: function(self) { |
|||
return !!self.loyalty; |
|||
}, |
|||
fields: ['name', 'type', 'minimum_points', 'gift_product_id', 'point_cost', 'discount_product_id', 'discount', 'discount_max', 'point_product_id'], |
|||
domain: function(self) { |
|||
return [ |
|||
['loyalty_program_id', '=', self.loyalty.id] |
|||
]; |
|||
}, |
|||
loaded: function(self, rewards) { |
|||
self.loyalty.rewards = rewards; |
|||
self.loyalty.rewards_by_id = {}; |
|||
for (var i = 0; i < rewards.length; i++) { |
|||
self.loyalty.rewards_by_id[rewards[i].id] = rewards[i]; |
|||
} |
|||
}, |
|||
}, ], { |
|||
'after': 'product.product' |
|||
}); |
|||
|
|||
var _orderline_super = models.Orderline.prototype; |
|||
models.Orderline = models.Orderline.extend({ |
|||
get_reward: function() { |
|||
return this.pos.loyalty.rewards_by_id[this.reward_id]; |
|||
}, |
|||
set_reward: function(reward) { |
|||
this.reward_id = reward.id; |
|||
}, |
|||
export_as_JSON: function() { |
|||
var json = _orderline_super.export_as_JSON.apply(this, arguments); |
|||
json.reward_id = this.reward_id; |
|||
return json; |
|||
}, |
|||
init_from_JSON: function(json) { |
|||
_orderline_super.init_from_JSON.apply(this, arguments); |
|||
this.reward_id = json.reward_id; |
|||
}, |
|||
}); |
|||
|
|||
var _order_super = models.Order.prototype; |
|||
models.Order = models.Order.extend({ |
|||
|
|||
/* The total of points won, excluding the points spent on rewards */ |
|||
get_won_points: function() { |
|||
if (!this.pos.loyalty || !this.get_client()) { |
|||
return 0; |
|||
} |
|||
|
|||
var orderLines = this.get_orderlines(); |
|||
var rounding = this.pos.loyalty.rounding; |
|||
|
|||
var product_sold = 0; |
|||
var total_sold = 0; |
|||
var total_points = 0; |
|||
|
|||
for (var i = 0; i < orderLines.length; i++) { |
|||
var line = orderLines[i]; |
|||
var product = line.get_product(); |
|||
var rules = this.pos.loyalty.rules_by_product_id[product.id] || []; |
|||
var overriden = false; |
|||
|
|||
if (line.get_reward()) { // Reward products are ignored
|
|||
continue; |
|||
} |
|||
|
|||
for (var j = 0; j < rules.length; j++) { |
|||
var rule = rules[j]; |
|||
total_points += round_pr(line.get_quantity() * rule.pp_product, rounding); |
|||
total_points += round_pr(line.get_price_with_tax() * rule.pp_currency, rounding); |
|||
// if affected by a non cumulative rule, skip the others. (non cumulative rules are put
|
|||
// at the beginning of the list when they are loaded )
|
|||
if (!rule.cumulative) { |
|||
overriden = true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// Test the category rules
|
|||
if (product.pos_categ_id) { |
|||
var category = this.pos.db.get_category_by_id(product.pos_categ_id[0]); |
|||
while (category && !overriden) { |
|||
var rules = this.pos.loyalty.rules_by_category_id[category.id] || []; |
|||
for (var j = 0; j < rules.length; j++) { |
|||
var rule = rules[j]; |
|||
total_points += round_pr(line.get_quantity() * rule.pp_product, rounding); |
|||
total_points += round_pr(line.get_price_with_tax() * rule.pp_currency, rounding); |
|||
if (!rule.cumulative) { |
|||
overriden = true; |
|||
break; |
|||
} |
|||
} |
|||
var _category = category; |
|||
category = this.pos.db.get_category_by_id(this.pos.db.get_category_parent_id(category.id)); |
|||
if (_category === category) { |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!overriden) { |
|||
product_sold += line.get_quantity(); |
|||
total_sold += line.get_price_with_tax(); |
|||
} |
|||
} |
|||
|
|||
total_points += round_pr(total_sold * this.pos.loyalty.pp_currency, rounding); |
|||
total_points += round_pr(product_sold * this.pos.loyalty.pp_product, rounding); |
|||
total_points += round_pr(this.pos.loyalty.pp_order, rounding); |
|||
|
|||
return total_points; |
|||
}, |
|||
|
|||
/* The total number of points spent on rewards */ |
|||
get_spent_points: function() { |
|||
if (!this.pos.loyalty || !this.get_client()) { |
|||
return 0; |
|||
} else { |
|||
var lines = this.get_orderlines(); |
|||
var rounding = this.pos.loyalty.rounding; |
|||
var points = 0; |
|||
|
|||
for (var i = 0; i < lines.length; i++) { |
|||
var line = lines[i]; |
|||
var reward = line.get_reward(); |
|||
if (reward) { |
|||
if (reward.type === 'gift') { |
|||
points += round_pr(line.get_quantity() * reward.point_cost, rounding); |
|||
} else if (reward.type === 'discount') { |
|||
points += reward.point_cost; |
|||
} else if (reward.type === 'resale') { |
|||
points += (-line.get_quantity()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return points; |
|||
} |
|||
}, |
|||
|
|||
/* The total number of points lost or won after the order is validated */ |
|||
get_new_points: function() { |
|||
if (!this.pos.loyalty || !this.get_client()) { |
|||
return 0; |
|||
} else { |
|||
return round_pr(this.get_won_points() - this.get_spent_points(), this.pos.loyalty.rounding); |
|||
} |
|||
}, |
|||
|
|||
/* The total number of points that the customer will have after this order is validated */ |
|||
get_new_total_points: function() { |
|||
if (!this.pos.loyalty || !this.get_client()) { |
|||
return 0; |
|||
} else { |
|||
return round_pr(this.get_client().loyalty_points + this.get_new_points(), this.pos.loyalty.rounding); |
|||
} |
|||
}, |
|||
|
|||
/* The number of loyalty points currently owned by the customer */ |
|||
get_current_points: function() { |
|||
return this.get_client() ? this.get_client().loyalty_points : 0; |
|||
}, |
|||
|
|||
/* The total number of points spendable on rewards */ |
|||
get_spendable_points: function() { |
|||
if (!this.pos.loyalty || !this.get_client()) { |
|||
return 0; |
|||
} else { |
|||
return round_pr(this.get_client().loyalty_points - this.get_spent_points(), this.pos.loyalty.rounding); |
|||
} |
|||
}, |
|||
|
|||
has_discount_reward: function() { |
|||
var res = false; |
|||
var lines = this.get_orderlines(); |
|||
|
|||
for (var i = 0; i < lines.length; i++) { |
|||
var line = lines[i]; |
|||
var reward = line.get_reward(); |
|||
if (reward && reward.type === 'discount') { |
|||
res = true; |
|||
break; |
|||
} |
|||
} |
|||
return res; |
|||
}, |
|||
|
|||
/* The list of rewards that the current customer can get */ |
|||
get_available_rewards: function() { |
|||
var client = this.get_client(); |
|||
if (!client) { |
|||
return []; |
|||
} |
|||
var rewards = []; |
|||
var discount_reward_set = this.has_discount_reward(); |
|||
for (var i = 0; i < this.pos.loyalty.rewards.length; i++) { |
|||
var reward = this.pos.loyalty.rewards[i]; |
|||
|
|||
if (reward.minimum_points > this.get_spendable_points()) { |
|||
continue; |
|||
} else if (reward.type === 'gift' && |
|||
reward.point_cost > this.get_spendable_points()) { |
|||
continue; |
|||
} else if (reward.type === 'discount' && |
|||
(discount_reward_set || reward.point_cost > this.get_spendable_points())) { |
|||
continue; |
|||
} |
|||
rewards.push(reward); |
|||
} |
|||
return rewards; |
|||
}, |
|||
|
|||
apply_reward: function(reward) { |
|||
var client = this.get_client(); |
|||
if (!client) { |
|||
return; |
|||
} else if (reward.type === 'gift') { |
|||
var product = this.pos.db.get_product_by_id(reward.gift_product_id[0]); |
|||
|
|||
if (!product) { |
|||
return; |
|||
} |
|||
|
|||
this.add_product(product, { |
|||
price: 0, |
|||
quantity: 1, |
|||
merge: false, |
|||
extras: { |
|||
reward_id: reward.id |
|||
}, |
|||
}); |
|||
|
|||
} else if (reward.type === 'discount') { |
|||
|
|||
var crounding = this.pos.currency.rounding; |
|||
var order_total = this.get_total_with_tax(); |
|||
var discount = round_pr(order_total * reward.discount, crounding); |
|||
var discount_max = reward.discount_max |
|||
|
|||
if (discount_max && discount > discount_max) { |
|||
discount = discount_max; |
|||
} |
|||
|
|||
var product = this.pos.db.get_product_by_id(reward.discount_product_id[0]); |
|||
|
|||
if (!product) { |
|||
return; |
|||
} |
|||
|
|||
this.add_product(product, { |
|||
price: -discount, |
|||
quantity: 1, |
|||
merge: false, |
|||
extras: { |
|||
reward_id: reward.id |
|||
}, |
|||
}); |
|||
|
|||
} else if (reward.type === 'resale') { |
|||
|
|||
var lrounding = this.pos.loyalty.rounding; |
|||
var crounding = this.pos.currency.rounding; |
|||
var spendable = this.get_spendable_points(); |
|||
|
|||
var order_total = this.get_total_with_tax(); |
|||
var product = this.pos.db.get_product_by_id(reward.point_product_id[0]); |
|||
|
|||
if (!product) { |
|||
return; |
|||
} |
|||
|
|||
if (round_pr(spendable * product.price, crounding) > order_total) { |
|||
spendable = round_pr(Math.floor(order_total / product.price), lrounding); |
|||
} |
|||
|
|||
if (spendable < 0.00001) { |
|||
return; |
|||
} |
|||
|
|||
this.add_product(product, { |
|||
quantity: -spendable, |
|||
merge: false, |
|||
extras: { |
|||
reward_id: reward.id |
|||
}, |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
finalize: function() { |
|||
var client = this.get_client(); |
|||
if (client) { |
|||
client.loyalty_points = this.get_new_total_points(); |
|||
this.pos.gui.screen_instances.clientlist.partner_cache.clear_node(client.id); |
|||
} |
|||
_order_super.finalize.apply(this, arguments); |
|||
}, |
|||
|
|||
export_for_printing: function() { |
|||
var json = _order_super.export_for_printing.apply(this, arguments); |
|||
if (this.pos.loyalty && this.get_client()) { |
|||
json.loyalty = { |
|||
rounding: this.pos.loyalty.rounding || 1, |
|||
name: this.pos.loyalty.name, |
|||
client: this.get_client().name, |
|||
points_won: this.get_won_points(), |
|||
points_spent: this.get_spent_points(), |
|||
points_total: this.get_new_total_points(), |
|||
}; |
|||
} |
|||
return json; |
|||
}, |
|||
|
|||
export_as_JSON: function() { |
|||
var json = _order_super.export_as_JSON.apply(this, arguments); |
|||
json.loyalty_points = this.get_new_points(); |
|||
return json; |
|||
}, |
|||
}); |
|||
|
|||
var LoyaltyButton = screens.ActionButtonWidget.extend({ |
|||
template: 'LoyaltyButton', |
|||
button_click: function() { |
|||
var self = this; |
|||
var order = this.pos.get_order(); |
|||
var client = order.get_client(); |
|||
if (!client) { |
|||
this.gui.show_screen('clientlist'); |
|||
return; |
|||
} |
|||
|
|||
var rewards = order.get_available_rewards(); |
|||
if (rewards.length === 0) { |
|||
this.gui.show_popup('error', { |
|||
'title': _t('No Rewards Available'), |
|||
'body': _t('There are no rewards available for this customer as part of the loyalty program'), |
|||
}); |
|||
} else if (rewards.length === 1 && this.pos.loyalty.rewards.length === 1) { |
|||
order.apply_reward(rewards[0]); |
|||
} else { |
|||
var list = []; |
|||
for (var i = 0; i < rewards.length; i++) { |
|||
list.push({ |
|||
label: rewards[i].name, |
|||
item: rewards[i], |
|||
}); |
|||
} |
|||
this.gui.show_popup('selection', { |
|||
'title': _t('Please select a reward'), |
|||
'list': list, |
|||
'confirm': function(reward) { |
|||
order.apply_reward(reward); |
|||
}, |
|||
}); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
screens.define_action_button({ |
|||
'name': 'loyalty', |
|||
'widget': LoyaltyButton, |
|||
'condition': function() { |
|||
return this.pos.loyalty && this.pos.loyalty.rewards.length; |
|||
}, |
|||
}); |
|||
|
|||
screens.OrderWidget.include({ |
|||
update_summary: function() { |
|||
this._super(); |
|||
|
|||
var order = this.pos.get_order(); |
|||
|
|||
var $loypoints = $(this.el).find('.summary .loyalty-points'); |
|||
|
|||
if (this.pos.loyalty && order.get_client()) { |
|||
var points_won = order.get_won_points(); |
|||
var points_spent = order.get_spent_points(); |
|||
var points_total = order.get_new_total_points(); |
|||
$loypoints.replaceWith($(QWeb.render('LoyaltyPoints', { |
|||
widget: this, |
|||
rounding: this.pos.loyalty.rounding, |
|||
points_won: points_won, |
|||
points_spent: points_spent, |
|||
points_total: points_total, |
|||
}))); |
|||
$loypoints = $(this.el).find('.summary .loyalty-points'); |
|||
$loypoints.removeClass('oe_hidden'); |
|||
|
|||
if (points_total < 0) { |
|||
$loypoints.addClass('negative'); |
|||
} else { |
|||
$loypoints.removeClass('negative'); |
|||
} |
|||
} else { |
|||
$loypoints.empty(); |
|||
$loypoints.addClass('oe_hidden'); |
|||
} |
|||
|
|||
if (this.pos.loyalty && |
|||
order.get_client() && |
|||
this.getParent().action_buttons && |
|||
this.getParent().action_buttons.loyalty) { |
|||
|
|||
var rewards = order.get_available_rewards(); |
|||
this.getParent().action_buttons.loyalty.highlight(!!rewards.length); |
|||
} |
|||
}, |
|||
}); |
|||
}); |
@ -0,0 +1,170 @@ |
|||
// Copyright 2004-2018 Odoo SA
|
|||
// Copyright 2018 Lambda IS DOOEL <https://www.lambda-is.com>
|
|||
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|||
|
|||
odoo.define('pos_loyalty.tour.test_pos_loyalty', function(require) { |
|||
"use strict"; |
|||
|
|||
// Some of the steps are taken from the pos_basic_order tour in point_of_sale.
|
|||
// Added additional ones necessary for testing the rewards.
|
|||
|
|||
var Tour = require("web_tour.tour"); |
|||
|
|||
function add_customer(customer_name) { |
|||
return [{ |
|||
content: 'open customer screen', |
|||
trigger: '.button.set-customer', |
|||
}, { |
|||
content: 'choose customer ' + customer_name, |
|||
trigger: 'table.client-list tbody.client-list-contents tr.client-line td:contains("' + customer_name + '")', |
|||
}, { |
|||
content: 'select customer ' + customer_name, |
|||
trigger: '.button.next:contains("Set Customer")', |
|||
}, { |
|||
content: 'Check if customer ' + customer_name + ' is added', |
|||
trigger: '.button.set-customer:contains("' + customer_name + '")', |
|||
run: function() {}, // it's a check
|
|||
}]; |
|||
} |
|||
|
|||
function add_reward(reward_name) { |
|||
return [{ |
|||
content: 'open rewards screen', |
|||
trigger: '.control-button:contains("Rewards")', |
|||
}, { |
|||
content: 'choose reward', |
|||
trigger: '.selection-item:contains("' + reward_name + '")', |
|||
}] |
|||
} |
|||
|
|||
function add_product_to_order(product_name) { |
|||
return [{ |
|||
content: 'buy ' + product_name, |
|||
trigger: '.product-list .product-name:contains("' + product_name + '")', |
|||
}, { |
|||
content: 'the ' + product_name + ' have been added to the order', |
|||
trigger: '.order .product-name:contains("' + product_name + '")', |
|||
run: function() {}, // it's a check
|
|||
}]; |
|||
} |
|||
|
|||
function verify_order_product(product_name) { |
|||
return [{ |
|||
content: 'check if ' + product_name + ' is in order', |
|||
trigger: '.orderline .product-name:contains("' + product_name + '")', |
|||
run: function() {}, // it's a check
|
|||
}] |
|||
} |
|||
|
|||
function generate_keypad_steps(amount_str, keypad_selector) { |
|||
var i, steps = [], |
|||
current_char; |
|||
for (i = 0; i < amount_str.length; ++i) { |
|||
current_char = amount_str[i]; |
|||
steps.push({ |
|||
content: 'press ' + current_char + ' on payment keypad', |
|||
trigger: keypad_selector + ' .input-button:contains("' + current_char + '"):visible' |
|||
}); |
|||
} |
|||
|
|||
return steps; |
|||
} |
|||
|
|||
function generate_payment_screen_keypad_steps(amount_str) { |
|||
return generate_keypad_steps(amount_str, '.payment-numpad'); |
|||
} |
|||
|
|||
function generate_product_screen_keypad_steps(amount_str) { |
|||
return generate_keypad_steps(amount_str, '.numpad'); |
|||
} |
|||
|
|||
function verify_order_total(total_str) { |
|||
return [{ |
|||
content: 'order total contains ' + total_str, |
|||
trigger: '.order .total .value:contains("' + total_str + '")', |
|||
run: function() {}, // it's a check
|
|||
}]; |
|||
} |
|||
|
|||
function goto_payment_screen_and_select_payment_method() { |
|||
return [{ |
|||
content: "go to payment screen", |
|||
trigger: '.button.pay', |
|||
}, { |
|||
content: "pay with cash", |
|||
trigger: '.paymentmethod:contains("Cash")', |
|||
}]; |
|||
} |
|||
|
|||
function finish_order() { |
|||
return [{ |
|||
content: "validate the order", |
|||
trigger: '.button.next:visible', |
|||
}, { |
|||
content: "verify that the order is being sent to the backend", |
|||
trigger: ".js_connecting:visible", |
|||
run: function() {}, // it's a check
|
|||
}, { |
|||
content: "verify that the order has been succesfully sent to the backend", |
|||
trigger: ".js_connected:visible", |
|||
run: function() {}, // it's a check
|
|||
}, { |
|||
content: "next order", |
|||
trigger: '.button.next:visible', |
|||
}]; |
|||
} |
|||
|
|||
var steps = [{ |
|||
content: 'waiting for loading to finish', |
|||
trigger: '.o_main_content:has(.loader:hidden)', |
|||
run: function() {}, // it's a check
|
|||
}]; |
|||
|
|||
steps = steps.concat(add_customer('Agrolait')); |
|||
steps = steps.concat(add_product_to_order('Peaches')); |
|||
steps = steps.concat(verify_order_total('5.10')); |
|||
|
|||
steps = steps.concat(add_product_to_order('Peaches')); // buy another kg of peaches
|
|||
steps = steps.concat(verify_order_total('10.20')); |
|||
steps = steps.concat(goto_payment_screen_and_select_payment_method()); |
|||
steps = steps.concat(generate_payment_screen_keypad_steps("12.20")); |
|||
|
|||
steps = steps.concat([{ |
|||
content: "verify tendered", |
|||
trigger: '.col-tendered:contains("12.20")', |
|||
run: function() {}, // it's a check
|
|||
}, { |
|||
content: "verify change", |
|||
trigger: '.col-change:contains("2.00")', |
|||
run: function() {}, // it's a check
|
|||
}]); |
|||
|
|||
steps = steps.concat(finish_order()); |
|||
|
|||
Tour.register('test_pos_loyalty_acquire_points', { |
|||
test: true, |
|||
url: '/pos/web' |
|||
}, steps); |
|||
|
|||
steps = [{ |
|||
content: 'waiting for loading to finish', |
|||
trigger: '.o_main_content:has(.loader:hidden)', |
|||
run: function() {}, // it's a check
|
|||
}]; |
|||
steps = steps.concat(add_customer('Agrolait')); |
|||
steps = steps.concat(add_reward('Free Peaches')); |
|||
steps = steps.concat(verify_order_product('Peaches')); |
|||
steps = steps.concat(verify_order_total('0.00')); |
|||
steps = steps.concat(goto_payment_screen_and_select_payment_method()); |
|||
steps = steps.concat([{ |
|||
content: "verify tendered", |
|||
trigger: '.col-tendered:contains("0.00")', |
|||
run: function() {}, // it's a check
|
|||
}]); |
|||
steps = steps.concat(finish_order()); |
|||
|
|||
Tour.register('test_pos_loyalty_spend_points', { |
|||
test: true, |
|||
url: '/pos/web' |
|||
}, steps); |
|||
}) |
@ -0,0 +1,85 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<templates id="template" xml:space="preserve"> |
|||
|
|||
<t t-name="LoyaltyPoints"> |
|||
<div class='loyalty-points'> |
|||
<div class='loyalty-points-title'>Points</div> |
|||
<t t-if='points_won'> |
|||
<div class="loyalty-points-won"> |
|||
<span class='value'>+<t t-esc='widget.format_pr(points_won,rounding)'/></span> |
|||
</div> |
|||
</t> |
|||
<t t-if='points_spent'> |
|||
<div class="loyalty-points-spent"> |
|||
<span class='value'>-<t t-esc='widget.format_pr(points_spent,rounding)'/></span> |
|||
</div> |
|||
</t> |
|||
<div class='loyalty-points-total'> |
|||
<span class='value'><t t-esc='widget.format_pr(points_total,rounding)' /></span> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
|
|||
<t t-name="LoyaltyButton"> |
|||
<div class='control-button'> |
|||
<i class='fa fa-star' /> Rewards |
|||
</div> |
|||
</t> |
|||
|
|||
<t t-extend='OrderWidget'> |
|||
<t t-jquery='.summary' t-operation='prepend'> |
|||
<div class='loyalty-points oe_hidden'>.</div> |
|||
</t> |
|||
</t> |
|||
|
|||
<t t-extend='ClientLine'> |
|||
<t t-jquery='.client-line' t-operation='append'> |
|||
<td> |
|||
<t t-esc='widget.format_pr(partner.loyalty_points || 0, widget.pos.loyalty ? widget.pos.loyalty.rounding : 1.0)' /> |
|||
</td> |
|||
</t> |
|||
</t> |
|||
|
|||
<t t-extend="XmlReceipt"> |
|||
<t t-jquery='.before-footer' t-operation='append'> |
|||
<t t-if='receipt.loyalty'> |
|||
<div class='loyalty' value-decimals='2' value-autoint='on'> |
|||
<div>--------------------------------</div> |
|||
<br/> |
|||
<div size='double-height'><t t-esc='receipt.loyalty.name'/></div> |
|||
<br /> |
|||
<div><t t-esc='receipt.loyalty.client' /></div> |
|||
<br/> |
|||
<t t-if='receipt.loyalty.points_won'> |
|||
<line><left>Points Won</left><right><value><t t-esc='receipt.loyalty.points_won' /></value></right></line> |
|||
</t> |
|||
<t t-if='receipt.loyalty.points_spent'> |
|||
<line><left>Points Spent</left><right><value><t t-esc='receipt.loyalty.points_spent' /></value></right></line> |
|||
</t> |
|||
<t t-if='receipt.loyalty.points_total'> |
|||
<line><left>Total Points</left><right><value><t t-esc='receipt.loyalty.points_total' /></value></right></line> |
|||
</t> |
|||
<br /> |
|||
</div> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
|
|||
<t t-extend='ClientDetails'> |
|||
<t t-jquery='.client-details-right' t-operation='prepend'> |
|||
<div class='client-detail'> |
|||
<span class='label'>Points</span> |
|||
<span class='detail client-points'> |
|||
<t t-esc='widget.format_pr(partner.loyalty_points || 0, widget.pos.loyalty ? widget.pos.loyalty.rounding : 1.0)' /> |
|||
</span> |
|||
</div> |
|||
</t> |
|||
</t> |
|||
|
|||
<t t-extend='ClientListScreenWidget'> |
|||
<t t-jquery='.client-list thead tr' t-operation='append'> |
|||
<th>Points</th> |
|||
</t> |
|||
</t> |
|||
|
|||
</templates> |
@ -0,0 +1,2 @@ |
|||
|
|||
from . import test_pos_loyalty |
@ -0,0 +1,74 @@ |
|||
# Copyright 2004-2018 Odoo SA |
|||
# Copyright 2018 Lambda IS DOOEL <https://www.lambda-is.com> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from odoo.api import Environment |
|||
from odoo.tests import HttpCase |
|||
|
|||
|
|||
class TestPOSLoyalty(HttpCase): |
|||
|
|||
def test_pos_loyalty(self): |
|||
cr = self.registry.cursor() |
|||
assert cr == self.registry.test_cr |
|||
env = Environment(cr, self.uid, {}) |
|||
main_pos_config = env.ref('point_of_sale.pos_config_main') |
|||
target_product = env.ref('point_of_sale.peche') |
|||
free_product = env.ref('point_of_sale.Onions') |
|||
customer = env.ref('base.res_partner_2') |
|||
loyalty_program = env['loyalty.program'].create({ |
|||
'name': 'foo', |
|||
'rule_ids': [(0, 0, { |
|||
'name': 'Peaches', |
|||
'type': 'product', |
|||
'product_id': target_product.id, |
|||
'pp_product': 10, |
|||
})], |
|||
'reward_ids': [(0, 0, { |
|||
'name': 'Free Peaches', |
|||
'type': 'gift', |
|||
'gift_product_id': target_product.id, |
|||
'point_cost': 20, |
|||
'minimum_points': 20, |
|||
}), (0, 0, { |
|||
'name': 'Free Onions', |
|||
'type': 'gift', |
|||
'gift_product_id': free_product.id, |
|||
'point_cost': 20, |
|||
'minimum_points': 20, |
|||
})] |
|||
}) |
|||
main_pos_config.write({'loyalty_id': loyalty_program.id}) |
|||
main_pos_config.open_session_cb() |
|||
|
|||
# needed because tests are run before the module is marked as |
|||
# installed. In js web will only load qweb coming from modules |
|||
# that are returned by the backend in module_boot. Without |
|||
# this you end up with js, css but no qweb. |
|||
env['ir.module.module'].search( |
|||
[('name', '=', 'pos_loyalty')], limit=1).state = 'installed' |
|||
|
|||
cr.release() |
|||
|
|||
# Process an order with 2kg of Peaches which should |
|||
# add 20 loyalty points |
|||
self.phantom_js("/pos/web", |
|||
"odoo.__DEBUG__.services['web_tour.tour'].run(" |
|||
"'test_pos_loyalty_acquire_points')", |
|||
"odoo.__DEBUG__.services['web_tour.tour'].tours" |
|||
".test_pos_loyalty_acquire_points.ready", |
|||
login="admin") |
|||
|
|||
self.assertEqual(customer.loyalty_points, 20) |
|||
|
|||
# Spend 20 loyalty points on "Free Peaches" reward |
|||
self.phantom_js("/pos/web", |
|||
"odoo.__DEBUG__.services['web_tour.tour'].run(" |
|||
"'test_pos_loyalty_spend_points')", |
|||
"odoo.__DEBUG__.services['web_tour.tour'].tours" |
|||
".test_pos_loyalty_spend_points.ready", |
|||
login="admin") |
|||
|
|||
customer_points = customer.read( |
|||
['loyalty_points'])[0]['loyalty_points'] |
|||
self.assertEqual(customer_points, 0) |
@ -0,0 +1,83 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<record id="loyalty_program_form_view" model="ir.ui.view"> |
|||
<field name="name">loyalty.program.form</field> |
|||
<field name="model">loyalty.program</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Loyalty Program"> |
|||
<sheet> |
|||
<div class="oe_title"> |
|||
<label class="oe_edit_only" for="name"/> |
|||
<h1> |
|||
<field name="name" class="oe_inline"/> |
|||
</h1> |
|||
</div> |
|||
|
|||
<group> |
|||
<group> |
|||
<field name="pp_product"/> |
|||
<field name="pp_currency"/> |
|||
</group> |
|||
<group> |
|||
<field name="pp_order"/> |
|||
<field name="rounding"/> |
|||
</group> |
|||
</group> |
|||
|
|||
<separator string="Rules" colspan="4"/> |
|||
<p>Rules define how loyalty points are earned for specific products or categories</p> |
|||
<field name="rule_ids" colspan="4" nolabel="1"> |
|||
<tree string="Rules"> |
|||
<field name="name"/> |
|||
<field name="pp_product"/> |
|||
<field name="pp_currency"/> |
|||
<field name="cumulative"/> |
|||
</tree> |
|||
</field> |
|||
|
|||
<separator string="Rewards" colspan="4"/> |
|||
<p>Reward the customer with gifts or discounts for loyalty points</p> |
|||
<field name="reward_ids" colspan="4" nolabel="1"> |
|||
<tree string="Rewards"> |
|||
<field name="name"/> |
|||
<field name="type"/> |
|||
</tree> |
|||
</field> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="loyalty_program_tree_view" model="ir.ui.view"> |
|||
<field name="name">loyalty.program.tree</field> |
|||
<field name="model">loyalty.program</field> |
|||
<field name="arch" type="xml"> |
|||
<tree string="Loyalty Programs"> |
|||
<field name="name"/> |
|||
</tree> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="loyalty_program_action" model="ir.actions.act_window"> |
|||
<field name="name">Loyalty Programs</field> |
|||
<field name="type">ir.actions.act_window</field> |
|||
<field name="res_model">loyalty.program</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">tree,form</field> |
|||
<field name="help" type="html"> |
|||
<p class="oe_view_nocontent_create"> |
|||
Click create to define a Loyalty Program. |
|||
</p> |
|||
<p> |
|||
Loyalty Programs allow you customers to earn points |
|||
and rewards when purchasing from your shops. |
|||
</p> |
|||
</field> |
|||
</record> |
|||
|
|||
<menuitem parent="point_of_sale.menu_point_config_product" |
|||
action="loyalty_program_action" |
|||
id="loyalty_program_menu" |
|||
sequence="30" |
|||
groups="point_of_sale.group_pos_manager"/> |
|||
</odoo> |
@ -0,0 +1,37 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<record id="loyalty_reward_form_view" model="ir.ui.view"> |
|||
<field name="name">loyalty.reward.form</field> |
|||
<field name="model">loyalty.reward</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Loyalty Reward"> |
|||
<div class="oe_title"> |
|||
<label class="oe_edit_only" for="name"/> |
|||
<h1> |
|||
<field name="name" class="oe_inline"/> |
|||
</h1> |
|||
</div> |
|||
<group> |
|||
<field name="type"/> |
|||
</group> |
|||
<group > |
|||
<field name="gift_product_id" |
|||
attrs="{'invisible':[('type','!=','gift')], 'required':[('type','=','gift')]}"/> |
|||
<field name="discount" |
|||
attrs="{'invisible':[('type','!=','discount')]}"/> |
|||
<field name="discount_max" |
|||
attrs="{'invisible':[('type','!=','discount')]}"/> |
|||
<field name="discount_product_id" |
|||
attrs="{'invisible':[('type','!=','discount')], 'required':[('type','=','discount')]}"/> |
|||
<field name="point_product_id" |
|||
attrs="{'invisible': [('type','!=','resale')], 'required': [('type','=','resale')]}"/> |
|||
</group> |
|||
<group> |
|||
<field name="point_cost" |
|||
attrs="{'invisible':[('type','=','resale')]}"/> |
|||
<field name="minimum_points"/> |
|||
</group> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,31 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<record id="loyalty_rule_form_view" model="ir.ui.view"> |
|||
<field name="name">loyalty.rule.form</field> |
|||
<field name="model">loyalty.rule</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Loyalty Rule"> |
|||
<div class="oe_title"> |
|||
<label class="oe_edit_only" for="name"/> |
|||
<h1> |
|||
<field name="name" class="oe_inline"/> |
|||
</h1> |
|||
</div> |
|||
<group> |
|||
<group> |
|||
<field name="type"/> |
|||
</group> |
|||
<group> |
|||
<field name="product_id" attrs="{'invisible': [('type','!=','product')], 'required': [('type','=','product')]}"/> |
|||
<field name="category_id" attrs="{'invisible': [('type','!=','category')], 'required': [('type','=','category')]}"/> |
|||
</group> |
|||
</group> |
|||
<group col="6"> |
|||
<field name="pp_product"/> |
|||
<field name="pp_currency"/> |
|||
<field name="cumulative"/> |
|||
</group> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,23 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<record id="pos_config_view_form" model="ir.ui.view"> |
|||
<field name="name">pos.config.form</field> |
|||
<field name="model">pos.config</field> |
|||
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//div[@id='pricelist_setting']" position="after"> |
|||
<div class="col-xs-12 col-md-6 o_setting_box" title="abc"> |
|||
<div class="o_setting_right_pane"> |
|||
<label for="loyalty_id" string="Loyalty Program (OCA)"/> |
|||
<div class="text-muted"> |
|||
Loyalty program that will be available in this PoS |
|||
</div> |
|||
<div class="content-group mt16"> |
|||
<field name="loyalty_id"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<record id="pos_order_form_view" model="ir.ui.view"> |
|||
<field name="name">pos.order.form</field> |
|||
<field name="model">pos.order</field> |
|||
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//page[@name='extra']" position="inside"> |
|||
<group name="loyalty" string="Loyalty Programs"> |
|||
<field name="loyalty_points"/> |
|||
</group> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,18 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<record id="partner_property_form_view" model="ir.ui.view"> |
|||
<field name="name">partner.property.form</field> |
|||
<field name="model">res.partner</field> |
|||
<field name="inherit_id" ref="point_of_sale.view_partner_property_form"/> |
|||
<field name="arch" type="xml"> |
|||
<button name="%(point_of_sale.action_pos_pos_form)d" position="before"> |
|||
<button class="oe_stat_button" type="action" name="%(point_of_sale.action_pos_pos_form)d" |
|||
context="{'search_default_partner_id': active_id,'default_partner_id': active_id}" |
|||
attrs="{'invisible': ['|', ('customer', '=', False), ('pos_order_count', '=', 0)]}" |
|||
icon="fa-star"> |
|||
<field name="loyalty_points" widget="statinfo"/> |
|||
</button> |
|||
</button> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,9 @@ |
|||
<?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="/pos_loyalty/static/src/js/pos.js"></script> |
|||
<link rel="stylesheet" href="/pos_loyalty/static/src/css/pos.css"/> |
|||
</xpath> |
|||
</template> |
|||
</odoo> |
@ -0,0 +1,86 @@ |
|||
================ |
|||
Pos Mail Receipt |
|||
================ |
|||
|
|||
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|||
!! This file is generated by oca-gen-addon-readme !! |
|||
!! changes will be overwritten. !! |
|||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|||
|
|||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png |
|||
:target: https://odoo-community.org/page/development-status |
|||
:alt: Beta |
|||
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png |
|||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github |
|||
:target: https://github.com/OCA/pos/tree/12.0/pos_mail_receipt |
|||
:alt: OCA/pos |
|||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png |
|||
:target: https://translation.odoo-community.org/projects/pos-12-0/pos-12-0-pos_mail_receipt |
|||
:alt: Translate me on Weblate |
|||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png |
|||
:target: https://runbot.odoo-community.org/runbot/184/12.0 |
|||
:alt: Try me on Runbot |
|||
|
|||
|badge1| |badge2| |badge3| |badge4| |badge5| |
|||
|
|||
This modules allows you to send the PoS receipt by e-mail. |
|||
|
|||
Instead of printing the ticket you can choose to send it be e-mail. |
|||
If you know the customer's e-mail, it will use it. |
|||
If not you will be prompted for it. |
|||
|
|||
**Table of contents** |
|||
|
|||
.. contents:: |
|||
:local: |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
#. Open a new PoS session. |
|||
#. Make an order and validate it. |
|||
#. You should see the company logo in the receipt preview. |
|||
|
|||
Bug Tracker |
|||
=========== |
|||
|
|||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/pos/issues>`_. |
|||
In case of trouble, please check there if your issue has already been reported. |
|||
If you spotted it first, help us smashing it by providing a detailed and welcomed |
|||
`feedback <https://github.com/OCA/pos/issues/new?body=module:%20pos_mail_receipt%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. |
|||
|
|||
Do not contact contributors directly about support or help with technical issues. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Authors |
|||
~~~~~~~ |
|||
|
|||
* Coop IT Easy SCRLfs |
|||
|
|||
Contributors |
|||
~~~~~~~~~~~~ |
|||
|
|||
* `Coop IT Easy SCRLfs <https://coopiteasy.be>`_: |
|||
|
|||
* Pierrick Brun |
|||
|
|||
Maintainers |
|||
~~~~~~~~~~~ |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use. |
|||
|
|||
This module is part of the `OCA/pos <https://github.com/OCA/pos/tree/12.0/pos_mail_receipt>`_ project on GitHub. |
|||
|
|||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. |
@ -0,0 +1 @@ |
|||
from . import models |
@ -0,0 +1,14 @@ |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
|||
|
|||
{ |
|||
"name": "Pos Mail Receipt", |
|||
"category": "Point Of Sale", |
|||
"version": "12.0.1.0.0", |
|||
"author": "Coop IT Easy SCRLfs, " "Odoo Community Association (OCA)", |
|||
"website": "https://github.com/OCA/pos", |
|||
"license": "AGPL-3", |
|||
"depends": ["point_of_sale"], |
|||
"data": ["templates/assets.xml", "data/email.xml"], |
|||
"qweb": ["static/src/xml/pos.xml"], |
|||
"installable": True, |
|||
} |
@ -0,0 +1,19 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
|
|||
<data noupdate="0"> |
|||
<!--Email template --> |
|||
<record id="email_send_ticket" model="mail.template"> |
|||
<field name="name">Send Receipt</field> |
|||
<field name="email_from">${(object.user_id.email and '%s <%s>' % (object.user_id.name, object.user_id.email) or '')|safe}</field> |
|||
<field name="subject">${object.pos_reference}</field> |
|||
<field name="model_id" ref="point_of_sale.model_pos_order" /> |
|||
<field name="auto_delete" eval="True" /> |
|||
<field name="lang">${object.partner_id.lang}</field> |
|||
<field name="body_html"><![CDATA[ |
|||
Your Ticket ${object.pos_reference} |
|||
]]></field> |
|||
</record> |
|||
|
|||
</data> |
|||
</odoo> |
@ -0,0 +1,131 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_mail_receipt |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 12.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2019-12-17 14:32+0000\n" |
|||
"PO-Revision-Date: 2019-12-17 15:38+0100\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"Plural-Forms: nplurals=2; plural=(n > 1);\n" |
|||
"X-Generator: Poedit 2.2.4\n" |
|||
"Last-Translator: \n" |
|||
"Language: fr\n" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:mail.template,subject:pos_mail_receipt.email_send_ticket |
|||
msgid "${object.pos_reference}" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:mail.template,body_html:pos_mail_receipt.email_send_ticket |
|||
msgid "" |
|||
"<p style=\"margin:0px 0 1rem 0;font-size:13px;font-family:"Lucida " |
|||
"Grande", Helvetica, Verdana, Arial, sans-serif;\">\n" |
|||
"Your Ticket ${object.pos_reference}\n" |
|||
" </p>" |
|||
msgstr "" |
|||
"<p style=\"margin:0px 0 1rem 0;font-size:13px;font-family:"Lucida " |
|||
"Grande", Helvetica, Verdana, Arial, sans-serif;\">\n" |
|||
"Votre Ticket ${object.pos_reference}\n" |
|||
" </p>" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/xml/pos.xml:25 |
|||
#, python-format |
|||
msgid "Cancel" |
|||
msgstr "Annuler" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: code:addons/pos_mail_receipt/models/pos_order.py:25 |
|||
#, python-format |
|||
msgid "Cannot send the ticket, no email address found for the client" |
|||
msgstr "" |
|||
"Impossible d'envoyer le ticket, pas d'adresse mail trouvée pour le " |
|||
"client" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/js/screens.js:82 |
|||
#, python-format |
|||
msgid "Check your internet connection and try again." |
|||
msgstr "Vérifiez votre connexion internet et essayez à nouveau." |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/xml/pos.xml:22 |
|||
#, python-format |
|||
msgid "Confirm" |
|||
msgstr "Confirmer" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/js/screens.js:28 |
|||
#, python-format |
|||
msgid "E-mail address to use" |
|||
msgstr "Adresse mail à utiliser" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: code:addons/pos_mail_receipt/models/pos_order.py:23 |
|||
#, python-format |
|||
msgid "E-mail already sent" |
|||
msgstr "E-mail déjà envoyé" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:ir.model.fields,field_description:pos_mail_receipt.field_pos_order__email_receipt_sent |
|||
msgid "Email Receipt Sent" |
|||
msgstr "Reçu envoyé" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: code:addons/pos_mail_receipt/models/pos_order.py:21 |
|||
#, python-format |
|||
msgid "Error: no order found" |
|||
msgstr "Erreur: pas de commande trouvée" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/xml/pos.xml:19 |
|||
#, python-format |
|||
msgid "Mail" |
|||
msgstr "Mail" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/xml/pos.xml:8 |
|||
#, python-format |
|||
msgid "Mail Receipt" |
|||
msgstr "Reçu" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:ir.model,name:pos_mail_receipt.model_pos_order |
|||
msgid "Point of Sale Orders" |
|||
msgstr "Commandes du point de vente" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: code:addons/pos_mail_receipt/models/pos_order.py:44 |
|||
#, python-format |
|||
msgid "Receipt_{}.pdf" |
|||
msgstr "Recu_{}.pdf" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/js/screens.js:81 |
|||
#, python-format |
|||
msgid "The e-mail could not be sent" |
|||
msgstr "L'e-mail n'a pas pu être envoyé" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:mail.template,report_name:pos_mail_receipt.email_send_ticket |
|||
msgid "Ticket ${object.pos_reference}" |
|||
msgstr "Ticket ${object.pos_reference}" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:ir.model,name:pos_mail_receipt.model_report_pos_mail_receipt_pos_receipt_report |
|||
msgid "report.pos_mail_receipt.pos_receipt_report" |
|||
msgstr "" |
@ -0,0 +1,122 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * pos_mail_receipt |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 12.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2019-12-17 14:32+0000\n" |
|||
"PO-Revision-Date: 2019-12-17 14:32+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:mail.template,subject:pos_mail_receipt.email_send_ticket |
|||
msgid "${object.pos_reference}" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:mail.template,body_html:pos_mail_receipt.email_send_ticket |
|||
msgid "<p style=\"margin:0px 0 1rem 0;font-size:13px;font-family:"Lucida Grande", Helvetica, Verdana, Arial, sans-serif;\">\n" |
|||
"Your Ticket ${object.pos_reference}\n" |
|||
" </p>" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/xml/pos.xml:25 |
|||
#, python-format |
|||
msgid "Cancel" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: code:addons/pos_mail_receipt/models/pos_order.py:25 |
|||
#, python-format |
|||
msgid "Cannot send the ticket, no email address found for the client" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/js/screens.js:82 |
|||
#, python-format |
|||
msgid "Check your internet connection and try again." |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/xml/pos.xml:22 |
|||
#, python-format |
|||
msgid "Confirm" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/js/screens.js:28 |
|||
#, python-format |
|||
msgid "E-mail address to use" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: code:addons/pos_mail_receipt/models/pos_order.py:23 |
|||
#, python-format |
|||
msgid "E-mail already sent" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:ir.model.fields,field_description:pos_mail_receipt.field_pos_order__email_receipt_sent |
|||
msgid "Email Receipt Sent" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: code:addons/pos_mail_receipt/models/pos_order.py:21 |
|||
#, python-format |
|||
msgid "Error: no order found" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/xml/pos.xml:19 |
|||
#, python-format |
|||
msgid "Mail" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/xml/pos.xml:8 |
|||
#, python-format |
|||
msgid "Mail Receipt" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:ir.model,name:pos_mail_receipt.model_pos_order |
|||
msgid "Point of Sale Orders" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: code:addons/pos_mail_receipt/models/pos_order.py:44 |
|||
#, python-format |
|||
msgid "Receipt_{}.pdf" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#. openerp-web |
|||
#: code:addons/pos_mail_receipt/static/src/js/screens.js:81 |
|||
#, python-format |
|||
msgid "The e-mail could not be sent" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:mail.template,report_name:pos_mail_receipt.email_send_ticket |
|||
msgid "Ticket ${object.pos_reference}" |
|||
msgstr "" |
|||
|
|||
#. module: pos_mail_receipt |
|||
#: model:ir.model,name:pos_mail_receipt.model_report_pos_mail_receipt_pos_receipt_report |
|||
msgid "report.pos_mail_receipt.pos_receipt_report" |
|||
msgstr "" |
|||
|
@ -0,0 +1 @@ |
|||
from . import pos_order |
@ -0,0 +1,86 @@ |
|||
# Copyright 2019 Coop IT Easy SCRLfs |
|||
# @author Pierrick Brun <pierrick.brun@akretion.com> |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
|||
|
|||
|
|||
import logging |
|||
import base64 |
|||
from odoo import fields, models, api, _ |
|||
|
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
|
|||
class PosOrder(models.Model): |
|||
_inherit = "pos.order" |
|||
|
|||
email_receipt_sent = fields.Boolean() |
|||
|
|||
@api.model |
|||
def send_mail_receipt( |
|||
self, pos_reference, email, body_from_ui, force=True |
|||
): |
|||
order = self.search([("pos_reference", "=", pos_reference)]) |
|||
if len(order) < 1: |
|||
_logger.error(_("Error: no order found")) |
|||
return |
|||
if order.email_receipt_sent: |
|||
_logger.info(_("E-mail already sent")) |
|||
return |
|||
if not email and not order.partner_id and not order.partner_id.email: |
|||
_logger.error( |
|||
_( |
|||
"Cannot send the ticket, " |
|||
"no email address found for the client" |
|||
) |
|||
) |
|||
email_values = {} |
|||
if email: |
|||
email_values["email_to"] = email |
|||
else: |
|||
email_values["email_to"] = order.partner_id.email |
|||
|
|||
receipt = ( |
|||
"<main><div class='article'><div class='pos'>" |
|||
"<div class='pos-receipt-container'>" |
|||
"{}</div></div></div></main>".format(body_from_ui) |
|||
) |
|||
|
|||
bodies, html_ids, header, footer, specific_paperformat_args = self.env[ |
|||
"ir.actions.report" |
|||
]._prepare_html(receipt) |
|||
base64_pdf = self.env["ir.actions.report"]._run_wkhtmltopdf( |
|||
bodies, |
|||
landscape=False, |
|||
specific_paperformat_args=specific_paperformat_args, |
|||
) |
|||
attachment = self.env["ir.attachment"].create( |
|||
{ |
|||
"name": pos_reference, |
|||
"datas_fname": _("Receipt_{}.pdf".format(pos_reference)), |
|||
"type": "binary", |
|||
"mimetype": "application/x-pdf", |
|||
"db_datas": base64.encodestring(base64_pdf), |
|||
"res_model": "pos.order", |
|||
"res_id": order.id, |
|||
} |
|||
) |
|||
email_values["attachment_ids"] = [attachment.id] |
|||
mail_template = self.env.ref("pos_mail_receipt.email_send_ticket") |
|||
mail_template.send_mail( |
|||
order.id, force_send=force, email_values=email_values, |
|||
) |
|||
order.email_receipt_sent = True |
|||
|
|||
@api.model |
|||
def create_from_ui(self, orders): |
|||
res = super(PosOrder, self).create_from_ui(orders) |
|||
for order in orders: |
|||
if "email" in order["data"]: |
|||
self.send_mail_receipt( |
|||
order["data"]["name"], |
|||
order["data"]["email"], |
|||
order["data"]["body_from_ui"], |
|||
force=False, |
|||
) |
|||
return res |
@ -0,0 +1,3 @@ |
|||
* `Coop IT Easy SCRLfs <https://coopiteasy.be>`_: |
|||
|
|||
* Pierrick Brun |
@ -0,0 +1,5 @@ |
|||
This modules allows you to send the PoS receipt by e-mail. |
|||
|
|||
Instead of printing the ticket you can choose to send it be e-mail. |
|||
If you know the customer's e-mail, it will use it. |
|||
If not you will be prompted for it. |
@ -0,0 +1,3 @@ |
|||
#. Open a new PoS session. |
|||
#. Make an order and validate it. |
|||
#. You should see the company logo in the receipt preview. |
After Width: 128 | Height: 128 | Size: 9.3 KiB |
@ -0,0 +1,434 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
|||
<head> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
|||
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" /> |
|||
<title>Pos Mail Receipt</title> |
|||
<style type="text/css"> |
|||
|
|||
/* |
|||
:Author: David Goodger (goodger@python.org) |
|||
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $ |
|||
:Copyright: This stylesheet has been placed in the public domain. |
|||
|
|||
Default cascading style sheet for the HTML output of Docutils. |
|||
|
|||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to |
|||
customize this style sheet. |
|||
*/ |
|||
|
|||
/* used to remove borders from tables and images */ |
|||
.borderless, table.borderless td, table.borderless th { |
|||
border: 0 } |
|||
|
|||
table.borderless td, table.borderless th { |
|||
/* Override padding for "table.docutils td" with "! important". |
|||
The right padding separates the table cells. */ |
|||
padding: 0 0.5em 0 0 ! important } |
|||
|
|||
.first { |
|||
/* Override more specific margin styles with "! important". */ |
|||
margin-top: 0 ! important } |
|||
|
|||
.last, .with-subtitle { |
|||
margin-bottom: 0 ! important } |
|||
|
|||
.hidden { |
|||
display: none } |
|||
|
|||
.subscript { |
|||
vertical-align: sub; |
|||
font-size: smaller } |
|||
|
|||
.superscript { |
|||
vertical-align: super; |
|||
font-size: smaller } |
|||
|
|||
a.toc-backref { |
|||
text-decoration: none ; |
|||
color: black } |
|||
|
|||
blockquote.epigraph { |
|||
margin: 2em 5em ; } |
|||
|
|||
dl.docutils dd { |
|||
margin-bottom: 0.5em } |
|||
|
|||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { |
|||
overflow: hidden; |
|||
} |
|||
|
|||
/* Uncomment (and remove this text!) to get bold-faced definition list terms |
|||
dl.docutils dt { |
|||
font-weight: bold } |
|||
*/ |
|||
|
|||
div.abstract { |
|||
margin: 2em 5em } |
|||
|
|||
div.abstract p.topic-title { |
|||
font-weight: bold ; |
|||
text-align: center } |
|||
|
|||
div.admonition, div.attention, div.caution, div.danger, div.error, |
|||
div.hint, div.important, div.note, div.tip, div.warning { |
|||
margin: 2em ; |
|||
border: medium outset ; |
|||
padding: 1em } |
|||
|
|||
div.admonition p.admonition-title, div.hint p.admonition-title, |
|||
div.important p.admonition-title, div.note p.admonition-title, |
|||
div.tip p.admonition-title { |
|||
font-weight: bold ; |
|||
font-family: sans-serif } |
|||
|
|||
div.attention p.admonition-title, div.caution p.admonition-title, |
|||
div.danger p.admonition-title, div.error p.admonition-title, |
|||
div.warning p.admonition-title, .code .error { |
|||
color: red ; |
|||
font-weight: bold ; |
|||
font-family: sans-serif } |
|||
|
|||
/* Uncomment (and remove this text!) to get reduced vertical space in |
|||
compound paragraphs. |
|||
div.compound .compound-first, div.compound .compound-middle { |
|||
margin-bottom: 0.5em } |
|||
|
|||
div.compound .compound-last, div.compound .compound-middle { |
|||
margin-top: 0.5em } |
|||
*/ |
|||
|
|||
div.dedication { |
|||
margin: 2em 5em ; |
|||
text-align: center ; |
|||
font-style: italic } |
|||
|
|||
div.dedication p.topic-title { |
|||
font-weight: bold ; |
|||
font-style: normal } |
|||
|
|||
div.figure { |
|||
margin-left: 2em ; |
|||
margin-right: 2em } |
|||
|
|||
div.footer, div.header { |
|||
clear: both; |
|||
font-size: smaller } |
|||
|
|||
div.line-block { |
|||
display: block ; |
|||
margin-top: 1em ; |
|||
margin-bottom: 1em } |
|||
|
|||
div.line-block div.line-block { |
|||
margin-top: 0 ; |
|||
margin-bottom: 0 ; |
|||
margin-left: 1.5em } |
|||
|
|||
div.sidebar { |
|||
margin: 0 0 0.5em 1em ; |
|||
border: medium outset ; |
|||
padding: 1em ; |
|||
background-color: #ffffee ; |
|||
width: 40% ; |
|||
float: right ; |
|||
clear: right } |
|||
|
|||
div.sidebar p.rubric { |
|||
font-family: sans-serif ; |
|||
font-size: medium } |
|||
|
|||
div.system-messages { |
|||
margin: 5em } |
|||
|
|||
div.system-messages h1 { |
|||
color: red } |
|||
|
|||
div.system-message { |
|||
border: medium outset ; |
|||
padding: 1em } |
|||
|
|||
div.system-message p.system-message-title { |
|||
color: red ; |
|||
font-weight: bold } |
|||
|
|||
div.topic { |
|||
margin: 2em } |
|||
|
|||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, |
|||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { |
|||
margin-top: 0.4em } |
|||
|
|||
h1.title { |
|||
text-align: center } |
|||
|
|||
h2.subtitle { |
|||
text-align: center } |
|||
|
|||
hr.docutils { |
|||
width: 75% } |
|||
|
|||
img.align-left, .figure.align-left, object.align-left, table.align-left { |
|||
clear: left ; |
|||
float: left ; |
|||
margin-right: 1em } |
|||
|
|||
img.align-right, .figure.align-right, object.align-right, table.align-right { |
|||
clear: right ; |
|||
float: right ; |
|||
margin-left: 1em } |
|||
|
|||
img.align-center, .figure.align-center, object.align-center { |
|||
display: block; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
} |
|||
|
|||
table.align-center { |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
} |
|||
|
|||
.align-left { |
|||
text-align: left } |
|||
|
|||
.align-center { |
|||
clear: both ; |
|||
text-align: center } |
|||
|
|||
.align-right { |
|||
text-align: right } |
|||
|
|||
/* reset inner alignment in figures */ |
|||
div.align-right { |
|||
text-align: inherit } |
|||
|
|||
/* div.align-center * { */ |
|||
/* text-align: left } */ |
|||
|
|||
.align-top { |
|||
vertical-align: top } |
|||
|
|||
.align-middle { |
|||
vertical-align: middle } |
|||
|
|||
.align-bottom { |
|||
vertical-align: bottom } |
|||
|
|||
ol.simple, ul.simple { |
|||
margin-bottom: 1em } |
|||
|
|||
ol.arabic { |
|||
list-style: decimal } |
|||
|
|||
ol.loweralpha { |
|||
list-style: lower-alpha } |
|||
|
|||
ol.upperalpha { |
|||
list-style: upper-alpha } |
|||
|
|||
ol.lowerroman { |
|||
list-style: lower-roman } |
|||
|
|||
ol.upperroman { |
|||
list-style: upper-roman } |
|||
|
|||
p.attribution { |
|||
text-align: right ; |
|||
margin-left: 50% } |
|||
|
|||
p.caption { |
|||
font-style: italic } |
|||
|
|||
p.credits { |
|||
font-style: italic ; |
|||
font-size: smaller } |
|||
|
|||
p.label { |
|||
white-space: nowrap } |
|||
|
|||
p.rubric { |
|||
font-weight: bold ; |
|||
font-size: larger ; |
|||
color: maroon ; |
|||
text-align: center } |
|||
|
|||
p.sidebar-title { |
|||
font-family: sans-serif ; |
|||
font-weight: bold ; |
|||
font-size: larger } |
|||
|
|||
p.sidebar-subtitle { |
|||
font-family: sans-serif ; |
|||
font-weight: bold } |
|||
|
|||
p.topic-title { |
|||
font-weight: bold } |
|||
|
|||
pre.address { |
|||
margin-bottom: 0 ; |
|||
margin-top: 0 ; |
|||
font: inherit } |
|||
|
|||
pre.literal-block, pre.doctest-block, pre.math, pre.code { |
|||
margin-left: 2em ; |
|||
margin-right: 2em } |
|||
|
|||
pre.code .ln { color: grey; } /* line numbers */ |
|||
pre.code, code { background-color: #eeeeee } |
|||
pre.code .comment, code .comment { color: #5C6576 } |
|||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } |
|||
pre.code .literal.string, code .literal.string { color: #0C5404 } |
|||
pre.code .name.builtin, code .name.builtin { color: #352B84 } |
|||
pre.code .deleted, code .deleted { background-color: #DEB0A1} |
|||
pre.code .inserted, code .inserted { background-color: #A3D289} |
|||
|
|||
span.classifier { |
|||
font-family: sans-serif ; |
|||
font-style: oblique } |
|||
|
|||
span.classifier-delimiter { |
|||
font-family: sans-serif ; |
|||
font-weight: bold } |
|||
|
|||
span.interpreted { |
|||
font-family: sans-serif } |
|||
|
|||
span.option { |
|||
white-space: nowrap } |
|||
|
|||
span.pre { |
|||
white-space: pre } |
|||
|
|||
span.problematic { |
|||
color: red } |
|||
|
|||
span.section-subtitle { |
|||
/* font-size relative to parent (h1..h6 element) */ |
|||
font-size: 80% } |
|||
|
|||
table.citation { |
|||
border-left: solid 1px gray; |
|||
margin-left: 1px } |
|||
|
|||
table.docinfo { |
|||
margin: 2em 4em } |
|||
|
|||
table.docutils { |
|||
margin-top: 0.5em ; |
|||
margin-bottom: 0.5em } |
|||
|
|||
table.footnote { |
|||
border-left: solid 1px black; |
|||
margin-left: 1px } |
|||
|
|||
table.docutils td, table.docutils th, |
|||
table.docinfo td, table.docinfo th { |
|||
padding-left: 0.5em ; |
|||
padding-right: 0.5em ; |
|||
vertical-align: top } |
|||
|
|||
table.docutils th.field-name, table.docinfo th.docinfo-name { |
|||
font-weight: bold ; |
|||
text-align: left ; |
|||
white-space: nowrap ; |
|||
padding-left: 0 } |
|||
|
|||
/* "booktabs" style (no vertical lines) */ |
|||
table.docutils.booktabs { |
|||
border: 0px; |
|||
border-top: 2px solid; |
|||
border-bottom: 2px solid; |
|||
border-collapse: collapse; |
|||
} |
|||
table.docutils.booktabs * { |
|||
border: 0px; |
|||
} |
|||
table.docutils.booktabs th { |
|||
border-bottom: thin solid; |
|||
text-align: left; |
|||
} |
|||
|
|||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, |
|||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { |
|||
font-size: 100% } |
|||
|
|||
ul.auto-toc { |
|||
list-style-type: none } |
|||
|
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div class="document" id="pos-mail-receipt"> |
|||
<h1 class="title">Pos Mail Receipt</h1> |
|||
|
|||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|||
!! This file is generated by oca-gen-addon-readme !! |
|||
!! changes will be overwritten. !! |
|||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> |
|||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/pos/tree/12.0/pos_mail_receipt"><img alt="OCA/pos" src="https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/pos-12-0/pos-12-0-pos_mail_receipt"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/184/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p> |
|||
<p>This modules allows you to send the PoS receipt by e-mail.</p> |
|||
<p>Instead of printing the ticket you can choose to send it be e-mail. |
|||
If you know the customer’s e-mail, it will use it. |
|||
If not you will be prompted for it.</p> |
|||
<p><strong>Table of contents</strong></p> |
|||
<div class="contents local topic" id="contents"> |
|||
<ul class="simple"> |
|||
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li> |
|||
<li><a class="reference internal" href="#bug-tracker" id="id2">Bug Tracker</a></li> |
|||
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul> |
|||
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li> |
|||
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li> |
|||
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li> |
|||
</ul> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="usage"> |
|||
<h1><a class="toc-backref" href="#id1">Usage</a></h1> |
|||
<ol class="arabic simple"> |
|||
<li>Open a new PoS session.</li> |
|||
<li>Make an order and validate it.</li> |
|||
<li>You should see the company logo in the receipt preview.</li> |
|||
</ol> |
|||
</div> |
|||
<div class="section" id="bug-tracker"> |
|||
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1> |
|||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/pos/issues">GitHub Issues</a>. |
|||
In case of trouble, please check there if your issue has already been reported. |
|||
If you spotted it first, help us smashing it by providing a detailed and welcomed |
|||
<a class="reference external" href="https://github.com/OCA/pos/issues/new?body=module:%20pos_mail_receipt%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p> |
|||
<p>Do not contact contributors directly about support or help with technical issues.</p> |
|||
</div> |
|||
<div class="section" id="credits"> |
|||
<h1><a class="toc-backref" href="#id3">Credits</a></h1> |
|||
<div class="section" id="authors"> |
|||
<h2><a class="toc-backref" href="#id4">Authors</a></h2> |
|||
<ul class="simple"> |
|||
<li>Coop IT Easy SCRLfs</li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="contributors"> |
|||
<h2><a class="toc-backref" href="#id5">Contributors</a></h2> |
|||
<ul class="simple"> |
|||
<li><a class="reference external" href="https://coopiteasy.be">Coop IT Easy SCRLfs</a>:<ul> |
|||
<li>Pierrick Brun</li> |
|||
</ul> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="section" id="maintainers"> |
|||
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2> |
|||
<p>This module is maintained by the OCA.</p> |
|||
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a> |
|||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use.</p> |
|||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/pos/tree/12.0/pos_mail_receipt">OCA/pos</a> project on GitHub.</p> |
|||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,3 @@ |
|||
.pos .modal-dialog .popup-textinput input{ |
|||
margin-left: 10% |
|||
} |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue