2
0
mirror of https://github.com/frappe/books.git synced 2024-11-08 14:50:56 +00:00

incr: use CommonForm for a few docs

- show errors
- shit setName to after validation
This commit is contained in:
18alantom 2023-02-21 11:04:35 +05:30
parent 0c077f09d2
commit e0141b70d8
14 changed files with 262 additions and 150 deletions

View File

@ -117,7 +117,10 @@ export class DocHandler {
const idx = this.#temporaryNameCounters[schema.name];
this.#temporaryNameCounters[schema.name] = idx + 1;
return this.fyo.t`New ${schema.label ?? schema.name} - Temp No. ${idx}`;
return this.fyo.t`New ${schema.label ?? schema.name} ${String(idx).padStart(
2,
'0'
)}`;
}
/**

View File

@ -868,9 +868,9 @@ export class Doc extends Observable<DocValue | Doc[]> {
}
async _insert() {
await setName(this, this.fyo);
this._setBaseMetaValues();
await this._preSync();
await setName(this, this.fyo);
const validDict = this.getValidDict(false, true);
let data: DocValueMap;

View File

@ -111,6 +111,7 @@ export class Item extends Doc {
static getListViewSettings(): ListViewSettings {
return {
formRoute: ({ name }) => `/edit/Item/${name}`,
columns: ['name', 'unit', 'tax', 'rate'],
};
}

View File

@ -617,6 +617,7 @@ export class Payment extends Transactional {
static getListViewSettings(fyo: Fyo): ListViewSettings {
return {
formRoute: ({ name }) => `/edit/Payment/${name}`,
columns: ['name', getDocStatusListColumn(), 'party', 'date', 'amount'],
};
}

View File

@ -75,6 +75,7 @@ export class StockMovement extends Transfer {
static getListViewSettings(fyo: Fyo): ListViewSettings {
return {
formRoute: ({ name }) => `/edit/StockMovement/${name}`,
columns: [
'name',
getDocStatusListColumn(),

View File

@ -4,50 +4,20 @@
"isSingle": false,
"naming": "manual",
"fields": [
{
"fieldname": "image",
"label": "Image",
"section": "Default",
"fieldtype": "AttachImage"
},
{
"fieldname": "name",
"label": "Item Name",
"fieldtype": "Data",
"placeholder": "Item Name",
"section": "Default",
"required": true
},
{
"fieldname": "image",
"label": "Image",
"fieldtype": "AttachImage"
},
{
"fieldname": "description",
"label": "Description",
"placeholder": "Item Description",
"fieldtype": "Text"
},
{
"fieldname": "unit",
"label": "Unit Type",
"fieldtype": "Link",
"target": "UOM",
"create": true,
"default": "Unit",
"placeholder": "Unit Type"
},
{
"fieldname": "itemType",
"label": "Type",
"placeholder": "Type",
"fieldtype": "Select",
"default": "Product",
"options": [
{
"value": "Product",
"label": "Product"
},
{
"value": "Service",
"label": "Service"
}
]
},
{
"fieldname": "for",
"label": "For",
@ -67,14 +37,57 @@
}
],
"required": true,
"section": "Default",
"default": "Both"
},
{
"fieldname": "itemType",
"label": "Type",
"placeholder": "Type",
"fieldtype": "Select",
"default": "Product",
"section": "Default",
"options": [
{
"value": "Product",
"label": "Product"
},
{
"value": "Service",
"label": "Service"
}
]
},
{
"fieldname": "unit",
"label": "Unit Type",
"fieldtype": "Link",
"target": "UOM",
"create": true,
"default": "Unit",
"section": "Details",
"placeholder": "Unit Type"
},
{
"fieldname": "rate",
"label": "Rate",
"section": "Details",
"fieldtype": "Currency"
},
{
"fieldname": "description",
"label": "Description",
"placeholder": "Item Description",
"section": "Details",
"fieldtype": "Text"
},
{
"fieldname": "incomeAccount",
"label": "Sales Acc.",
"fieldtype": "Link",
"target": "Account",
"placeholder": "Income",
"section": "Accounts",
"create": true,
"required": true
},
@ -84,6 +97,7 @@
"fieldtype": "Link",
"target": "Account",
"placeholder": "Expense",
"section": "Accounts",
"create": true,
"required": true
},
@ -92,31 +106,30 @@
"label": "Tax",
"fieldtype": "Link",
"target": "Tax",
"section": "Accounts",
"create": true,
"placeholder": "Tax"
},
{
"fieldname": "rate",
"label": "Rate",
"fieldtype": "Currency"
},
{
"fieldname": "hsnCode",
"label": "HSN/SAC",
"fieldtype": "Int",
"placeholder": "HSN/SAC Code"
"placeholder": "HSN/SAC Code",
"section": "Inventory"
},
{
"fieldname": "barcode",
"label": "Barcode",
"fieldtype": "Data",
"placeholder": "Barcode"
"placeholder": "Barcode",
"section": "Inventory"
},
{
"fieldname": "trackItem",
"label": "Track Item",
"fieldtype": "Check",
"default": false
"default": false,
"section": "Inventory"
}
],
"quickEditFields": [

View File

@ -100,6 +100,27 @@
"required": true,
"section": "Details"
},
{
"fieldname": "clearanceDate",
"label": "Clearance Date",
"placeholder": "Clearance Date",
"fieldtype": "Date",
"section": "Details"
},
{
"fieldname": "referenceId",
"label": "Ref. / Cheque No.",
"placeholder": "Ref. / Cheque No.",
"fieldtype": "Data",
"section": "Details"
},
{
"fieldname": "referenceDate",
"label": "Reference Date",
"placeholder": "Ref. Date",
"fieldtype": "Date",
"section": "Details"
},
{
"fieldname": "amount",
"label": "Amount",
@ -120,27 +141,6 @@
"computed": true,
"section": "Amounts"
},
{
"fieldname": "referenceId",
"label": "Ref. / Cheque No.",
"placeholder": "Ref. / Cheque No.",
"fieldtype": "Data",
"section": "References"
},
{
"fieldname": "referenceDate",
"label": "Reference Date",
"placeholder": "Ref. Date",
"fieldtype": "Date",
"section": "References"
},
{
"fieldname": "clearanceDate",
"label": "Clearance Date",
"placeholder": "Clearance Date",
"fieldtype": "Date",
"section": "References"
},
{
"fieldname": "for",
"label": "Payment Reference",

View File

@ -11,13 +11,18 @@
"fieldname": "name",
"fieldtype": "Data",
"required": true,
"readOnly": true
"readOnly": true,
"section": "Default"
},
{
"fieldname": "date",
"label": "Date",
"fieldtype": "Datetime",
"required": true
"fieldname": "numberSeries",
"label": "Number Series",
"fieldtype": "Link",
"target": "NumberSeries",
"create": true,
"required": true,
"default": "SMOV-",
"section": "Default"
},
{
"fieldname": "movementType",
@ -41,29 +46,30 @@
"label": "Manufacture"
}
],
"required": true
},
{
"fieldname": "numberSeries",
"label": "Number Series",
"fieldtype": "Link",
"target": "NumberSeries",
"create": true,
"required": true,
"default": "SMOV-"
"section": "Default"
},
{
"fieldname": "amount",
"label": "Total Amount",
"fieldtype": "Currency",
"readOnly": true
"fieldname": "date",
"label": "Date",
"fieldtype": "Datetime",
"required": true,
"section": "Default"
},
{
"fieldname": "items",
"label": "Items",
"fieldtype": "Table",
"target": "StockMovementItem",
"required": true
"required": true,
"section": "Items"
},
{
"fieldname": "amount",
"label": "Total Amount",
"fieldtype": "Currency",
"readOnly": true,
"section": "Items"
}
],
"quickEditFields": [

View File

@ -116,7 +116,7 @@ export default {
},
async openNewDoc() {
const schemaName = this.df.target;
const doc = await fyo.doc.getNewDoc(schemaName);
const doc = fyo.doc.getNewDoc(schemaName);
const filters = await this.getCreateFilters();

View File

@ -13,15 +13,12 @@
</p>
<feather-icon v-else name="more-horizontal" class="w-4 h-4" />
</DropdownWithActions>
<Button v-if="doc?.canSave" type="primary" @click="() => doc.sync()">
<Button v-if="doc?.canSave" type="primary" @click="sync">
{{ t`Save` }}
</Button>
<Button
v-else-if="doc?.canSubmit"
type="primary"
@click="() => doc.submit()"
>{{ t`Submit` }}</Button
>
<Button v-else-if="doc?.canSubmit" type="primary" @click="submit">{{
t`Submit`
}}</Button>
</template>
<template #body>
<FormHeader
@ -44,6 +41,8 @@
:title="name"
:fields="fields"
:doc="doc"
:errors="errors"
@value-change="onValueChange"
/>
</div>
@ -61,7 +60,7 @@
bottom-0
bg-white
"
v-if="groupedFields.size > 1"
v-if="groupedFields && groupedFields.size > 1"
>
<div
v-for="key of groupedFields.keys()"
@ -100,6 +99,7 @@
</FormContainer>
</template>
<script lang="ts">
import { DocValue } from 'fyo/core/types';
import { Doc } from 'fyo/model/doc';
import { ValidationError } from 'fyo/utils/errors';
import { getDocStatus } from 'models/helpers';
@ -110,6 +110,8 @@ import DropdownWithActions from 'src/components/DropdownWithActions.vue';
import FormContainer from 'src/components/FormContainer.vue';
import FormHeader from 'src/components/FormHeader.vue';
import StatusBadge from 'src/components/StatusBadge.vue';
import { handleErrorWithDialog } from 'src/errorHandling';
import { getErrorMessage } from 'src/utils';
import { ActionGroup, UIGroupedFields } from 'src/utils/types';
import {
getFieldsGroupedByTabAndSection,
@ -122,7 +124,7 @@ import CommonFormSection from './CommonFormSection.vue';
export default defineComponent({
props: {
name: { type: String, default: '' },
schemaName: { type: String, default: ModelNameEnum.StockMovement },
schemaName: { type: String, default: ModelNameEnum.SalesInvoice },
},
provide() {
return {
@ -133,12 +135,16 @@ export default defineComponent({
},
data() {
return {
errors: {},
docOrNull: null,
activeTab: 'Default',
groupedFields: null,
quickEditDoc: null,
} as {
errors: Record<string, string>;
docOrNull: null | Doc;
activeTab: string;
groupedFields: null | UIGroupedFields;
quickEditDoc: null | Doc;
};
},
@ -149,6 +155,7 @@ export default defineComponent({
}
await this.setDoc();
this.groupedFields = this.getGroupedFields();
},
computed: {
hasDoc(): boolean {
@ -187,7 +194,7 @@ export default defineComponent({
return this.t`New Entry`;
}
return this.docOrNull?.name!?? this.t`New Entry`;
return this.docOrNull?.name! ?? this.t`New Entry`;
},
schema(): Schema {
const schema = this.fyo.schemaMap[this.schemaName];
@ -198,6 +205,10 @@ export default defineComponent({
return schema;
},
activeGroup(): Map<string, Field[]> {
if (!this.groupedFields) {
return new Map();
}
const group = this.groupedFields.get(this.activeTab);
if (!group) {
throw new ValidationError(
@ -207,9 +218,6 @@ export default defineComponent({
return group;
},
groupedFields(): UIGroupedFields {
return getFieldsGroupedByTabAndSection(this.schema);
},
groupedActions(): ActionGroup[] {
if (!this.hasDoc) {
return [];
@ -219,17 +227,53 @@ export default defineComponent({
},
},
methods: {
getGroupedFields(): UIGroupedFields {
if (!this.hasDoc) {
return new Map();
}
return getFieldsGroupedByTabAndSection(this.schema, this.doc);
},
async sync() {
try {
await this.doc.sync();
} catch (err) {
if (!(err instanceof Error)) {
return;
}
await handleErrorWithDialog(err, this.doc);
}
},
async submit() {
try {
await this.doc.submit();
} catch (err) {
if (!(err instanceof Error)) {
return;
}
await handleErrorWithDialog(err, this.doc);
}
},
async setDoc() {
if (this.hasDoc) {
return;
}
if (this.name) {
this.docOrNull = await this.fyo.doc.getDoc(this.schemaName, this.name);
await this.setDocFromName(this.name);
} else {
this.docOrNull = this.fyo.doc.getNewDoc(this.schemaName);
}
},
async setDocFromName(name: string) {
try {
this.docOrNull = await this.fyo.doc.getDoc(this.schemaName, name);
} catch (err) {
this.docOrNull = this.fyo.doc.getNewDoc(this.schemaName);
}
},
async toggleQuickEditDoc(doc: Doc | null) {
if (this.quickEditDoc && doc) {
this.quickEditDoc = null;
@ -238,6 +282,23 @@ export default defineComponent({
this.quickEditDoc = doc;
},
async onValueChange(field: Field, value: DocValue) {
const { fieldname } = field;
console.log(fieldname, value);
delete this.errors[fieldname];
try {
await this.doc.set(fieldname, value);
} catch (err) {
if (!(err instanceof Error)) {
return;
}
this.errors[fieldname] = getErrorMessage(err, this.doc);
}
this.groupedFields = this.getGroupedFields();
},
},
components: {
FormContainer,

View File

@ -1,5 +1,5 @@
<template>
<div v-if="filteredFields.length > 0">
<div v-if="(fields ?? []).length > 0">
<div
v-if="showTitle && title"
class="flex justify-between items-center cursor-pointer select-none"
@ -15,55 +15,47 @@
/>
</div>
<div class="grid gap-4 gap-x-8 grid-cols-2" v-if="!collapsed">
<FormControl
v-for="field of filteredFields"
class="mt-auto"
:class="field.fieldtype === 'Table' ? 'col-span-2 text-base' : ''"
:show-label="true"
:border="true"
<div
v-for="field of fields"
:key="field.fieldname"
:df="field"
:value="doc[field.fieldname]"
@editrow="(doc:Doc) => $emit('editrow', doc)"
@change="async (value) => await doc.set(field.fieldname, value)"
/>
class="mb-auto"
:class="field.fieldtype === 'Table' ? 'col-span-2 text-base' : ''"
>
<FormControl
:show-label="true"
:border="true"
:df="field"
:value="doc[field.fieldname]"
@editrow="(doc: Doc) => $emit('editrow', doc)"
@change="(value: DocValue) => $emit('value-change', field, value)"
/>
<div v-if="errors?.[field.fieldname]" class="text-sm text-red-600">
{{ errors[field.fieldname] }}
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { DocValue } from 'fyo/core/types';
import { Doc } from 'fyo/model/doc';
import { Field } from 'schemas/types';
import FormControl from 'src/components/Controls/FormControl.vue';
import { evaluateHidden } from 'src/utils/doc';
import { defineComponent, PropType } from 'vue';
export default defineComponent({
emits: ['editrow'],
emits: ['editrow', 'value-change'],
props: {
title: String,
errors: Object as PropType<Record<string, string>>,
showTitle: Boolean,
doc: { type: Object as PropType<Doc>, required: true },
fields: Array as PropType<Field[]>,
},
data() {
return { collapsed: false };
},
computed: {
filteredFields(): Field[] {
const fields: Field[] = [];
for (const field of this.fields ?? []) {
if (evaluateHidden(field, this.doc)) {
continue;
}
if (field.meta) {
continue;
}
fields.push(field);
}
return fields;
},
return { collapsed: false } as {
collapsed: boolean;
};
},
components: { FormControl },
});

View File

@ -9,6 +9,7 @@ import JournalEntryForm from 'src/pages/JournalEntryForm.vue';
import ListView from 'src/pages/ListView/ListView.vue';
import PrintView from 'src/pages/PrintView/PrintView.vue';
import QuickEditForm from 'src/pages/QuickEditForm.vue';
import CommonForm from 'src/pages/CommonForm/CommonForm.vue';
import Report from 'src/pages/Report.vue';
import Settings from 'src/pages/Settings/Settings.vue';
import {
@ -43,6 +44,33 @@ function getGeneralFormItems(): RouteRecordRaw[] {
);
}
function getCommonFormItems(): RouteRecordRaw[] {
return [
ModelNameEnum.Payment,
ModelNameEnum.StockMovement,
ModelNameEnum.Item,
].map((schemaName) => {
return {
path: `/edit/${schemaName}/:name`,
name: `${schemaName}Form`,
components: {
default: CommonForm,
edit: QuickEditForm,
},
props: {
default: (route) => {
route.params.schemaName = schemaName;
return {
schemaName,
name: route.params.name,
};
},
edit: (route) => route.query,
},
};
});
}
const routes: RouteRecordRaw[] = [
{
path: '/',
@ -53,6 +81,7 @@ const routes: RouteRecordRaw[] = [
component: GetStarted,
},
...getGeneralFormItems(),
...getCommonFormItems(),
{
path: '/edit/JournalEntry/:name',
name: 'JournalEntryForm',
@ -167,6 +196,9 @@ export function getCreateRoute(
ModelNameEnum.JournalEntry,
ModelNameEnum.Shipment,
ModelNameEnum.PurchaseReceipt,
ModelNameEnum.StockMovement,
ModelNameEnum.Payment,
ModelNameEnum.Item,
].includes(schemaName as ModelNameEnum)
) {
return `/edit/${schemaName}/${name}`;

View File

@ -93,25 +93,16 @@ async function openFormEditDoc(schemaName: string, fyo: Fyo) {
function getCreateList(fyo: Fyo): SearchItem[] {
const hasInventory = fyo.doc.singles.AccountingSettings?.enableInventory;
const quickEditCreateList = [
...(hasInventory ? [ModelNameEnum.StockMovement] : []),
].map(
(schemaName) =>
({
label: fyo.schemaMap[schemaName]?.label,
group: 'Create',
action() {
openQuickEditDoc(schemaName, fyo);
},
} as SearchItem)
);
const formEditCreateList = [
ModelNameEnum.SalesInvoice,
ModelNameEnum.PurchaseInvoice,
ModelNameEnum.JournalEntry,
...(hasInventory
? [ModelNameEnum.Shipment, ModelNameEnum.PurchaseReceipt]
? [
ModelNameEnum.Shipment,
ModelNameEnum.PurchaseReceipt,
ModelNameEnum.StockMovement,
]
: []),
].map(
(schemaName) =>
@ -197,7 +188,7 @@ function getCreateList(fyo: Fyo): SearchItem[] {
} as SearchItem;
});
return [quickEditCreateList, formEditCreateList, filteredCreateList].flat();
return [formEditCreateList, filteredCreateList].flat();
}
function getReportList(fyo: Fyo): SearchItem[] {

View File

@ -17,6 +17,7 @@ import { IPC_ACTIONS } from 'utils/messages';
import { App, createApp, h, ref } from 'vue';
import { RouteLocationRaw } from 'vue-router';
import { stringifyCircular } from './';
import { evaluateHidden } from './doc';
import {
ActionGroup,
MessageDialogOptions,
@ -73,6 +74,7 @@ export async function openQuickEdit({
if (forWhat[0] === 'not in' && forWhat[1] === 'Sales') {
defaults = Object.assign({ for: 'Purchases' });
}
console.log(method, schemaName, name);
router[method]({
query: {
@ -390,7 +392,8 @@ function getDuplicateAction(doc: Doc): Action {
}
export function getFieldsGroupedByTabAndSection(
schema: Schema
schema: Schema,
doc: Doc
): UIGroupedFields {
const grouped: UIGroupedFields = new Map();
for (const field of schema?.fields ?? []) {
@ -405,6 +408,14 @@ export function getFieldsGroupedByTabAndSection(
tabbed.set(section, []);
}
if (field.meta) {
continue;
}
if (evaluateHidden(field, doc)) {
continue;
}
tabbed.get(section)!.push(field);
}