mirror of
https://github.com/frappe/books.git
synced 2026-08-24 02:24:17 -05:00
fix: added erp item visibility
This commit is contained in:
+33
-2
@@ -920,6 +920,35 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
|
||||
return this;
|
||||
}
|
||||
async _hasERPSyncableItems(): Promise<boolean> {
|
||||
const isSalesInvoice = this.schemaName === ModelNameEnum.SalesInvoice;
|
||||
if (!isSalesInvoice) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const items = (this.get('items') as Doc[]) ?? [];
|
||||
|
||||
for (const item of items) {
|
||||
const itemName = item.get('item') as string;
|
||||
if (!itemName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const itemDoc = await this.fyo.doc.getDoc('Item', itemName);
|
||||
const isInventoryItem = !!itemDoc.get('trackItem');
|
||||
const isFromERP = !!itemDoc.get('datafromErp');
|
||||
|
||||
if (!isInventoryItem && isFromERP) {
|
||||
return true;
|
||||
}
|
||||
} catch (err) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async sync(): Promise<Doc> {
|
||||
this._syncing = true;
|
||||
@@ -936,10 +965,12 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
|
||||
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);
|
||||
|
||||
@@ -40,6 +40,9 @@ export class ERPNextSyncSettings extends Doc {
|
||||
batchSyncType: () => {
|
||||
return !this.fyo.singles.InventorySettings?.enableBatches;
|
||||
},
|
||||
// syncDataFromServer: () => {
|
||||
// return !this.deviceID;
|
||||
// },
|
||||
};
|
||||
|
||||
async change(ch: ChangeArg) {
|
||||
|
||||
@@ -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,28 @@ export abstract class InvoiceItem extends Doc {
|
||||
};
|
||||
|
||||
static filters: FiltersMap = {
|
||||
item: (doc: Doc) => {
|
||||
item: async (doc: Doc): Promise<QueryFilter> => {
|
||||
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 === 'ERP Sync Items') {
|
||||
filters.datafromErp = true;
|
||||
}
|
||||
}
|
||||
|
||||
return filters;
|
||||
},
|
||||
batch: async (doc: Doc) => {
|
||||
const batches = await doc.fyo.db.getAll(ModelNameEnum.Batch, {
|
||||
|
||||
@@ -30,6 +30,7 @@ export class Item extends Doc {
|
||||
hsnCode?: number;
|
||||
hasSerialNumber?: boolean;
|
||||
serialNumberSeries?: string;
|
||||
datafromErp?: boolean;
|
||||
uomConversions: UOMConversionItem[] = [];
|
||||
|
||||
formulas: FormulaMap = {
|
||||
@@ -237,5 +238,6 @@ export class Item extends Doc {
|
||||
trackItem: () => this.inserted,
|
||||
hasBatch: () => this.inserted,
|
||||
hasSerialNumber: () => this.inserted,
|
||||
datafromErp: () => true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -116,6 +116,12 @@ export async function getItemQtyMap(doc: SalesInvoice): Promise<ItemQtyMap> {
|
||||
|
||||
export async function getItemVisibility(fyo: Fyo): Promise<ItemVisibility> {
|
||||
const posProfileName = fyo.singles.POSSettings?.posProfile as string;
|
||||
const enableERPNextSync = fyo.singles.AccountingSettings?.enableERPNextSync;
|
||||
|
||||
if (enableERPNextSync) {
|
||||
// When ERP sync is enabled, use itemVisibilityERP from POSSettings
|
||||
return fyo.singles.POSSettings?.itemVisibilityERP as ItemVisibility;
|
||||
}
|
||||
|
||||
if (posProfileName) {
|
||||
const posProfile = await fyo.doc.getDoc(
|
||||
|
||||
@@ -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,8 @@ export class POSSettings extends Doc {
|
||||
!this.fyo.singles.InventorySettings?.enableBarcodes ||
|
||||
!this.weightEnabledBarcode,
|
||||
itemVisibility: () =>
|
||||
!this.fyo.singles.AccountingSettings?.enablePointOfSaleWithOutInventory,
|
||||
!!this.fyo.singles.AccountingSettings?.enableERPNextSync,
|
||||
itemVisibilityERP: () =>
|
||||
!this.fyo.singles.AccountingSettings?.enableERPNextSync,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -172,6 +172,13 @@
|
||||
"fieldtype": "Table",
|
||||
"target": "UOMConversionItem",
|
||||
"section": "Inventory"
|
||||
},
|
||||
{
|
||||
"fieldname": "datafromErp",
|
||||
"fieldtype": "Check",
|
||||
"hidden": false,
|
||||
"default": false,
|
||||
"section": "Default"
|
||||
}
|
||||
],
|
||||
"quickEditFields": [
|
||||
|
||||
@@ -121,6 +121,28 @@
|
||||
"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",
|
||||
"required": true,
|
||||
"section": "Default"
|
||||
},
|
||||
{
|
||||
"fieldname": "canChangeRate",
|
||||
"label": "Can Change Rate",
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
v-if="!isErpSync"
|
||||
class="
|
||||
absolute
|
||||
top-1
|
||||
@@ -111,6 +112,10 @@ export default defineComponent({
|
||||
itemQtyMap: {
|
||||
type: Object,
|
||||
},
|
||||
isErpSync: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getExtractedWords(item: string) {
|
||||
|
||||
@@ -81,13 +81,20 @@ export default defineComponent({
|
||||
props: {
|
||||
items: Array,
|
||||
itemQtyMap: Object,
|
||||
isErpSync: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
ratio() {
|
||||
if (this.isErpSync) {
|
||||
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.isErpSync) {
|
||||
fields.splice(2, 0, {
|
||||
fieldname: 'availableQty',
|
||||
label: 'Qty',
|
||||
placeholder: 'Available Qty',
|
||||
fieldtype: 'Float',
|
||||
readOnly: true,
|
||||
});
|
||||
}
|
||||
|
||||
return fields;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
v-if="!isErpSync"
|
||||
class="
|
||||
w-6
|
||||
h-6
|
||||
@@ -114,6 +115,10 @@ export default defineComponent({
|
||||
itemQtyMap: {
|
||||
type: Object,
|
||||
},
|
||||
isErpSync: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getExtractedWords(item: string) {
|
||||
|
||||
@@ -137,13 +137,20 @@ export default defineComponent({
|
||||
props: {
|
||||
items: Array,
|
||||
itemQtyMap: Object,
|
||||
isErpSync: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
ratio() {
|
||||
return [1, 1, 0.6, 0.7];
|
||||
if (this.isErpSync) {
|
||||
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.isErpSync) {
|
||||
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));
|
||||
|
||||
@@ -10,7 +10,7 @@ export type ItemGroupMap = Record<string, string>;
|
||||
|
||||
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',
|
||||
|
||||
@@ -134,6 +134,7 @@
|
||||
v-if="tableView"
|
||||
:items="items"
|
||||
:item-qty-map="itemQuantityMap as ItemQtyMap"
|
||||
:is-erp-sync="isErpSync"
|
||||
@add-item="(item) => emitEvent('addItem', item)"
|
||||
/>
|
||||
|
||||
@@ -141,6 +142,7 @@
|
||||
v-else
|
||||
:items="items"
|
||||
:item-qty-map="itemQuantityMap as ItemQtyMap"
|
||||
:is-erp-sync="isErpSync"
|
||||
@add-item="(item) => emitEvent('addItem', item)"
|
||||
/>
|
||||
|
||||
@@ -483,6 +485,10 @@ export default defineComponent({
|
||||
type: Array as PropType<POSItem[] | undefined>,
|
||||
default: () => [],
|
||||
},
|
||||
isErpSync: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
profile: {
|
||||
type: Object as PropType<POSProfile>,
|
||||
required: false,
|
||||
|
||||
@@ -345,6 +345,7 @@
|
||||
v-if="tableView"
|
||||
:items="items"
|
||||
:item-qty-map="itemQuantityMap as ItemQtyMap"
|
||||
:is-erp-sync="isErpSync"
|
||||
@add-item="(item:string) => emitEvent('addItem', item)"
|
||||
/>
|
||||
|
||||
@@ -352,6 +353,7 @@
|
||||
v-else
|
||||
:items="items"
|
||||
:item-qty-map="itemQuantityMap as ItemQtyMap"
|
||||
:is-erp-sync="isErpSync"
|
||||
@add-item="(item:string) => emitEvent('addItem', item)"
|
||||
/>
|
||||
|
||||
@@ -489,6 +491,10 @@ export default defineComponent({
|
||||
type: Array as PropType<POSItem[] | undefined>,
|
||||
default: () => [],
|
||||
},
|
||||
isErpSync: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
profile: {
|
||||
type: Object as PropType<POSProfile>,
|
||||
required: false,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
:selected-item-group="selectedItemGroup"
|
||||
:is-pos-shift-open="isPosShiftOpen"
|
||||
:items="(items as [] as POSItem[])"
|
||||
:is-erp-sync="isErpSync"
|
||||
: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[])"
|
||||
:is-erp-sync="isErpSync"
|
||||
:sinv-doc="(sinvDoc as SalesInvoice)"
|
||||
:disable-pay-button="disablePayButton"
|
||||
:open-payment-modal="openPaymentModal"
|
||||
@@ -262,6 +264,7 @@ export default defineComponent({
|
||||
quickQtyKeyUpHandler: null as ((e: KeyboardEvent) => void) | null,
|
||||
selectedItemForBatch: '' as string,
|
||||
pendingBatchItem: null as { item: POSItem; quantity: number } | null,
|
||||
isErpSyncValue: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -271,6 +274,9 @@ export default defineComponent({
|
||||
return !!fyo.singles.AccountingSettings?.enableDiscounting;
|
||||
},
|
||||
isPosShiftOpen: () => !!fyo.singles.POSSettings?.isShiftOpen,
|
||||
isErpSync() {
|
||||
return this.isErpSyncValue;
|
||||
},
|
||||
disablePayButton(): boolean {
|
||||
if (!this.sinvDoc.items?.length || !this.sinvDoc.party) {
|
||||
return true;
|
||||
@@ -295,6 +301,7 @@ export default defineComponent({
|
||||
async mounted() {
|
||||
await this.setItems();
|
||||
await this.loadPOSProfile();
|
||||
this.isErpSyncValue = !!fyo.singles.AccountingSettings?.enableERPNextSync;
|
||||
},
|
||||
async activated() {
|
||||
toggleSidebar(false);
|
||||
@@ -694,8 +701,11 @@ export default defineComponent({
|
||||
|
||||
if (itemVisibility === 'Inventory Items') {
|
||||
filters.trackItem = true;
|
||||
} else if (itemVisibility === 'ERP Sync Items') {
|
||||
filters.datafromErp = true;
|
||||
} else {
|
||||
filters.trackItem = false;
|
||||
filters.datafromErp = false;
|
||||
}
|
||||
|
||||
if (this.selectedItemGroup) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user