mirror of
https://github.com/frappe/books.git
synced 2026-08-24 10:04:45 -05:00
Merge pull request #1244 from frappe/fix-search
fix: unify item search for barcode, weight barcode, and name
This commit is contained in:
@@ -300,6 +300,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.triggerChange(e.target.value);
|
||||
this.toggleDropdown(true);
|
||||
this.updateSuggestions(e.target.value);
|
||||
},
|
||||
|
||||
@@ -22,6 +22,11 @@ export default {
|
||||
},
|
||||
},
|
||||
props: {
|
||||
thirdLink: String,
|
||||
showSecondaryLink: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
secondaryLink: String,
|
||||
},
|
||||
mounted() {
|
||||
@@ -80,9 +85,10 @@ export default {
|
||||
return (this.results = results
|
||||
.map((r) => {
|
||||
const option = {
|
||||
label: r[this.secondaryLink]
|
||||
? `${r[schema.titleField]} ` + ` ${r[this.secondaryLink]}`
|
||||
: r[schema.titleField],
|
||||
label:
|
||||
r[this.secondaryLink] && this.showSecondaryLink
|
||||
? `${r[schema.titleField]} ` + ` ${r[this.secondaryLink]}`
|
||||
: r[schema.titleField],
|
||||
value: r.name,
|
||||
value2: r[this.secondaryLink],
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ export type PosEmits =
|
||||
| 'setPaymentMethod'
|
||||
| 'setCouponsCount'
|
||||
| 'routeToSinvList'
|
||||
| 'handleItemSearch'
|
||||
| 'applyPricingRule'
|
||||
| 'setTransferRefNo'
|
||||
| 'setLoyaltyPoints'
|
||||
|
||||
@@ -85,47 +85,23 @@
|
||||
<div class="rounded-md p-4 col-span-5">
|
||||
<div class="flex gap-x-2">
|
||||
<!-- Item Search -->
|
||||
<Link
|
||||
:class="
|
||||
fyo.singles.InventorySettings?.enableBarcodes
|
||||
? 'flex-shrink-0 w-2/3'
|
||||
: 'w-full'
|
||||
"
|
||||
<MultiLabelLink
|
||||
class="w-full"
|
||||
secondary-link="barcode"
|
||||
third-link="itemCode"
|
||||
:df="{
|
||||
label: t`Search an Item`,
|
||||
label: t`Search Item (Name or
|
||||
Barcode)`,
|
||||
fieldtype: 'Link',
|
||||
fieldname: 'item',
|
||||
target: 'Item',
|
||||
}"
|
||||
:border="true"
|
||||
:value="itemSearchTerm"
|
||||
@keyup.enter="
|
||||
async () => emitEvent('addItem', await getItem(itemSearchTerm) as Item)
|
||||
"
|
||||
@change="(item: string) =>itemSearchTerm= item"
|
||||
/>
|
||||
|
||||
<Barcode
|
||||
v-if="
|
||||
fyo.singles.InventorySettings?.enableBarcodes &&
|
||||
!fyo.singles.POSSettings?.weightEnabledBarcode
|
||||
"
|
||||
class="w-1/3"
|
||||
@item-selected="
|
||||
async (name: string) => {
|
||||
emitEvent('addItem', await getItem(name) as Item);
|
||||
}
|
||||
"
|
||||
/>
|
||||
|
||||
<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);
|
||||
}
|
||||
@keyup.enter="(item) =>
|
||||
emitEvent('handleItemSearch', item.target.value as string, true)
|
||||
"
|
||||
@change="(item: string) => emitEvent('handleItemSearch', item)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -369,14 +345,12 @@ import Button from 'src/components/Button.vue';
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import PriceListModal from './PriceListModal.vue';
|
||||
import { Item } from 'models/baseModels/Item/Item';
|
||||
import Link from 'src/components/Controls/Link.vue';
|
||||
import CouponCodeModal from './CouponCodeModal.vue';
|
||||
import POSQuickActions from './POSQuickActions.vue';
|
||||
import { PosEmits } from 'src/components/POS/types';
|
||||
import SavedInvoiceModal from './SavedInvoiceModal.vue';
|
||||
import OpenPOSShiftModal from './OpenPOSShiftModal.vue';
|
||||
import ClosePOSShiftModal from './ClosePOSShiftModal.vue';
|
||||
import Barcode from 'src/components/Controls/Barcode.vue';
|
||||
import LoyaltyProgramModal from './LoyaltyProgramModal.vue';
|
||||
import { POSItem, ItemQtyMap } from 'src/components/POS/types';
|
||||
import ItemsGrid from 'src/components/POS/Classic/ItemsGrid.vue';
|
||||
@@ -385,7 +359,6 @@ import ReturnSalesInvoiceModal from './ReturnSalesInvoiceModal.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';
|
||||
@@ -393,9 +366,7 @@ import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/Applied
|
||||
export default defineComponent({
|
||||
name: 'ClassicPOS',
|
||||
components: {
|
||||
Link,
|
||||
Button,
|
||||
Barcode,
|
||||
ItemsGrid,
|
||||
AlertModal,
|
||||
ItemsTable,
|
||||
@@ -409,7 +380,6 @@ export default defineComponent({
|
||||
SavedInvoiceModal,
|
||||
ClosePOSShiftModal,
|
||||
LoyaltyProgramModal,
|
||||
WeightEnabledBarcode,
|
||||
FloatingLabelFloatInput,
|
||||
ReturnSalesInvoiceModal,
|
||||
FloatingLabelCurrencyInput,
|
||||
@@ -437,6 +407,10 @@ export default defineComponent({
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
itemSearchTerm: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
loyaltyProgram: {
|
||||
type: String,
|
||||
default: '',
|
||||
@@ -471,6 +445,7 @@ export default defineComponent({
|
||||
'setPaidAmount',
|
||||
'setCouponsCount',
|
||||
'routeToSinvList',
|
||||
'handleItemSearch',
|
||||
'setPaymentMethod',
|
||||
'setTransferRefNo',
|
||||
'setLoyaltyPoints',
|
||||
@@ -486,7 +461,6 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
additionalDiscounts: fyo.pesa(0),
|
||||
itemSearchTerm: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
:loyalty-points="loyaltyPoints"
|
||||
:open-alert-modal="openAlertModal"
|
||||
:default-customer="defaultCustomer"
|
||||
:item-search-term="itemSearchTerm"
|
||||
:is-pos-shift-open="isPosShiftOpen"
|
||||
:items="(items as [] as POSItem[])"
|
||||
:sinv-doc="(sinvDoc as SalesInvoice)"
|
||||
@@ -39,6 +40,7 @@
|
||||
@clear-values="clearValues"
|
||||
@set-customer="setCustomer"
|
||||
@toggle-modal="toggleModal"
|
||||
@handle-item-search="handleItemSearch"
|
||||
@set-paid-amount="setPaidAmount"
|
||||
@set-payment-method="setPaymentMethod"
|
||||
@set-coupons-count="setCouponsCount"
|
||||
@@ -287,6 +289,87 @@ export default defineComponent({
|
||||
this.loyaltyProgram = party[0]?.loyaltyProgram as string;
|
||||
this.loyaltyPoints = party[0]?.loyaltyPoints as number;
|
||||
},
|
||||
|
||||
async handleItemSearch(searchTerm: string, addItem?: boolean) {
|
||||
this.itemSearchTerm = searchTerm;
|
||||
if (!addItem) return;
|
||||
|
||||
let quantity = 1;
|
||||
const posSettings = fyo.singles.POSSettings;
|
||||
const isWeightEnabledBarcode = posSettings?.weightEnabledBarcode;
|
||||
|
||||
const checkDigits = posSettings?.checkDigits || '';
|
||||
const itemCodeDigits = posSettings?.itemCodeDigits || 0;
|
||||
const weightDigits = posSettings?.itemWeightDigits || 0;
|
||||
|
||||
const expectedWeightBarcodeLength =
|
||||
String(checkDigits).length +
|
||||
Number(itemCodeDigits) +
|
||||
Number(weightDigits);
|
||||
|
||||
let isWeightBarcode = false;
|
||||
let itemCode = searchTerm;
|
||||
let weightPart = '';
|
||||
|
||||
if (
|
||||
isWeightEnabledBarcode &&
|
||||
searchTerm.length === expectedWeightBarcodeLength
|
||||
) {
|
||||
const extractedItemCode = searchTerm.slice(
|
||||
checkDigits.toString().length,
|
||||
checkDigits.toString().length + itemCodeDigits
|
||||
);
|
||||
const weightData = searchTerm.slice(
|
||||
checkDigits.toString().length + itemCodeDigits
|
||||
);
|
||||
|
||||
if (!isNaN(Number(weightData))) {
|
||||
isWeightBarcode = true;
|
||||
itemCode = extractedItemCode;
|
||||
weightPart = weightData;
|
||||
}
|
||||
}
|
||||
|
||||
const allItems = await this.fyo.db.getAll(ModelNameEnum.Item, {
|
||||
fields: ['name', 'barcode', 'itemCode', 'unit'],
|
||||
});
|
||||
|
||||
let matchedItem = null;
|
||||
|
||||
if (isWeightBarcode) {
|
||||
matchedItem = allItems.find(
|
||||
(item) => item.itemCode === itemCode || item.barcode === itemCode
|
||||
);
|
||||
} else if (searchTerm.length === 12) {
|
||||
matchedItem = allItems.find((item) => item.barcode === searchTerm);
|
||||
}
|
||||
|
||||
if (!matchedItem) {
|
||||
matchedItem = allItems.find((item) => item.name === searchTerm);
|
||||
}
|
||||
|
||||
if (!matchedItem) return;
|
||||
|
||||
if (isWeightBarcode && weightPart) {
|
||||
const weightValue = parseInt(weightPart, 10);
|
||||
if ((matchedItem.unit as string)?.toLowerCase() === 'kg') {
|
||||
quantity = weightValue / 1000;
|
||||
} else {
|
||||
quantity = weightValue;
|
||||
}
|
||||
}
|
||||
|
||||
const itemDoc = this.getItem(matchedItem.name as string);
|
||||
if (itemDoc && addItem) {
|
||||
await this.addItem(itemDoc as POSItem, quantity);
|
||||
this.itemSearchTerm = '';
|
||||
}
|
||||
},
|
||||
|
||||
getItem(name: string) {
|
||||
return this.items.find((item) => item.name === name);
|
||||
},
|
||||
|
||||
isModalOpen() {
|
||||
for (const modal of modalNames) {
|
||||
if (modal && this[`open${modal}Modal`]) {
|
||||
|
||||
@@ -337,7 +337,6 @@ export default defineComponent({
|
||||
paymentDoc: {} as Payment,
|
||||
itemSerialNumbers: {} as ItemSerialNumbers,
|
||||
|
||||
itemSearchTerm: '',
|
||||
transferRefNo: undefined as string | undefined,
|
||||
transferClearanceDate: undefined as Date | undefined,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user