2
0
mirror of https://github.com/frappe/books.git synced 2025-01-09 01:44:15 +00:00

feat: WeightEnabledBarcode in POS

This commit is contained in:
AbleKSaju 2024-12-26 15:17:49 +05:30
parent 364173101b
commit 896b16e08e
3 changed files with 232 additions and 4 deletions

View File

@ -0,0 +1,198 @@
<template>
<div
class="
px-2
w-36
flex
items-center
border
rounded
bg-gray-50
dark:border-gray-800 dark:bg-gray-890 dark:focus-within:bg-gray-900
focus-within:bg-gray-100
"
>
<input
ref="scanner"
type="text"
class="text-base placeholder-gray-600 w-full bg-transparent outline-none"
:placeholder="t`Enter weight barcode`"
@change="handleChange"
/>
<feather-icon
name="maximize"
class="w-3 h-3 text-gray-600 dark:text-gray-400 cursor-text"
@click="() => ($refs.scanner as HTMLInputElement).focus()"
/>
</div>
</template>
<script lang="ts">
import { showToast } from 'src/utils/interactive';
import { defineComponent } from 'vue';
export default defineComponent({
emits: ['item-selected'],
data() {
return {
timerId: null,
barcode: '',
cooldown: '',
} as {
timerId: null | ReturnType<typeof setTimeout>;
barcode: string;
cooldown: string;
};
},
mounted() {
document.addEventListener('keydown', this.scanListener);
},
unmounted() {
document.removeEventListener('keydown', this.scanListener);
},
activated() {
document.addEventListener('keydown', this.scanListener);
},
deactivated() {
document.removeEventListener('keydown', this.scanListener);
},
methods: {
handleChange(e: Event) {
const elem = e.target as HTMLInputElement;
this.selectItem(elem.value);
elem.value = '';
},
async selectItem(code: string) {
const barcode = code.trim();
if (this.cooldown === barcode) {
return;
}
this.cooldown = barcode;
setTimeout(() => (this.cooldown = ''), 100);
const isWeightEnabled =
this.fyo.singles.POSSettings?.weightEnabledBarcode;
const randomNumberLength = this.fyo.singles.POSSettings
?.randomNumberLength as number;
const ItemBarcodeLength = this.fyo.singles.POSSettings
?.ItemBarcodeLength as number;
const quantityBarcodeLength = this.fyo.singles.POSSettings
?.quantityBarcodeLength as number;
if (
code.length !==
randomNumberLength + ItemBarcodeLength + quantityBarcodeLength
) {
return this.error(this.t`Barcode ${barcode} has an invalid length.`);
}
const filters: Record<string, string> = isWeightEnabled
? {
weightBarcode: barcode.slice(
randomNumberLength,
randomNumberLength + ItemBarcodeLength
),
}
: { barcode };
const fields = isWeightEnabled ? ['name', 'unit'] : ['name'];
const items =
(await this.fyo.db.getAll('Item', { filters, fields })) || [];
const { name, unit } = items[0] || {};
if (!name) {
return this.error(this.t`Item with barcode ${barcode} not found.`);
}
const quantity = isWeightEnabled
? this.parseBarcode(
barcode,
unit as string,
randomNumberLength + ItemBarcodeLength
)
: 1;
this.success(this.t`${name as string} quantity ${quantity} added.`);
this.$emit('item-selected', name, quantity);
},
parseBarcode(barcode: string, unitType: string, sliceDigit: number) {
const weightRaw = parseInt(barcode.slice(sliceDigit), sliceDigit);
let itemQuantity = 0;
switch (unitType) {
case 'Kg':
itemQuantity = Math.floor(weightRaw / 1000);
break;
case 'Gram':
itemQuantity = weightRaw;
break;
case 'Unit':
case 'Meter':
case 'Hour':
case 'Day':
itemQuantity = weightRaw;
break;
default:
throw new Error('Unknown unit type!');
}
return itemQuantity;
},
async scanListener({ key, code }: KeyboardEvent) {
/**
* Based under the assumption that
* - Barcode scanners trigger keydown events
* - Keydown events are triggered quicker than human can
* i.e. at max 20ms between events
* - Keydown events are triggered for barcode digits
* - The sequence of digits might be punctuated by a return
*/
const keyCode = Number(key);
const isEnter = code === 'Enter';
if (Number.isNaN(keyCode) && !isEnter) {
return;
}
if (isEnter) {
return await this.setItemFromBarcode();
}
this.clearInterval();
this.barcode += key;
this.timerId = setTimeout(async () => {
await this.setItemFromBarcode();
this.barcode = '';
}, 20);
},
async setItemFromBarcode() {
if (this.barcode.length < 12) {
return;
}
await this.selectItem(this.barcode);
this.barcode = '';
this.clearInterval();
},
clearInterval() {
if (this.timerId === null) {
return;
}
clearInterval(this.timerId);
this.timerId = null;
},
error(message: string) {
showToast({ type: 'error', message });
},
success(message: string) {
showToast({ type: 'success', message });
},
},
});
</script>

View File

@ -98,7 +98,10 @@
/>
<Barcode
v-if="fyo.singles.InventorySettings?.enableBarcodes"
v-if="
fyo.singles.InventorySettings?.enableBarcodes &&
!fyo.singles.POSSettings?.weightEnabledBarcode
"
class="w-1/3"
@item-selected="
async (name: string) => {
@ -106,6 +109,16 @@
}
"
/>
<WeightEnabledBarcode
v-if="fyo.singles.POSSettings?.weightEnabledBarcode"
class="w-1/3"
@item-selected="
async (name: string,qty:number) => {
emitEvent('addItem', await getItem(name) as Item,qty as number);
}
"
/>
</div>
<ItemsTable
@ -313,6 +326,7 @@ import ItemsTable from 'src/components/POS/Classic/ItemsTable.vue';
import MultiLabelLink from 'src/components/Controls/MultiLabelLink.vue';
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
import SelectedItemTable from 'src/components/POS/Classic/SelectedItemTable.vue';
import WeightEnabledBarcode from 'src/components/Controls/weightEnabledBarcode.vue';
import FloatingLabelFloatInput from 'src/components/POS/FloatingLabelFloatInput.vue';
import FloatingLabelCurrencyInput from 'src/components/POS/FloatingLabelCurrencyInput.vue';
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
@ -336,6 +350,7 @@ export default defineComponent({
SavedInvoiceModal,
ClosePOSShiftModal,
LoyaltyProgramModal,
WeightEnabledBarcode,
FloatingLabelFloatInput,
FloatingLabelCurrencyInput,
},
@ -414,7 +429,7 @@ export default defineComponent({
methods: {
emitEvent(
eventName: PosEmits,
...args: (string | boolean | Item | Money)[]
...args: (string | boolean | Item | number | Money)[]
) {
this.$emit(eventName, ...args);
},

View File

@ -255,7 +255,10 @@
/>
<Barcode
v-if="fyo.singles.InventorySettings?.enableBarcodes"
v-if="
fyo.singles.InventorySettings?.enableBarcodes &&
!fyo.singles.POSSettings?.weightEnabledBarcode
"
class="w-1/3"
@item-selected="
async (name: string) => {
@ -263,6 +266,16 @@
}
"
/>
<WeightEnabledBarcode
v-if="fyo.singles.POSSettings?.weightEnabledBarcode"
class="w-1/3"
@item-selected="
async (name: string,qty:number) => {
emitEvent('addItem', await getItem(name) as Item,qty as number);
}
"
/>
</div>
<ModernPOSItemsTable
@ -321,6 +334,7 @@ import { POSItem, PosEmits, ItemQtyMap } from 'src/components/POS/types';
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
import ModernPOSItemsGrid from 'src/components/POS/Modern/ModernPOSItemsGrid.vue';
import ModernPOSItemsTable from 'src/components/POS/Modern/ModernPOSItemsTable.vue';
import WeightEnabledBarcode from 'src/components/Controls/weightEnabledBarcode.vue';
import FloatingLabelFloatInput from 'src/components/POS/FloatingLabelFloatInput.vue';
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
import FloatingLabelCurrencyInput from 'src/components/POS/FloatingLabelCurrencyInput.vue';
@ -346,6 +360,7 @@ export default defineComponent({
ClosePOSShiftModal,
LoyaltyProgramModal,
ModernPOSItemsTable,
WeightEnabledBarcode,
FloatingLabelFloatInput,
FloatingLabelCurrencyInput,
ModernPOSSelectedItemTable,
@ -430,7 +445,7 @@ export default defineComponent({
methods: {
emitEvent(
eventName: PosEmits,
...args: (string | boolean | Item | Money)[]
...args: (string | boolean | Item | number | Money)[]
) {
this.$emit(eventName, ...args);
},