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

incr: improve stockmovement ux

- hide inventory defaults until enabled
This commit is contained in:
18alantom 2022-11-16 14:05:38 +05:30
parent 8b04b5b2ab
commit b5f8e49299
13 changed files with 131 additions and 59 deletions

View File

@ -233,6 +233,7 @@ export class Fyo {
instanceId: '',
deviceId: '',
openCount: -1,
appFlags: {} as Record<string, boolean>,
};
}

View File

@ -1,4 +1,5 @@
import { strictEqual } from 'assert';
import { assertThrows } from 'backend/database/tests/helpers';
import Observable from 'fyo/utils/observable';
import test from 'tape';
@ -30,55 +31,69 @@ const listenerBOnce = (value: number) => {
strictEqual(params.b, value, 'listenerBOnce');
};
test('set A One', function (t) {
test('set A One', (t) => {
t.equal(obs.hasListener(ObsEvent.A), false, 'pre');
obs.once(ObsEvent.A, listenerAOnce);
t.equal(obs.hasListener(ObsEvent.A), true, 'non specific');
t.equal(obs.hasListener(ObsEvent.A, listenerAOnce), true, 'specific once');
t.equal(obs.hasListener(ObsEvent.A, listenerAEvery), false, 'specific every');
t.end()
t.end();
});
test('set A Two', function (t) {
test('set A Two', (t) => {
obs.on(ObsEvent.A, listenerAEvery);
t.equal(obs.hasListener(ObsEvent.A), true, 'non specific');
t.equal(obs.hasListener(ObsEvent.A, listenerAOnce), true, 'specific once');
t.equal(obs.hasListener(ObsEvent.A, listenerAEvery), true, 'specific every');
t.end()
t.end();
});
test('set B', function (t) {
test('set B', (t) => {
t.equal(obs.hasListener(ObsEvent.B), false, 'pre');
obs.once(ObsEvent.B, listenerBOnce);
t.equal(obs.hasListener(ObsEvent.A, listenerBOnce), false, 'specific false');
t.equal(obs.hasListener(ObsEvent.B, listenerBOnce), true, 'specific true');
t.end()
t.end();
});
test('trigger A 0', async function (t) {
test('trigger A 0', async (t) => {
await obs.trigger(ObsEvent.A, params.aOne);
t.equal(obs.hasListener(ObsEvent.A), true, 'non specific');
t.equal(obs.hasListener(ObsEvent.A, listenerAOnce), false, 'specific');
});
test('trigger A 1', async function (t) {
test('trigger A 1', async (t) => {
t.equal(obs.hasListener(ObsEvent.A, listenerAEvery), true, 'specific pre');
await obs.trigger(ObsEvent.A, params.aTwo);
t.equal(obs.hasListener(ObsEvent.A, listenerAEvery), true, 'specific post');
});
test('trigger B', async function (t) {
test('trigger B', async (t) => {
t.equal(obs.hasListener(ObsEvent.B, listenerBOnce), true, 'specific pre');
await obs.trigger(ObsEvent.B, params.b);
t.equal(obs.hasListener(ObsEvent.B, listenerBOnce), false, 'specific post');
});
test('remove A', async function (t) {
test('remove A', async (t) => {
obs.off(ObsEvent.A, listenerAEvery);
t.equal(obs.hasListener(ObsEvent.A, listenerAEvery), false, 'specific pre');
t.equal(counter, 2, 'incorrect counter');
await obs.trigger(ObsEvent.A, 777);
});
test('observable trigger error propagation', async (t) => {
const obs = new Observable();
obs.on('testOne', () => {
throw new Error('stuff');
});
await assertThrows(async () => {
await obs.trigger('testOne');
t.ok(false, 'trigger should throw error');
});
t.ok(true, 'assert throws success');
});

View File

@ -1,5 +1,5 @@
import { Doc } from 'fyo/model/doc';
import { FiltersMap } from 'fyo/model/types';
import { FiltersMap, HiddenMap } from 'fyo/model/types';
import { ModelNameEnum } from 'models/types';
export class Defaults extends Doc {
@ -42,6 +42,18 @@ export class Defaults extends Doc {
static filters: FiltersMap = this.commonFilters;
static createFilters: FiltersMap = this.commonFilters;
hideInventoryDefaults(): boolean {
return !this.fyo.store.appFlags.getIsInventoryEnabled;
}
hidden: HiddenMap = {
stockMovementNumberSeries: this.hideInventoryDefaults.bind(this),
shipmentNumberSeries: this.hideInventoryDefaults.bind(this),
purchaseReceiptNumberSeries: this.hideInventoryDefaults.bind(this),
shipmentTerms: this.hideInventoryDefaults.bind(this),
purchaseReceiptTerms: this.hideInventoryDefaults.bind(this),
};
}
export const numberSeriesDefaultsMap: Record<

View File

@ -10,15 +10,13 @@ import {
HiddenMap,
ListViewSettings,
RequiredMap,
ValidationMap
ValidationMap,
} from 'fyo/model/types';
import { NotFoundError, ValidationError } from 'fyo/utils/errors';
import {
getDocStatus,
getDocStatusListColumn,
getLedgerLinkAction,
getNumberSeries,
getStatusMap,
statusColor
} from 'models/helpers';
import { LedgerPosting } from 'models/Transactional/LedgerPosting';
import { Transactional } from 'models/Transactional/Transactional';
@ -619,27 +617,7 @@ export class Payment extends Transactional {
static getListViewSettings(fyo: Fyo): ListViewSettings {
return {
columns: [
'name',
{
label: t`Status`,
fieldname: 'status',
fieldtype: 'Select',
size: 'small',
render(doc) {
const status = getDocStatus(doc);
const color = statusColor[status] ?? 'gray';
const label = getStatusMap()[status];
return {
template: `<Badge class="text-xs" color="${color}">${label}</Badge>`,
};
},
},
'party',
'date',
'amount',
],
columns: ['name', getDocStatusListColumn(), 'party', 'date', 'amount'],
};
}
}

View File

@ -267,3 +267,21 @@ export function getNumberSeries(schemaName: string, fyo: Fyo) {
const value = defaults?.[numberSeriesKey] as string | undefined;
return value ?? (field?.default as string | undefined);
}
export function getDocStatusListColumn(): ColumnConfig {
return {
label: t`Status`,
fieldname: 'status',
fieldtype: 'Select',
size: 'small',
render(doc) {
const status = getDocStatus(doc);
const color = statusColor[status] ?? 'gray';
const label = getStatusMap()[status];
return {
template: `<Badge class="text-xs" color="${color}">${label}</Badge>`,
};
},
};
}

View File

@ -25,6 +25,13 @@ export class StockManager {
this.fyo = fyo;
}
async validateTransfers(transferDetails: SMTransferDetails[]) {
const detailsList = transferDetails.map((d) => this.#getSMIDetails(d));
for (const details of detailsList) {
await this.#validate(details);
}
}
async createTransfers(transferDetails: SMTransferDetails[]) {
const detailsList = transferDetails.map((d) => this.#getSMIDetails(d));
for (const details of detailsList) {

View File

@ -5,6 +5,7 @@ import {
FormulaMap,
ListViewSettings
} from 'fyo/model/types';
import { getDocStatusListColumn } from 'models/helpers';
import { ModelNameEnum } from 'models/types';
import { Money } from 'pesa';
import { StockManager } from './StockManager';
@ -40,15 +41,25 @@ export class StockMovement extends Doc {
};
static getListViewSettings(): ListViewSettings {
return { columns: ['name', 'date', 'movementType'] };
return {
columns: ['name', getDocStatusListColumn(), 'date', 'movementType'],
};
}
async beforeSubmit(): Promise<void> {
await super.beforeSubmit();
const transferDetails = this._getTransferDetails();
await this._getStockManager().validateTransfers(transferDetails);
}
async afterSubmit(): Promise<void> {
await super.afterSubmit();
const transferDetails = this._getTransferDetails();
await this._getStockManager().createTransfers(transferDetails);
}
async afterCancel(): Promise<void> {
await super.afterCancel();
await this._getStockManager().cancelTransfers();
}

View File

@ -1,23 +1,15 @@
import { Doc } from 'fyo/model/doc';
import {
FilterFunction,
FiltersMap,
FormulaMap,
ReadOnlyMap,
RequiredMap
} from 'fyo/model/types';
import { ModelNameEnum } from 'models/types';
import { Money } from 'pesa';
import { QueryFilter } from 'utils/db/types';
import { locationFilter } from './helpers';
import { StockMovement } from './StockMovement';
const locationFilter: FilterFunction = (doc: Doc) => {
const item = doc.item;
if (!doc.item) {
return { item: null };
}
return { item: ['in', [null, item]] } as QueryFilter;
};
import { MovementType } from './types';
export class StockMovementItem extends Doc {
name?: string;
@ -50,6 +42,20 @@ export class StockMovementItem extends Doc {
formula: () => this.rate!.mul(this.quantity!),
dependsOn: ['item', 'rate', 'quantity'],
},
fromLocation: {
formula: () => {
if (this.parentdoc?.movementType === MovementType.MaterialReceipt) {
return null;
}
},
},
toLocation: {
formula: () => {
if (this.parentdoc?.movementType === MovementType.MaterialIssue) {
return null;
}
},
},
};
required: RequiredMap = {
@ -61,6 +67,13 @@ export class StockMovementItem extends Doc {
this.parentdoc?.movementType === 'MaterialTransfer',
};
readOnly: ReadOnlyMap = {
fromLocation: () =>
this.parentdoc?.movementType === MovementType.MaterialReceipt,
toLocation: () =>
this.parentdoc?.movementType === MovementType.MaterialIssue,
};
static createFilters: FiltersMap = {
item: () => ({ trackItem: true, itemType: 'Product' }),
fromLocation: (doc: Doc) => ({ item: (doc.item ?? '') as string }),

View File

@ -1 +1,12 @@
import { Doc } from "fyo/model/doc";
import { FilterFunction } from "fyo/model/types";
import { QueryFilter } from "utils/db/types";
export const locationFilter: FilterFunction = (doc: Doc) => {
const item = doc.item;
if (!doc.item) {
return { item: null };
}
return { item: ['in', [null, item]] } as QueryFilter;
};

View File

@ -149,7 +149,6 @@ const routes: RouteRecordRaw[] = [
},
},
];
console.log(routes);
export function getEntryRoute(schemaName: string, name: string) {
if (

View File

@ -3,6 +3,7 @@ import { ConfigFile, ConfigKeys } from 'fyo/core/types';
import { getRegionalModels, models } from 'models/index';
import { ModelNameEnum } from 'models/types';
import { getRandomString, getValueMapFromList } from 'utils/index';
import { getIsInventoryEnabled } from './misc';
export async function initializeInstance(
dbPath: string,
@ -27,6 +28,11 @@ export async function initializeInstance(
await setInstanceId(fyo);
await setOpenCount(fyo);
await setCurrencySymbols(fyo);
await setAppFlags(fyo);
}
async function setAppFlags(fyo: Fyo) {
fyo.store.appFlags.isInventoryEnabled = await getIsInventoryEnabled(fyo);
}
async function closeDbIfConnected(fyo: Fyo) {

View File

@ -129,3 +129,12 @@ export async function convertFileToDataURL(file: File, type: string) {
const array = new Uint8Array(buffer);
return await getDataURL(type, array);
}
export async function getIsInventoryEnabled(fyo: Fyo) {
const values = await fyo.db.getAllRaw('Item', {
fields: ['name'],
filters: { trackItem: true },
});
return !!values.length;
}

View File

@ -1,5 +1,6 @@
import { Fyo, t } from 'fyo';
import { t } from 'fyo';
import { fyo } from '../initFyo';
import { getIsInventoryEnabled } from './misc';
import { SidebarConfig, SidebarRoot } from './types';
export async function getSidebarConfig(): Promise<SidebarConfig> {
@ -7,15 +8,6 @@ export async function getSidebarConfig(): Promise<SidebarConfig> {
return getFilteredSidebar(sideBar);
}
async function getIsInventoryEnabled(fyo: Fyo) {
const values = await fyo.db.getAllRaw('Item', {
fields: ['name'],
filters: { trackItem: true },
});
return !!values.length;
}
function getFilteredSidebar(sideBar: SidebarConfig): SidebarConfig {
return sideBar.filter((root) => {
root.items = root.items?.filter((item) => {