19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:12 +01:00
parent 79f83631d5
commit 73afc09215
6267 changed files with 1534193 additions and 1130106 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="70" height="70" viewBox="0 0 70 70"><defs><path id="a" d="M4 0h61c4 0 5 1 5 5v60c0 4-1 5-5 5H4c-3 0-4-1-4-5V5c0-4 1-5 4-5z"/><linearGradient id="c" x1="100%" x2="0%" y1="0%" y2="98.616%"><stop offset="0%" stop-color="#797C79"/><stop offset="100%" stop-color="#545554"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><g mask="url(#b)"><path fill="url(#c)" d="M0 0H70V70H0z"/><path fill="#FFF" fill-opacity=".383" d="M4 1h61c2.667 0 4.333.667 5 2V0H0v3c.667-1.333 2-2 4-2z"/><path fill="#393939" d="M32.25 69H4c-2 0-4-1-4-4V39.181L19 20h32v6.208l1.992 12.632L51 41.123V50L32.25 69z" opacity=".324"/><path fill="#000" fill-opacity=".383" d="M4 69h61c2.667 0 4.333-1 5-3v4H0v-4c.667 2 2 3 4 3z"/><path fill="#000" fill-opacity=".3" d="M51 22H19v3.75h32V22zm2 18.75V37l-2-9.375H19L17 37v3.75h2V52h20V40.75h8V52h4V40.75h2zm-18 7.5H23v-7.5h12v7.5z"/><path fill="#FFF" d="M51 20H19v3.75h32V20zm2 18.75V35l-2-9.375H19L17 35v3.75h2V50h20V38.75h8V50h4V38.75h2zm-18 7.5H23v-7.5h12v7.5z"/></g></g></svg>
<svg width="50" height="50" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d="M9.5 8 6 36h6.019a6.5 6.5 0 0 0 12.962 0H25a6 6 0 0 0 12 0h.019a6.5 6.5 0 0 0 12.962 0H50v-1l-.045-.273a6.231 6.231 0 0 0-.058-.386l-.023-.118-.02-.1-3.797-22.78A4 4 0 0 0 42.112 8H9.5Z" fill="#985184"/><path d="m14 8-2 28a6 6 0 0 1-12 0v-1l3.494-23.586A4 4 0 0 1 7.451 8H14Zm21 0 2 28a6 6 0 0 1-12 0V8h10Z" fill="#FBB945"/><path d="M12 36a6 6 0 1 1-12 0h12Zm25 0a6 6 0 0 1-12 0h12Z" fill="#F78613"/><path d="M12.02 36a6.5 6.5 0 0 0 12.961 0H12.02Zm25 0a6.5 6.5 0 0 0 12.961 0H37.02Z" fill="#712258"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 600 B

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Before After
Before After

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
declare module "services" {
import { CustomerDisplayDataService } from "@point_of_sale/customer_display/customer_display_data_service";
import { alertService } from "@point_of_sale/app/services/alert_service";
import { barcodeReaderService } from "@point_of_sale/app/services/barcode_reader_service";
import { contextualUtilsService } from "@point_of_sale/app/services/contextual_utils_service";
import { hardwareProxyService } from "@point_of_sale/app/services/hardware_proxy_service";
import { numberBufferService } from "@point_of_sale/app/services/number_buffer_service";
import { PosDataService } from "@point_of_sale/app/services/data_service";
import { posService } from "@point_of_sale/app/services/pos_store";
import { posPrinterService } from "@point_of_sale/app/services/pos_printer_service";
import { renderService } from "@point_of_sale/app/services/render_service";
import { reportService } from "@point_of_sale/app/services/report_service";
export interface Services {
alert: typeof alertService;
barcode_reader: typeof barcodeReaderService;
contextual_utils_service: typeof contextualUtilsService;
customer_display_data: typeof CustomerDisplayDataService;
hardware_proxy: typeof hardwareProxyService;
number_buffer: typeof numberBufferService;
pos: typeof posService;
pos_data: typeof PosDataService;
printer: typeof posPrinterService;
renderer: typeof renderService;
report: typeof reportService;
}
}

View file

@ -0,0 +1,69 @@
import { Component, useState, useRef, useEffect, onMounted } from "@odoo/owl";
export class AccordionItem extends Component {
static template = "pos_hr.AccordionItem";
static props = {
disabled: { type: Boolean, optional: true },
slots: Object,
};
static defaultProps = {
disabled: false,
};
setup() {
this.content = useRef("content_container");
this.state = useState({
open: false,
});
onMounted(() => {
this.contentHeight = this.calculateFullHeight();
});
useEffect(
() => {
this.contentHeight = this.calculateFullHeight();
},
() => [this.props.slots.content]
);
}
toggle() {
if (this.props.disabled) {
return;
}
this.state.open = !this.state.open;
}
calculateFullHeight() {
const children = Array.from(this.content.el.getElementsByClassName("accordion-content"));
const fullHeight = children.reduce(
(accumulator, child) => accumulator + Math.min(this.getHiddenHeight(child), 100),
0
);
return fullHeight;
}
getHiddenHeight(el) {
if (!el?.cloneNode) {
return 0;
}
const clone = el.cloneNode(true);
Object.assign(clone.style, {
overflow: "visible",
height: "auto",
maxHeight: "none",
opacity: "0",
visibility: "hidden",
display: "block",
});
el.after(clone);
const height = clone.offsetHeight;
clone.remove();
return height;
}
}

View file

@ -0,0 +1,43 @@
@mixin hide-scrollbar {
-ms-overflow-style: none; // IE 10+
scrollbar-width: none; // Firefox
&::-webkit-scrollbar {
display: none; // Safari and Chrome
}
}
.accordion.disabled {
pointer-events: none;
opacity: 0.5;
}
.accordion-header {
margin-left: 1px;
&:not(.no-caret) {
display: flex;
align-items: center;
gap: 5px;
&:before {
content: "\f0d7";
font-family: FontAwesome;
display: inline-block;
transform: rotate(-90deg);
transition: .25s ease-in-out;
}
&.open:before {
transform: rotate(0deg);
}
}
}
.accordion-content-container {
overflow: auto;
transition: max-height 0.25s ease-in-out;
@include hide-scrollbar;
}

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="pos_hr.AccordionItem">
<div class="accordion position-relative" t-att-class="{'disabled': props.disabled}">
<button class="accordion-header dropdown-item" tabindex="0"
t-att-class="{'open': state.open}"
t-att-aria-expanded="state.open ? 'true' : 'false'"
t-on-click="toggle"
>
<t t-slot="header"/>
</button>
<div t-ref="content_container" class="accordion-content-container" t-attf-style="max-height: {{state.open ? contentHeight : 0}}px;">
<div class="accordion-content" >
<t t-slot="content"/>
</div>
</div>
</div>
</t>
</templates>

View file

@ -0,0 +1,23 @@
import { Component } from "@odoo/owl";
export class QuantityButtons extends Component {
static template = "point_of_sale.QuantityButtons";
static props = {
quantity: Number,
setQuantity: Function,
isPlusButtonDisabled: { type: Boolean, optional: true },
btnClasses: { type: String, optional: true },
};
changeQuantity(increment) {
const isDisabled = increment == 1 && this.props.isPlusButtonDisabled;
if (!isDisabled) {
this.props.setQuantity(this.props.quantity + increment);
}
}
setQuantity(event) {
const quantity = parseFloat(event.target.value);
this.props.setQuantity(isNaN(quantity) ? 0 : quantity);
}
}

View file

@ -0,0 +1,27 @@
input[name="pos_quantity"] {
padding: 0;
@include media-breakpoint-down(md) {
max-width: 3rem;
}
@include media-breakpoint-up(md) {
max-width: 4rem;
}
// removing input field=number arrows as their size might
// change depending on browser default styling and shift input's position
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
&[type=number] {
-moz-appearance: textfield;
}
}
.px-2-5 {
padding-right: 0.75rem !important;
padding-left: 0.75rem !important;
}

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="point_of_sale.QuantityButtons">
<div name="quantity_buttons_wrapper" class="input-group justify-content-end justify-content-md-center p-1 pt-0">
<button
t-attf-class="px-2-5 btn btn-secondary btn-sm {{ props.btnClasses or 'd-md-inline-block' }}"
name="pos_quantity_button_minus"
aria-label="Remove one"
t-on-click.stop="() => this.changeQuantity(-1)">
<i class="fa fa-minus"/>
</button>
<input
class="form-control quantity text-center"
name="pos_quantity"
type="number"
t-att-value="props.quantity"
t-on-click.stop=""
t-on-change="setQuantity"/>
<button
t-attf-class="px-2-5 btn btn-secondary btn-sm {{ props.btnClasses or 'd-md-inline-block' }}"
name="pos_quantity_button_plus"
aria-label="Add one"
t-att-disabled="props.isPlusButtonDisabled"
t-on-click.stop="() => this.changeQuantity(1)">
<i class="fa fa-plus"/>
</button>
</div>
</t>
</templates>

View file

@ -0,0 +1,87 @@
import { Component } from "@odoo/owl";
import { usePos } from "@point_of_sale/app/hooks/pos_hook";
import { useService } from "@web/core/utils/hooks";
import { pick } from "@web/core/utils/objects";
export class CategorySelector extends Component {
static template = "point_of_sale.CategorySelector";
static props = {};
setup() {
this.ui = useService("ui");
this.pos = usePos();
}
getCategoriesList(list, allParents, depth) {
const categoriesList = [...list];
list.forEach((item) => {
if (item.id === allParents[depth]?.id && item.child_ids?.length) {
categoriesList.push(
...this.getCategoriesList(item.child_ids, allParents, depth + 1)
);
}
});
return categoriesList;
}
getCategoriesAndSub() {
const { limit_categories, iface_available_categ_ids } = this.pos.config;
let rootCategories = this.pos.models["pos.category"].getAll();
if (limit_categories && iface_available_categ_ids.length > 0) {
rootCategories = iface_available_categ_ids;
}
rootCategories = rootCategories
.filter((category) => !category.parent_id)
.sort((a, b) => a.sequence - b.sequence);
const selected = this.pos.selectedCategory ? [this.pos.selectedCategory] : [];
const allParents = selected.concat(this.pos.selectedCategory?.allParents || []).reverse();
return this.getCategoriesList(rootCategories, allParents, 0)
.flat(Infinity)
.filter((c) => c.hasProductsToShow)
.map(this.getChildCategoriesInfo, this);
}
getAncestorsAndCurrent() {
const selectedCategory = this.pos.selectedCategory;
return selectedCategory
? [undefined, ...selectedCategory.allParents, selectedCategory]
: [selectedCategory];
}
getChildCategoriesInfo(category) {
return {
...pick(category, "id", "name", "color"),
imgSrc:
this.pos.config.show_category_images && category.has_image
? `/web/image?model=pos.category&field=image_128&id=${category.id}`
: undefined,
isSelected: this.getAncestorsAndCurrent().includes(category),
isChildren: this.getChildCategories(this.pos.selectedCategory).includes(category),
};
}
getChildCategories(selectedCategory) {
return selectedCategory
? [...selectedCategory.child_ids]
: this.pos.models["pos.category"].filter((category) => !category.parent_id);
}
getAllSelected() {
return this.getAncestorsAndCurrent().filter(Boolean).length === 0;
}
hasParent() {
const selectedCategory = this.pos.selectedCategory;
return !!(selectedCategory && selectedCategory.parent_id);
}
isAncestorOrSelected(category) {
const selected = this.pos.selectedCategory;
if (!selected) {
return false;
}
return category.id === selected.id || selected.allParents.some((p) => p.id === category.id);
}
showCategoryImg(category) {
return this.pos.config.show_category_images && category.imgSrc && !this.ui.isSmall;
}
}

View file

@ -0,0 +1,25 @@
.category-list {
grid-template-columns: repeat(auto-fill, minmax(115px, 1fr));
overflow-y: auto;
flex-shrink: 0;
margin-bottom: map-get($spacers, 2);
max-height: 16.5rem;
@include media-breakpoint-down(md) {
max-height: 11rem;
}
}
.category-button {
--btn-border-color: #{$o-gray-200};
border-width: 2px;
height: 4rem;
.line-clamp-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
}

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates id="template" xml:space="preserve">
<t t-name="point_of_sale.CategorySelector">
<div t-attf-class="{{this.pos.config.show_category_images ? 'category-list' : 'product-list'}} d-grid gap-1 gap-lg-2 p-2">
<t t-foreach="this.getCategoriesAndSub()" t-as="category" t-key="category.id">
<button t-on-click="() => this.pos.setSelectedCategory(category.id)"
t-attf-class="o_colorlist_item_color_{{category.color or 'none'}}"
t-att-class="{
'border-0': category.isChildren and !this.isAncestorOrSelected(category),
'opacity-75 border-0': !category.isChildren and !category.isSelected,
'justify-content-center': ui.isSmall
}"
class="category-button p-1 btn btn-light d-flex justify-content-around align-items-center rounded-3 gap-1">
<div t-if="showCategoryImg(category)" class="overflow-hidden flex-shrink-0 ratio ratio-1x1" style="width:40%;">
<img t-if="category.imgSrc and !ui.isSmall" t-att-src="category.imgSrc"
class="category-img-thumb h-100 rounded-3 object-fit-cover"
alt="Category"
/>
</div>
<div class="line-clamp-3" t-att-class="{'w-100': !showCategoryImg(category)}" t-att-style="{'width: 60%;': showCategoryImg(category)}">
<span t-if="category.name" class="text-center" t-esc="category.name" />
</div>
</button>
</t>
</div>
</t>
</templates>

Some files were not shown because too many files have changed in this diff Show more