mirror of
https://github.com/frappe/books.git
synced 2026-08-24 10:04:45 -05:00
Merge pull request #680 from frappe/minor-improvements
feat: a few minor improvements
This commit is contained in:
@@ -2212,8 +2212,10 @@
|
||||
},
|
||||
"Yemen": {
|
||||
"code": "ye",
|
||||
"currency": "YER",
|
||||
"currency_fraction": "Fils",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_name": "Yemeni Rial",
|
||||
"currency_symbol": "﷼",
|
||||
"timezones": ["Asia/Aden"],
|
||||
"locale": "ar-YE"
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { AccountingSettings } from 'models/baseModels/AccountingSettings/Ac
|
||||
import type { Defaults } from 'models/baseModels/Defaults/Defaults';
|
||||
import type { PrintSettings } from 'models/baseModels/PrintSettings/PrintSettings';
|
||||
import type { InventorySettings } from 'models/inventory/InventorySettings';
|
||||
import { Misc } from 'models/baseModels/Misc';
|
||||
|
||||
/**
|
||||
* The functions below are used for dynamic evaluation
|
||||
@@ -55,6 +56,7 @@ export interface SinglesMap {
|
||||
InventorySettings?: InventorySettings;
|
||||
PrintSettings?: PrintSettings;
|
||||
Defaults?: Defaults;
|
||||
Misc?: Misc;
|
||||
[key: string]: Doc | undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { HiddenMap } from 'fyo/model/types';
|
||||
|
||||
export class Misc extends Doc {
|
||||
openCount?: number;
|
||||
useFullWidth?: boolean;
|
||||
override hidden: HiddenMap = {};
|
||||
}
|
||||
+3
-1
@@ -7,12 +7,14 @@ import { Defaults } from './baseModels/Defaults/Defaults';
|
||||
import { Item } from './baseModels/Item/Item';
|
||||
import { JournalEntry } from './baseModels/JournalEntry/JournalEntry';
|
||||
import { JournalEntryAccount } from './baseModels/JournalEntryAccount/JournalEntryAccount';
|
||||
import { Misc } from './baseModels/Misc';
|
||||
import { Party } from './baseModels/Party/Party';
|
||||
import { Payment } from './baseModels/Payment/Payment';
|
||||
import { PaymentFor } from './baseModels/PaymentFor/PaymentFor';
|
||||
import { PriceList } from './baseModels/PriceList/PriceList';
|
||||
import { PriceListItem } from './baseModels/PriceList/PriceListItem';
|
||||
import { PrintSettings } from './baseModels/PrintSettings/PrintSettings';
|
||||
import { PrintTemplate } from './baseModels/PrintTemplate';
|
||||
import { PurchaseInvoice } from './baseModels/PurchaseInvoice/PurchaseInvoice';
|
||||
import { PurchaseInvoiceItem } from './baseModels/PurchaseInvoiceItem/PurchaseInvoiceItem';
|
||||
import { SalesInvoice } from './baseModels/SalesInvoice/SalesInvoice';
|
||||
@@ -32,7 +34,6 @@ import { StockLedgerEntry } from './inventory/StockLedgerEntry';
|
||||
import { StockMovement } from './inventory/StockMovement';
|
||||
import { StockMovementItem } from './inventory/StockMovementItem';
|
||||
|
||||
import { PrintTemplate } from './baseModels/PrintTemplate';
|
||||
export const models = {
|
||||
Account,
|
||||
AccountingLedgerEntry,
|
||||
@@ -43,6 +44,7 @@ export const models = {
|
||||
Item,
|
||||
JournalEntry,
|
||||
JournalEntryAccount,
|
||||
Misc,
|
||||
Party,
|
||||
Payment,
|
||||
PaymentFor,
|
||||
|
||||
+5
-3
@@ -232,13 +232,15 @@ export default defineComponent({
|
||||
const { onboardingComplete } = await fyo.doc.getDoc('GetStarted');
|
||||
const { hideGetStarted } = await fyo.doc.getDoc('SystemSettings');
|
||||
|
||||
let route = '/get-started';
|
||||
if (hideGetStarted || onboardingComplete) {
|
||||
await routeTo('/');
|
||||
} else {
|
||||
await routeTo('/get-started');
|
||||
route = '/';
|
||||
}
|
||||
|
||||
await routeTo(localStorage.getItem('lastRoute') || route);
|
||||
},
|
||||
async showDbSelector(): Promise<void> {
|
||||
localStorage.clear();
|
||||
fyo.config.set('lastSelectedFilePath', null);
|
||||
fyo.telemetry.stop();
|
||||
await fyo.purgeCache();
|
||||
|
||||
@@ -26,7 +26,16 @@
|
||||
:class="config.iconColor"
|
||||
/>
|
||||
</div>
|
||||
<p v-if="detail" class="text-base">{{ detail }}</p>
|
||||
|
||||
<template v-if="detail">
|
||||
<p v-if="typeof detail === 'string'" class="text-base">
|
||||
{{ detail }}
|
||||
</p>
|
||||
|
||||
<div v-else v-for="d of detail">
|
||||
<p class="text-base">{{ d }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex justify-end gap-4 mt-4">
|
||||
<Button
|
||||
v-for="(b, index) of buttons"
|
||||
@@ -57,7 +66,7 @@ export default defineComponent({
|
||||
type: { type: String as PropType<ToastType>, default: 'info' },
|
||||
title: { type: String, required: true },
|
||||
detail: {
|
||||
type: String,
|
||||
type: [String, Array] as PropType<string | string[]>,
|
||||
required: false,
|
||||
},
|
||||
buttons: {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</h1>
|
||||
<p class="text-gray-600 text-base select-none">
|
||||
{{
|
||||
t`Create a new file or select an existing one from your computer`
|
||||
t`Create a new company or select an existing one from your computer`
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
@@ -40,7 +40,7 @@
|
||||
{{ t`New Company` }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-600">
|
||||
{{ t`Create a new company and store it on your computer.` }}
|
||||
{{ t`Create a new company and store it on your computer` }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,7 +59,7 @@
|
||||
{{ t`Existing Company` }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-600">
|
||||
{{ t`Load an existing company from your computer.` }}
|
||||
{{ t`Load an existing company from your computer` }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -79,7 +79,7 @@
|
||||
{{ t`Create Demo` }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-600">
|
||||
{{ t`Create an instance with demo data to try out Frappe Books.` }}
|
||||
{{ t`Create a demo company to try out Frappe Books` }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
<template>
|
||||
<div class="flex flex-col flex-1 bg-gray-25">
|
||||
<PageHeader :border="true">
|
||||
<template #left>
|
||||
<AutoComplete
|
||||
v-if="templateList.length"
|
||||
:df="{
|
||||
fieldtype: 'AutoComplete',
|
||||
fieldname: 'templateName',
|
||||
label: t`Template Name`,
|
||||
options: templateList.map((n) => ({ label: n, value: n })),
|
||||
}"
|
||||
input-class="text-base py-0 h-8"
|
||||
class="w-56"
|
||||
:border="true"
|
||||
:value="templateName ?? ''"
|
||||
@change="onTemplateNameChange"
|
||||
/>
|
||||
</template>
|
||||
<PageHeader :border="true" :title="t`Print View`" class="bg-white">
|
||||
<AutoComplete
|
||||
v-if="templateList.length"
|
||||
:df="{
|
||||
fieldtype: 'AutoComplete',
|
||||
fieldname: 'templateName',
|
||||
label: t`Template Name`,
|
||||
options: templateList.map((n) => ({ label: n, value: n })),
|
||||
}"
|
||||
input-class="text-base py-0 h-8"
|
||||
class="w-40"
|
||||
:border="true"
|
||||
:value="templateName ?? ''"
|
||||
@change="onTemplateNameChange"
|
||||
/>
|
||||
<DropdownWithActions :actions="actions" :title="t`More`" />
|
||||
<Button class="text-xs" type="primary" @click="savePDF">
|
||||
{{ t`Save as PDF` }}
|
||||
|
||||
@@ -204,7 +204,7 @@ export default defineComponent({
|
||||
.filter((_, i) => this.columnSelection[i]);
|
||||
|
||||
const matrix: { value: string; idx: number }[][] = [columns];
|
||||
const start = Math.max(this.start - 1, 1);
|
||||
const start = Math.max(this.start - 1, 0);
|
||||
const end = Math.min(start + this.limit, this.report.reportData.length);
|
||||
const slice = this.report.reportData.slice(start, end);
|
||||
|
||||
@@ -252,6 +252,9 @@ export default defineComponent({
|
||||
this.limit = this.report.reportData.length;
|
||||
this.columnSelection = this.report.columns.map(() => true);
|
||||
this.setScale();
|
||||
|
||||
// @ts-ignore
|
||||
window.rpv = this;
|
||||
},
|
||||
methods: {
|
||||
setScale() {
|
||||
|
||||
+4
-2
@@ -10,9 +10,9 @@ import QuickEditForm from 'src/pages/QuickEditForm.vue';
|
||||
import Report from 'src/pages/Report.vue';
|
||||
import Settings from 'src/pages/Settings/Settings.vue';
|
||||
import TemplateBuilder from 'src/pages/TemplateBuilder/TemplateBuilder.vue';
|
||||
import type { HistoryState } from 'vue-router';
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
|
||||
import { historyState } from './utils/refs';
|
||||
import type { HistoryState } from 'vue-router';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
@@ -122,10 +122,12 @@ const routes: RouteRecordRaw[] = [
|
||||
|
||||
const router = createRouter({ routes, history: createWebHistory() });
|
||||
|
||||
router.afterEach(() => {
|
||||
router.afterEach(({ fullPath }) => {
|
||||
const state = history.state as HistoryState;
|
||||
historyState.forward = !!state.forward;
|
||||
historyState.back = !!state.back;
|
||||
|
||||
localStorage.setItem('lastRoute', fullPath);
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ export type PrintValues = {
|
||||
export interface DialogOptions {
|
||||
title: string;
|
||||
type?: ToastType;
|
||||
detail?: string;
|
||||
detail?: string | string[];
|
||||
buttons?: DialogButton[];
|
||||
}
|
||||
|
||||
|
||||
+74
-1
@@ -544,7 +544,19 @@ async function syncWithoutDialog(doc: Doc): Promise<boolean> {
|
||||
}
|
||||
|
||||
export async function commonDocSubmit(doc: Doc): Promise<boolean> {
|
||||
const success = await showSubmitOrSyncDialog(doc, 'submit');
|
||||
let success = true;
|
||||
if (
|
||||
doc instanceof SalesInvoice &&
|
||||
fyo.singles.AccountingSettings?.enableInventory
|
||||
) {
|
||||
success = await showInsufficientInventoryDialog(doc);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
success = await showSubmitOrSyncDialog(doc, 'submit');
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
@@ -553,6 +565,67 @@ export async function commonDocSubmit(doc: Doc): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function showInsufficientInventoryDialog(doc: SalesInvoice) {
|
||||
const insufficient: { item: string; quantity: number }[] = [];
|
||||
for (const { item, quantity, batch } of doc.items ?? []) {
|
||||
if (!item || typeof quantity !== 'number') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const isTracked = await fyo.getValue(ModelNameEnum.Item, item, 'trackItem');
|
||||
if (!isTracked) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const stockQuantity =
|
||||
(await fyo.db.getStockQuantity(
|
||||
item,
|
||||
undefined,
|
||||
undefined,
|
||||
doc.date!.toISOString(),
|
||||
batch
|
||||
)) ?? 0;
|
||||
|
||||
if (stockQuantity > quantity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
insufficient.push({ item, quantity: quantity - stockQuantity });
|
||||
}
|
||||
|
||||
if (insufficient.length) {
|
||||
const buttons = [
|
||||
{
|
||||
label: t`Yes`,
|
||||
action: () => true,
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
label: t`No`,
|
||||
action: () => false,
|
||||
isEscape: true,
|
||||
},
|
||||
];
|
||||
|
||||
const list = insufficient
|
||||
.map(({ item, quantity }) => `${item} (${quantity})`)
|
||||
.join(', ');
|
||||
const detail = [
|
||||
t`The following items have insufficient quantity for Shipment: ${list}`,
|
||||
t`Continue submitting Sales Invoice?`,
|
||||
];
|
||||
|
||||
return (await showDialog({
|
||||
title: t`Insufficient Quantity`,
|
||||
type: 'warning',
|
||||
detail,
|
||||
buttons,
|
||||
})) as boolean;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function showSubmitOrSyncDialog(doc: Doc, type: 'submit' | 'sync') {
|
||||
const label = getDocReferenceLabel(doc);
|
||||
let title = t`Save ${label}?`;
|
||||
|
||||
Reference in New Issue
Block a user