diff --git a/fyo/model/doc.ts b/fyo/model/doc.ts
index 14965ceb..5345e568 100644
--- a/fyo/model/doc.ts
+++ b/fyo/model/doc.ts
@@ -46,6 +46,7 @@ import {
import { validateOptions, validateRequired } from './validationFunction';
import { getShouldDocSyncToERPNext } from 'src/utils/erpnextSync';
import { ModelNameEnum } from 'models/types';
+import { DocItem } from 'models/inventory/types';
export class Doc extends Observable {
/* eslint-disable @typescript-eslint/no-floating-promises */
@@ -920,6 +921,25 @@ export class Doc extends Observable {
return this;
}
+ async _hasERPSyncableItems(): Promise {
+ const isSalesInvoice = this.schemaName === ModelNameEnum.SalesInvoice;
+ if (!isSalesInvoice) {
+ return true;
+ }
+ for (const item of this.items as DocItem[]) {
+ if (!item.item) {
+ continue;
+ }
+ const isFromERP = await this.fyo.getValue(
+ ModelNameEnum.Item,
+ item.item,
+ 'datafromErp'
+ );
+ if (isFromERP) continue;
+ else return false;
+ }
+ return true;
+ }
async sync(): Promise {
this._syncing = true;
@@ -936,10 +956,12 @@ export class Doc extends Observable {
if (this._addDocToSyncQueue && !!this.shouldDocSyncToERPNext) {
const isSalesInvoice = this.schemaName === ModelNameEnum.SalesInvoice;
+ const hasERPSyncableItems = await this._hasERPSyncableItems();
if (
- !(isSalesInvoice && this.isSyncedWithErp) ||
- (isSalesInvoice && !!this.isReturn)
+ hasERPSyncableItems &&
+ (!(isSalesInvoice && this.isSyncedWithErp) ||
+ (isSalesInvoice && !!this.isReturn))
) {
if (isSalesInvoice && !this.isReturn) {
await this.setAndSync('isSyncedWithErp', true);
diff --git a/models/baseModels/InvoiceItem/InvoiceItem.ts b/models/baseModels/InvoiceItem/InvoiceItem.ts
index 590702d2..b089ee41 100644
--- a/models/baseModels/InvoiceItem/InvoiceItem.ts
+++ b/models/baseModels/InvoiceItem/InvoiceItem.ts
@@ -19,8 +19,13 @@ import { Item } from '../Item/Item';
import { StockTransfer } from 'models/inventory/StockTransfer';
import { isPesa } from 'fyo/utils';
import { PricingRule } from '../PricingRule/PricingRule';
-import { getItemRateFromPriceList, getPricingRule } from 'models/helpers';
+import {
+ getItemRateFromPriceList,
+ getPricingRule,
+ getItemVisibility,
+} from 'models/helpers';
import { SalesInvoice } from '../SalesInvoice/SalesInvoice';
+import { QueryFilter } from 'utils/db/types';
export abstract class InvoiceItem extends Doc {
item?: string;
@@ -646,13 +651,33 @@ export abstract class InvoiceItem extends Doc {
};
static filters: FiltersMap = {
- item: (doc: Doc) => {
+ item: async (doc: Doc): Promise => {
let itemNotFor = 'Sales';
if (doc.isSales) {
itemNotFor = 'Purchases';
}
- return { for: ['not in', [itemNotFor]] };
+ const filters: QueryFilter = {
+ for: ['not in', [itemNotFor]],
+ };
+
+ const enableERPNextSync =
+ doc.fyo.singles.AccountingSettings?.enableERPNextSync;
+
+ if (enableERPNextSync) {
+ const itemVisibility = await getItemVisibility(doc.fyo);
+
+ if (itemVisibility === 'Inventory Items') {
+ filters.trackItem = true;
+ } else if (itemVisibility === 'ERP Sync Items') {
+ filters.datafromErp = true;
+ } else if (itemVisibility === 'Non-Inventory Items') {
+ filters.trackItem = false;
+ filters.datafromErp = false;
+ }
+ }
+
+ return filters;
},
batch: async (doc: Doc) => {
const batches = await doc.fyo.db.getAll(ModelNameEnum.Batch, {
diff --git a/models/baseModels/Item/Item.ts b/models/baseModels/Item/Item.ts
index 9971a1ba..1d0542b4 100644
--- a/models/baseModels/Item/Item.ts
+++ b/models/baseModels/Item/Item.ts
@@ -30,6 +30,7 @@ export class Item extends Doc {
hsnCode?: number;
hasSerialNumber?: boolean;
serialNumberSeries?: string;
+ datafromErp?: boolean;
uomConversions: UOMConversionItem[] = [];
formulas: FormulaMap = {
diff --git a/models/helpers.ts b/models/helpers.ts
index 07e13435..ac458d13 100644
--- a/models/helpers.ts
+++ b/models/helpers.ts
@@ -116,6 +116,11 @@ export async function getItemQtyMap(doc: SalesInvoice): Promise {
export async function getItemVisibility(fyo: Fyo): Promise {
const posProfileName = fyo.singles.POSSettings?.posProfile as string;
+ const enableERPNextSync = fyo.singles.AccountingSettings?.enableERPNextSync;
+
+ if (enableERPNextSync) {
+ return fyo.singles.POSSettings?.itemVisibilityERP as ItemVisibility;
+ }
if (posProfileName) {
const posProfile = await fyo.doc.getDoc(
diff --git a/models/inventory/Point of Sale/POSSettings.ts b/models/inventory/Point of Sale/POSSettings.ts
index 5fbcc9e6..3060b290 100644
--- a/models/inventory/Point of Sale/POSSettings.ts
+++ b/models/inventory/Point of Sale/POSSettings.ts
@@ -16,6 +16,7 @@ export class POSSettings extends Doc {
itemWeightDigits?: number;
defaultAccount?: string;
itemVisibility?: string;
+ itemVisibilityERP?: 'ERP Sync Items';
posUI?: 'Classic' | 'Modern';
canChangeRate?: boolean;
canEditDiscount?: boolean;
@@ -46,6 +47,9 @@ export class POSSettings extends Doc {
!this.fyo.singles.InventorySettings?.enableBarcodes ||
!this.weightEnabledBarcode,
itemVisibility: () =>
- !this.fyo.singles.AccountingSettings?.enablePointOfSaleWithOutInventory,
+ !this.fyo.singles.AccountingSettings?.enablePointOfSaleWithOutInventory ||
+ !!this.fyo.singles.AccountingSettings?.enableERPNextSync,
+ itemVisibilityERP: () =>
+ !this.fyo.singles.AccountingSettings?.enableERPNextSync,
};
}
diff --git a/schemas/app/Item.json b/schemas/app/Item.json
index 02a483fa..09f1bcca 100644
--- a/schemas/app/Item.json
+++ b/schemas/app/Item.json
@@ -172,6 +172,14 @@
"fieldtype": "Table",
"target": "UOMConversionItem",
"section": "Inventory"
+ },
+ {
+ "fieldname": "datafromErp",
+ "fieldtype": "Check",
+ "hidden": true,
+ "default": false,
+ "section": "Default",
+ "readOnly": true
}
],
"quickEditFields": [
diff --git a/schemas/app/inventory/Point of Sale/POSSettings.json b/schemas/app/inventory/Point of Sale/POSSettings.json
index 0d2887bb..f798a7af 100644
--- a/schemas/app/inventory/Point of Sale/POSSettings.json
+++ b/schemas/app/inventory/Point of Sale/POSSettings.json
@@ -96,13 +96,6 @@
"default": 0,
"section": "Barcode"
},
- {
- "fieldname": "itemWeightDigits",
- "label": "item Weight Digits",
- "fieldtype": "Int",
- "default": 0,
- "section": "Barcode"
- },
{
"fieldname": "itemVisibility",
"label": "Item Visibility",
@@ -118,7 +111,27 @@
}
],
"default": "Inventory Items",
- "required": true,
+ "section": "Default"
+ },
+ {
+ "fieldname": "itemVisibilityERP",
+ "label": "Item Visibility",
+ "fieldtype": "Select",
+ "options": [
+ {
+ "value": "ERP Sync Items",
+ "label": "ERP Sync Items"
+ },
+ {
+ "value": "Inventory Items",
+ "label": "Inventory Items"
+ },
+ {
+ "value": "Non-Inventory Items",
+ "label": "Non-Inventory Items"
+ }
+ ],
+ "default": "ERP Sync Items",
"section": "Default"
},
{
diff --git a/src/components/POS/Classic/ItemsGrid.vue b/src/components/POS/Classic/ItemsGrid.vue
index 08c289a2..8847e941 100644
--- a/src/components/POS/Classic/ItemsGrid.vue
+++ b/src/components/POS/Classic/ItemsGrid.vue
@@ -63,6 +63,7 @@
@@ -81,13 +81,20 @@ export default defineComponent({
props: {
items: Array,
itemQtyMap: Object,
+ itemVisibility: {
+ type: String,
+ default: 'Inventory Items',
+ },
},
computed: {
ratio() {
+ if (this.itemVisibility === 'ERP Sync Items') {
+ return [1, 1.5, 0.8];
+ }
return [1, 1, 1, 0.7];
},
tableFields() {
- return [
+ const fields = [
{
fieldname: 'name',
fieldtype: 'Data',
@@ -102,13 +109,6 @@ export default defineComponent({
fieldtype: 'Currency',
readOnly: true,
},
- {
- fieldname: 'availableQty',
- label: 'Qty',
- placeholder: 'Available Qty',
- fieldtype: 'Float',
- readOnly: true,
- },
{
fieldname: 'unit',
label: 'Unit',
@@ -118,6 +118,18 @@ export default defineComponent({
readOnly: true,
},
] as Field[];
+
+ if (this.itemVisibility !== 'ERP Sync Items') {
+ fields.splice(2, 0, {
+ fieldname: 'availableQty',
+ label: 'Qty',
+ placeholder: 'Available Qty',
+ fieldtype: 'Float',
+ readOnly: true,
+ });
+ }
+
+ return fields;
},
},
methods: {
diff --git a/src/components/POS/Modern/ModernPOSItemsGrid.vue b/src/components/POS/Modern/ModernPOSItemsGrid.vue
index 0c06aed3..9df82aad 100644
--- a/src/components/POS/Modern/ModernPOSItemsGrid.vue
+++ b/src/components/POS/Modern/ModernPOSItemsGrid.vue
@@ -66,6 +66,7 @@
@@ -113,7 +113,7 @@
:key="df.fieldname"
size="large"
:df="df"
- :value="row[df.fieldname]"
+ :value="(row as POSItem)[df.fieldname as keyof POSItem]"
:readOnly="true"
/>
@@ -137,13 +137,20 @@ export default defineComponent({
props: {
items: Array,
itemQtyMap: Object,
+ itemVisibility: {
+ type: String,
+ default: 'Inventory Items',
+ },
},
computed: {
ratio() {
- return [1, 1, 0.6, 0.7];
+ if (this.itemVisibility === 'ERP Sync Items') {
+ return [1, 1.5, 0.8];
+ }
+ return [1, 1, 1, 0.7];
},
tableFields() {
- return [
+ const fields = [
{
fieldname: 'name',
fieldtype: 'Data',
@@ -158,13 +165,6 @@ export default defineComponent({
fieldtype: 'Currency',
readOnly: true,
},
- {
- fieldname: 'availableQty',
- label: t`Qty`,
- placeholder: 'Available Qty',
- fieldtype: 'Float',
- readOnly: true,
- },
{
fieldname: 'unit',
label: t`Unit`,
@@ -174,6 +174,18 @@ export default defineComponent({
readOnly: true,
},
] as Field[];
+
+ if (this.itemVisibility !== 'ERP Sync Items') {
+ fields.splice(2, 0, {
+ fieldname: 'availableQty',
+ label: t`Qty`,
+ placeholder: 'Available Qty',
+ fieldtype: 'Float',
+ readOnly: true,
+ });
+ }
+
+ return fields;
},
firstColumnItems() {
return this.items?.slice(0, Math.ceil(this.items.length / 2));
diff --git a/src/components/POS/types.ts b/src/components/POS/types.ts
index 21023acb..cc49e511 100644
--- a/src/components/POS/types.ts
+++ b/src/components/POS/types.ts
@@ -10,7 +10,7 @@ export type ItemGroupMap = Record;
export type DiscountType = 'percent' | 'amount';
-export type ItemVisibility = 'Inventory Items' | 'Non-Inventory Items'
+export type ItemVisibility = 'Inventory Items' | 'Non-Inventory Items' | 'ERP Sync Items'
export const modalNames = [
'Keyboard',
diff --git a/src/pages/POS/ClassicPOS.vue b/src/pages/POS/ClassicPOS.vue
index de59942f..23832681 100644
--- a/src/pages/POS/ClassicPOS.vue
+++ b/src/pages/POS/ClassicPOS.vue
@@ -134,6 +134,7 @@
v-if="tableView"
:items="items"
:item-qty-map="itemQuantityMap as ItemQtyMap"
+ :item-visibility="itemVisibility"
@add-item="(item) => emitEvent('addItem', item)"
/>
@@ -141,6 +142,7 @@
v-else
:items="items"
:item-qty-map="itemQuantityMap as ItemQtyMap"
+ :item-visibility="itemVisibility"
@add-item="(item) => emitEvent('addItem', item)"
/>
@@ -483,6 +485,10 @@ export default defineComponent({
type: Array as PropType,
default: () => [],
},
+ itemVisibility: {
+ type: String,
+ default: 'Inventory Items',
+ },
profile: {
type: Object as PropType,
required: false,
diff --git a/src/pages/POS/ModernPOS.vue b/src/pages/POS/ModernPOS.vue
index 7d278ee8..71723617 100644
--- a/src/pages/POS/ModernPOS.vue
+++ b/src/pages/POS/ModernPOS.vue
@@ -345,6 +345,7 @@
v-if="tableView"
:items="items"
:item-qty-map="itemQuantityMap as ItemQtyMap"
+ :item-visibility="itemVisibility"
@add-item="(item:string) => emitEvent('addItem', item)"
/>
@@ -352,6 +353,7 @@
v-else
:items="items"
:item-qty-map="itemQuantityMap as ItemQtyMap"
+ :item-visibility="itemVisibility"
@add-item="(item:string) => emitEvent('addItem', item)"
/>
@@ -489,6 +491,10 @@ export default defineComponent({
type: Array as PropType,
default: () => [],
},
+ itemVisibility: {
+ type: String,
+ default: 'Inventory Items',
+ },
profile: {
type: Object as PropType,
required: false,
diff --git a/src/pages/POS/POS.vue b/src/pages/POS/POS.vue
index cdd6b032..154a85dd 100644
--- a/src/pages/POS/POS.vue
+++ b/src/pages/POS/POS.vue
@@ -27,6 +27,7 @@
:selected-item-group="selectedItemGroup"
:is-pos-shift-open="isPosShiftOpen"
:items="(items as [] as POSItem[])"
+ :item-visibility="itemVisibility"
:sinv-doc="(sinvDoc as SalesInvoice)"
:disable-pay-button="disablePayButton"
:open-payment-modal="openPaymentModal"
@@ -83,6 +84,7 @@
:selected-item-group="selectedItemGroup"
:is-pos-shift-open="isPosShiftOpen"
:items="(items as [] as POSItem[])"
+ :item-visibility="itemVisibility"
:sinv-doc="(sinvDoc as SalesInvoice)"
:disable-pay-button="disablePayButton"
:open-payment-modal="openPaymentModal"
@@ -167,6 +169,7 @@ import {
getItemRateFromPriceList,
getItemVisibility,
} from 'models/helpers';
+import { ItemVisibility } from 'src/components/POS/types';
import {
POSItem,
ItemQtyMap,
@@ -262,6 +265,7 @@ export default defineComponent({
quickQtyKeyUpHandler: null as ((e: KeyboardEvent) => void) | null,
selectedItemForBatch: '' as string,
pendingBatchItem: null as { item: POSItem; quantity: number } | null,
+ itemVisibilityValue: 'Inventory Items' as ItemVisibility,
};
},
computed: {
@@ -271,6 +275,9 @@ export default defineComponent({
return !!fyo.singles.AccountingSettings?.enableDiscounting;
},
isPosShiftOpen: () => !!fyo.singles.POSSettings?.isShiftOpen,
+ itemVisibility() {
+ return this.itemVisibilityValue;
+ },
disablePayButton(): boolean {
if (!this.sinvDoc.items?.length || !this.sinvDoc.party) {
return true;
@@ -295,6 +302,7 @@ export default defineComponent({
async mounted() {
await this.setItems();
await this.loadPOSProfile();
+ this.itemVisibilityValue = await getItemVisibility(this.fyo);
},
async activated() {
toggleSidebar(false);
@@ -694,8 +702,11 @@ export default defineComponent({
if (itemVisibility === 'Inventory Items') {
filters.trackItem = true;
- } else {
+ } else if (itemVisibility === 'ERP Sync Items') {
+ filters.datafromErp = true;
+ } else if (itemVisibility === 'Non-Inventory Items') {
filters.trackItem = false;
+ filters.datafromErp = false;
}
if (this.selectedItemGroup) {
diff --git a/src/utils/erpnextSync.ts b/src/utils/erpnextSync.ts
index 6197767f..728c2836 100644
--- a/src/utils/erpnextSync.ts
+++ b/src/utils/erpnextSync.ts
@@ -150,6 +150,10 @@ export async function syncDocumentsFromERPNext(fyo: Fyo) {
continue;
}
+ if (getDocTypeName(doc) === ModelNameEnum.Item) {
+ doc.datafromErp = true;
+ }
+
try {
if ((doc.fbooksDocName as string) || (doc.name as string)) {
const isDocExists = await fyo.db.exists(
@@ -239,6 +243,10 @@ async function createNewDocument(
token: string,
deviceID: string
) {
+ if (getDocTypeName(doc) === ModelNameEnum.Item) {
+ doc.datafromErp = true;
+ }
+
const newDoc = fyo.doc.getNewDoc(getDocTypeName(doc), doc);
await performPreSync(fyo, doc);
await appendDocValues(newDoc as DocValueMap, doc);
@@ -447,8 +455,10 @@ async function updateExistingDocument(
token: string,
deviceID: string
) {
+ const docType = getDocTypeName(doc);
+
const existingDoc = await fyo.doc.getDoc(
- getDocTypeName(doc),
+ docType,
(doc.fbooksDocName as string) || (doc.name as string)
);
@@ -533,6 +543,10 @@ export async function performInitialFullSync(fyo: Fyo) {
if (docsByType[docType] && docsByType[docType].length > 0) {
for (const doc of docsByType[docType]) {
try {
+ if (docType === ModelNameEnum.Item) {
+ doc.datafromErp = true;
+ }
+
const isDocExists = await fyo.db.exists(
docType,
(doc.fbooksDocName as string) || (doc.name as string)