├── .eslintrc.js ├── .git-blame-ignore-revs ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── 1-bug_template.yml │ ├── 2-feature_template.yml │ ├── 3-general_question_template.yml │ └── config.yml ├── frappe-books-preview.png ├── logo.png └── workflows │ ├── build.yml │ ├── lint.yml │ ├── publish.yml │ └── test.yml ├── .gitignore ├── .prettierignore ├── LICENSE ├── META.md ├── README.md ├── backend ├── database │ ├── bespoke.ts │ ├── core.ts │ ├── manager.ts │ ├── runPatch.ts │ ├── tests │ │ ├── helpers.ts │ │ └── testCore.spec.ts │ └── types.ts ├── helpers.ts └── patches │ ├── addUOMs.ts │ ├── createInventoryNumberSeries.ts │ ├── createPaymentMethods.ts │ ├── fixItemHSNField.ts │ ├── fixRoundOffAccount.ts │ ├── index.ts │ ├── setPaymentReferenceType.ts │ ├── testPatch.ts │ ├── updateSchemas.ts │ └── v0_21_0 │ └── fixLedgerDateTime.ts ├── build ├── entitlements.mac.plist ├── icon.icns ├── icon.ico ├── icon.png ├── icons │ ├── 128x128.png │ ├── 16x16.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 512x512.png │ └── 64x64.png ├── installerIcon.ico ├── scripts │ ├── build.mjs │ ├── dev.mjs │ └── helpers.mjs └── uninstallerIcon.ico ├── colors.json ├── dummy ├── README.md ├── helpers.ts ├── index.ts ├── items.json ├── logo.ts ├── parties.json └── tests │ └── testDummy.spec.ts ├── electron-builder-config.mjs ├── electron-builder.yml.disabled ├── fixtures ├── countryInfo.json └── verified │ ├── ae.json │ ├── ca.json │ ├── fr.json │ ├── gt.json │ ├── hu.json │ ├── id.json │ ├── in.json │ ├── mx.json │ ├── ni.json │ ├── nl.json │ ├── sg.json │ └── standardCOA.json ├── fyo ├── README.md ├── core │ ├── authHandler.ts │ ├── converter.ts │ ├── dbHandler.ts │ ├── docHandler.ts │ └── types.ts ├── demux │ ├── auth.ts │ ├── config.ts │ └── db.ts ├── index.ts ├── model │ ├── doc.ts │ ├── errorHelpers.ts │ ├── helpers.ts │ ├── naming.ts │ ├── types.ts │ └── validationFunction.ts ├── models │ ├── CustomField.ts │ ├── CustomForm.ts │ ├── NumberSeries.ts │ ├── SystemSettings.ts │ └── index.ts ├── telemetry │ ├── telemetry.ts │ └── types.ts ├── tests │ ├── helpers.ts │ ├── testFyo.spec.ts │ └── testObservable.spec.ts └── utils │ ├── cacheManager.ts │ ├── consts.ts │ ├── errors.ts │ ├── format.ts │ ├── index.ts │ ├── observable.ts │ ├── translation.ts │ └── types.ts ├── jobs └── triggerErpNextSync.ts ├── main.ts ├── main ├── api.ts ├── contactMothership.ts ├── getLanguageMap.ts ├── getPrintTemplates.ts ├── helpers.ts ├── initSheduler.ts ├── preload.ts ├── printHtmlDocument.ts ├── registerAppLifecycleListeners.ts ├── registerAutoUpdaterListeners.ts ├── registerIpcMainActionListeners.ts ├── registerIpcMainMessageListeners.ts ├── registerProcessListeners.ts └── saveHtmlAsPdf.ts ├── models ├── README.md ├── Transactional │ ├── LedgerPosting.ts │ ├── Transactional.ts │ └── types.ts ├── baseModels │ ├── Account │ │ ├── Account.ts │ │ └── types.ts │ ├── AccountingLedgerEntry │ │ └── AccountingLedgerEntry.ts │ ├── AccountingSettings │ │ └── AccountingSettings.ts │ ├── Address │ │ └── Address.ts │ ├── AppliedCouponCodes │ │ └── AppliedCouponCodes.ts │ ├── CollectionRulesItems │ │ └── CollectionRulesItems.ts │ ├── CouponCode │ │ └── CouponCode.ts │ ├── Defaults │ │ └── Defaults.ts │ ├── ERPNextSyncQueue │ │ └── ERPNextSyncQueue.ts │ ├── ERPNextSyncSettings │ │ └── ERPNextSyncSettings.ts │ ├── FetchFromERPNextQueue │ │ └── FetchFromERPNextQueue.ts │ ├── IntegrationErrorLog │ │ └── IntegrationErrorLog.ts │ ├── Invoice │ │ ├── Invoice.ts │ │ └── types.ts │ ├── InvoiceItem │ │ └── InvoiceItem.ts │ ├── Item │ │ └── Item.ts │ ├── JournalEntry │ │ └── JournalEntry.ts │ ├── JournalEntryAccount │ │ └── JournalEntryAccount.ts │ ├── Lead │ │ └── Lead.ts │ ├── LoyaltyPointEntry │ │ └── LoyaltyPointEntry.ts │ ├── LoyaltyProgram │ │ └── LoyaltyProgram.ts │ ├── Misc.ts │ ├── Party │ │ ├── Party.ts │ │ └── types.ts │ ├── Payment │ │ ├── Payment.ts │ │ └── types.ts │ ├── PaymentFor │ │ └── PaymentFor.ts │ ├── PaymentMethod │ │ └── PaymentMethod.ts │ ├── PriceList │ │ ├── PriceList.ts │ │ └── PriceListItem.ts │ ├── PricingRule │ │ └── PricingRule.ts │ ├── PricingRuleDetail │ │ └── PricingRuleDetail.ts │ ├── PricingRuleItem │ │ └── PricingRuleItem.ts │ ├── PrintSettings │ │ └── PrintSettings.ts │ ├── PrintTemplate.ts │ ├── PurchaseInvoice │ │ └── PurchaseInvoice.ts │ ├── PurchaseInvoiceItem │ │ └── PurchaseInvoiceItem.ts │ ├── SalesInvoice │ │ └── SalesInvoice.ts │ ├── SalesInvoiceItem │ │ └── SalesInvoiceItem.ts │ ├── SalesQuote │ │ └── SalesQuote.ts │ ├── SalesQuoteItem │ │ └── SalesQuoteItem.ts │ ├── SetupWizard │ │ └── SetupWizard.ts │ ├── Tax │ │ └── Tax.ts │ ├── TaxSummary │ │ └── TaxSummary.ts │ └── tests │ │ ├── testCouponCodes.spec.ts │ │ ├── testInvoice.spec.ts │ │ ├── testLead.spec.ts │ │ ├── testLoyaltyProgram.spec.ts │ │ ├── testPriceList.spec.ts │ │ └── testPricingRule.spec.ts ├── exchangeRate.ts ├── helpers.ts ├── index.ts ├── inventory │ ├── Batch.ts │ ├── InventorySettings.ts │ ├── Location.ts │ ├── Point of Sale │ │ ├── CashDenominations.ts │ │ ├── ClosingAmounts.ts │ │ ├── ClosingCash.ts │ │ ├── DefaultCashDenominations.ts │ │ ├── OpeningAmounts.ts │ │ ├── OpeningCash.ts │ │ ├── POSClosingShift.ts │ │ ├── POSOpeningShift.ts │ │ ├── POSSettings.ts │ │ └── tests │ │ │ └── testPointOfSale.spec.ts │ ├── PurchaseReceipt.ts │ ├── PurchaseReceiptItem.ts │ ├── SerialNumber.ts │ ├── Shipment.ts │ ├── ShipmentItem.ts │ ├── StockLedgerEntry.ts │ ├── StockManager.ts │ ├── StockMovement.ts │ ├── StockMovementItem.ts │ ├── StockTransfer.ts │ ├── StockTransferItem.ts │ ├── Transfer.ts │ ├── TransferItem.ts │ ├── helpers.ts │ ├── stockQueue.ts │ ├── tests │ │ ├── helpers.ts │ │ ├── testBatches.spec.ts │ │ ├── testInventory.spec.ts │ │ ├── testSerialNumbers.spec.ts │ │ ├── testStockMovement.spec.ts │ │ ├── testStockQueue.spec.ts │ │ └── testStockTransfer.spec.ts │ └── types.ts ├── regionalModels │ └── in │ │ ├── Address.ts │ │ ├── Party.ts │ │ └── types.ts └── types.ts ├── package.json ├── postcss.config.js ├── regional └── in.ts ├── reports ├── AccountReport.ts ├── BalanceSheet │ └── BalanceSheet.ts ├── GeneralLedger │ └── GeneralLedger.ts ├── GoodsAndServiceTax │ ├── BaseGSTR.ts │ ├── GSTR1.ts │ ├── GSTR2.ts │ ├── gstExporter.ts │ └── types.ts ├── LedgerReport.ts ├── ProfitAndLoss │ └── ProfitAndLoss.ts ├── README.md ├── Report.ts ├── TrialBalance │ └── TrialBalance.ts ├── commonExporter.ts ├── index.ts ├── inventory │ ├── StockBalance.ts │ ├── StockLedger.ts │ ├── helpers.ts │ └── types.ts └── types.ts ├── schemas ├── README.md ├── app │ ├── Account.json │ ├── AccountingLedgerEntry.json │ ├── AccountingSettings.json │ ├── Address.json │ ├── AppliedCouponCodes.json │ ├── Batch.json │ ├── CollectionRulesItems.json │ ├── Color.json │ ├── CouponCode.json │ ├── Currency.json │ ├── Defaults.json │ ├── ERPNextSyncQueue.json │ ├── ERPNextSyncSettings.json │ ├── FetchFromERPNextQueue.json │ ├── GetStarted.json │ ├── IntegrationErrorLog.json │ ├── Invoice.json │ ├── InvoiceItem.json │ ├── Item.json │ ├── JournalEntry.json │ ├── JournalEntryAccount.json │ ├── Lead.json │ ├── LoyaltyPointEntry.json │ ├── LoyaltyProgram.json │ ├── Misc.json │ ├── NumberSeries.json │ ├── Party.json │ ├── Payment.json │ ├── PaymentFor.json │ ├── PaymentMethod.json │ ├── PriceList.json │ ├── PriceListItem.json │ ├── PricingRule.json │ ├── PricingRuleDetail.json │ ├── PricingRuleItem.json │ ├── PrintSettings.json │ ├── PrintTemplate.json │ ├── PurchaseInvoice.json │ ├── PurchaseInvoiceItem.json │ ├── SalesInvoice.json │ ├── SalesInvoiceItem.json │ ├── SalesQuote.json │ ├── SalesQuoteItem.json │ ├── SetupWizard.json │ ├── Tax.json │ ├── TaxDetail.json │ ├── TaxSummary.json │ ├── UOM.json │ └── inventory │ │ ├── InventorySettings.json │ │ ├── Location.json │ │ ├── Point of Sale │ │ ├── CashDenominations.json │ │ ├── ClosingAmounts.json │ │ ├── ClosingCash.json │ │ ├── DefaultCashDenominations.json │ │ ├── OpeningAmounts.json │ │ ├── OpeningCash.json │ │ ├── POSClosingShift.json │ │ ├── POSOpeningShift.json │ │ ├── POSSettings.json │ │ └── POSShiftAmounts.json │ │ ├── PurchaseReceipt.json │ │ ├── PurchaseReceiptItem.json │ │ ├── SerialNumber.json │ │ ├── Shipment.json │ │ ├── ShipmentItem.json │ │ ├── StockLedgerEntry.json │ │ ├── StockMovement.json │ │ ├── StockMovementItem.json │ │ ├── StockTransfer.json │ │ ├── StockTransferItem.json │ │ └── UOMConversionItem.json ├── core │ ├── CustomField.json │ ├── CustomForm.json │ ├── PatchRun.json │ ├── SingleValue.json │ └── SystemSettings.json ├── index.ts ├── meta │ ├── base.json │ ├── child.json │ ├── submittable.json │ └── tree.json ├── regional │ ├── ch │ │ ├── AccountingSettings.json │ │ └── index.ts │ ├── in │ │ ├── AccountingSettings.json │ │ ├── Address.json │ │ ├── Party.json │ │ └── index.ts │ └── index.ts ├── schemas.ts ├── tests │ ├── Customer.json │ ├── Party.json │ ├── helpers.ts │ └── testSchemaBuilder.spec.ts └── types.ts ├── scripts ├── generateTranslations.ts ├── profile.sh ├── profile.ts ├── publish-mac-arm.sh ├── runner.sh └── test.sh ├── src ├── App.vue ├── README.md ├── assets │ ├── fonts │ │ └── Inter.var.woff2 │ └── img │ │ └── list-empty-state.svg ├── components │ ├── Avatar.vue │ ├── Badge.vue │ ├── Button.vue │ ├── Charts │ │ ├── BarChart.vue │ │ ├── DonutChart.vue │ │ └── LineChart.vue │ ├── Controls │ │ ├── AttachImage.vue │ │ ├── Attachment.vue │ │ ├── AutoComplete.vue │ │ ├── Barcode.vue │ │ ├── Base.vue │ │ ├── Button.vue │ │ ├── Check.vue │ │ ├── Color.vue │ │ ├── Currency.vue │ │ ├── Data.vue │ │ ├── Date.vue │ │ ├── Datetime.vue │ │ ├── DatetimePicker.vue │ │ ├── Datetime_old.vue │ │ ├── DynamicLink.vue │ │ ├── ExchangeRate.vue │ │ ├── Float.vue │ │ ├── FormControl.vue │ │ ├── Int.vue │ │ ├── LanguageSelector.vue │ │ ├── Link.vue │ │ ├── MultiLabelLink.vue │ │ ├── Secret.vue │ │ ├── Select.vue │ │ ├── Table.vue │ │ ├── TableRow.vue │ │ ├── Text.vue │ │ └── WeightEnabledBarcode.vue │ ├── Dialog.vue │ ├── Dropdown.vue │ ├── DropdownWithActions.vue │ ├── ErrorBoundary.vue │ ├── ExportWizard.vue │ ├── FeatherIcon.vue │ ├── FilterDropdown.vue │ ├── FormContainer.vue │ ├── FormHeader.vue │ ├── HorizontalResizer.vue │ ├── HowTo.vue │ ├── Icon.vue │ ├── Icons │ │ ├── 8 │ │ │ ├── arrow-right.vue │ │ │ ├── chevron-left.vue │ │ │ ├── chevron-right.vue │ │ │ ├── circle.vue │ │ │ ├── dot-horizontal.vue │ │ │ ├── dot-vertical.vue │ │ │ ├── index.ts │ │ │ ├── pencil.vue │ │ │ ├── plus.vue │ │ │ ├── up.vue │ │ │ └── x.vue │ │ ├── 12 │ │ │ ├── arrow-left-right.vue │ │ │ ├── drag-handle.vue │ │ │ ├── filter.vue │ │ │ ├── index.ts │ │ │ ├── list.vue │ │ │ ├── select.vue │ │ │ └── sidebar.vue │ │ ├── 16 │ │ │ ├── account-in.vue │ │ │ ├── address.vue │ │ │ ├── assets.vue │ │ │ ├── calendar.vue │ │ │ ├── circle.vue │ │ │ ├── down-small.vue │ │ │ ├── down.vue │ │ │ ├── expenses.vue │ │ │ ├── income.vue │ │ │ ├── index.ts │ │ │ ├── items.vue │ │ │ ├── liabilities.vue │ │ │ ├── mail.vue │ │ │ ├── normal.vue │ │ │ ├── opened.vue │ │ │ ├── phone.vue │ │ │ ├── plus.vue │ │ │ └── search.vue │ │ ├── 18 │ │ │ ├── check.vue │ │ │ ├── common-entries.vue │ │ │ ├── customer.vue │ │ │ ├── dashboard.vue │ │ │ ├── fb.vue │ │ │ ├── general.vue │ │ │ ├── gst.vue │ │ │ ├── index.ts │ │ │ ├── inventory.vue │ │ │ ├── invoice.vue │ │ │ ├── item.vue │ │ │ ├── mail.vue │ │ │ ├── opening-ac.vue │ │ │ ├── percentage.vue │ │ │ ├── pos.vue │ │ │ ├── property.vue │ │ │ ├── purchase-invoice.vue │ │ │ ├── purchase.vue │ │ │ ├── reports.vue │ │ │ ├── review-ac.vue │ │ │ ├── sales-invoice.vue │ │ │ ├── sales.vue │ │ │ ├── settings.vue │ │ │ ├── start.vue │ │ │ ├── supplier.vue │ │ │ └── system.vue │ │ ├── 24 │ │ │ ├── general.vue │ │ │ ├── green-check.vue │ │ │ ├── index.ts │ │ │ ├── invoice.vue │ │ │ ├── mail.vue │ │ │ ├── privacy.vue │ │ │ └── system.vue │ │ └── base.vue │ ├── Loading.vue │ ├── Modal.vue │ ├── MouseFollower.vue │ ├── POS │ │ ├── Classic │ │ │ ├── ItemsGrid.vue │ │ │ ├── ItemsTable.vue │ │ │ ├── SelectedItemRow.vue │ │ │ └── SelectedItemTable.vue │ │ ├── FloatingLabelCurrencyInput.vue │ │ ├── FloatingLabelFloatInput.vue │ │ ├── FloatingLabelInputBase.vue │ │ ├── Modern │ │ │ ├── ModernPOSItemsGrid.vue │ │ │ ├── ModernPOSItemsTable.vue │ │ │ ├── ModernPOSSelectedItemRow.vue │ │ │ └── ModernPOSSelectedItemTable.vue │ │ └── types.ts │ ├── PageHeader.vue │ ├── PageHeaderNavGroup.vue │ ├── Paginator.vue │ ├── Popover.vue │ ├── QuickView.vue │ ├── Report │ │ └── ListReport.vue │ ├── Row.vue │ ├── SearchBar.vue │ ├── ShortcutKeys.vue │ ├── ShortcutsHelper.vue │ ├── Sidebar.vue │ ├── StatusPill.vue │ ├── Toast.vue │ ├── Tooltip.vue │ ├── TwoColumnForm.vue │ ├── WindowsTitleBar.vue │ └── WithScroll.vue ├── errorHandling.ts ├── importer.ts ├── index.html ├── initFyo.ts ├── pages │ ├── ChartOfAccounts.vue │ ├── CommonForm │ │ ├── CommonForm.vue │ │ ├── CommonFormSection.vue │ │ ├── LinkedEntries.vue │ │ └── RowEditForm.vue │ ├── CustomizeForm │ │ └── CustomizeForm.vue │ ├── Dashboard │ │ ├── BaseDashboardChart.vue │ │ ├── Cashflow.vue │ │ ├── Dashboard.vue │ │ ├── Expenses.vue │ │ ├── PeriodSelector.vue │ │ ├── ProfitAndLoss.vue │ │ ├── SectionHeader.vue │ │ └── UnpaidInvoices.vue │ ├── DatabaseSelector.vue │ ├── Desk.vue │ ├── GetStarted.vue │ ├── ImportWizard.vue │ ├── ListView │ │ ├── List.vue │ │ ├── ListCell.vue │ │ └── ListView.vue │ ├── POS │ │ ├── AlertModal.vue │ │ ├── ClassicPOS.vue │ │ ├── ClosePOSShiftModal.vue │ │ ├── CouponCodeModal.vue │ │ ├── KeyboardModal.vue │ │ ├── LoyaltyProgramModal.vue │ │ ├── ModernPOS.vue │ │ ├── OpenPOSShiftModal.vue │ │ ├── POS.vue │ │ ├── POSQuickActions.vue │ │ ├── PaymentModal.vue │ │ ├── PriceListModal.vue │ │ ├── ReturnSalesInvoiceModal.vue │ │ └── SavedInvoiceModal.vue │ ├── PrintView │ │ ├── PrintView.vue │ │ └── ReportPrintView.vue │ ├── QuickEditForm.vue │ ├── Report.vue │ ├── Settings │ │ └── Settings.vue │ ├── SetupWizard │ │ └── SetupWizard.vue │ └── TemplateBuilder │ │ ├── PrintContainer.vue │ │ ├── ScaledContainer.vue │ │ ├── SetPrintSize.vue │ │ ├── SetType.vue │ │ ├── TemplateBuilder.vue │ │ ├── TemplateBuilderHint.vue │ │ └── TemplateEditor.vue ├── regional │ ├── in │ │ └── in.ts │ └── index.ts ├── renderer.ts ├── renderer │ ├── helpers.ts │ └── registerIpcRendererListeners.ts ├── router.ts ├── setup │ ├── createCOA.ts │ ├── setupInstance.ts │ ├── standardCOA.ts │ └── types.ts ├── shims-tsx.d.ts ├── shims-vue-custom.d.ts ├── shims-vue.d.ts ├── styles │ └── index.css └── utils │ ├── api.ts │ ├── chart.ts │ ├── colors.ts │ ├── db.ts │ ├── doc.ts │ ├── erpnextSync.ts │ ├── export.ts │ ├── filters.ts │ ├── getStartedConfig.ts │ ├── index.ts │ ├── initialization.ts │ ├── injectionKeys.ts │ ├── interactive.ts │ ├── language.ts │ ├── misc.ts │ ├── pos.ts │ ├── printTemplates.ts │ ├── refs.ts │ ├── search.ts │ ├── shortcuts.ts │ ├── sidebarConfig.ts │ ├── theme.ts │ ├── types.ts │ ├── ui.ts │ └── vueUtils.ts ├── tailwind.config.js ├── templates ├── Basic.template.html ├── Business-POS.template.html ├── Business.Payment.template.html ├── Business.Shipment.template.html ├── Business.template.html └── Minimal.template.html ├── tests ├── helpers.ts ├── items.csv ├── parties.csv ├── sales_invoices.csv ├── testImporter.spec.ts └── testSetupInstance.spec.ts ├── translations ├── ar.csv ├── ca-ES.csv ├── da.csv ├── de.csv ├── es.csv ├── fr.csv ├── gu.csv ├── hi.csv ├── id.csv ├── ko.csv ├── nl.csv ├── np.csv ├── pt.csv ├── sv.csv ├── tr.csv ├── zh-CN.csv └── zh-Hant.csv ├── tsconfig.json ├── uitest └── index.mjs ├── utils ├── auth │ └── types.ts ├── config.ts ├── csvParser.ts ├── db │ └── types.ts ├── defaults.ts ├── index.ts ├── ipc │ └── types.ts ├── messages.ts ├── misc.ts ├── translationHelpers.ts ├── types.ts └── version.ts ├── vite.config.ts └── yarn.lock /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Rename 'frappe' to 'fyo' outside src 2 | 32d282dc9c6f129807a1cf53eae47fc3602aa976 3 | 4 | # Format files using prettier 5 | 8c9d81d298dd08ae7acaf6de297aa30d95329778 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-general_question_template.yml: -------------------------------------------------------------------------------- 1 | name: 'General Question' 2 | description: Create a new ticket for a general question 3 | title: '🐛 [General Question] - ' 4 | labels: ['question'] 5 | 6 | body: 7 | - type: textarea 8 | id: summary 9 | attributes: 10 | label: 'Summary' 11 | description: General Question(s) (for Bugs and Feature Requests, please use the appropriate template) 12 | placeholder: '...' 13 | validations: 14 | required: true 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/.github/ISSUE_TEMPLATE/config.yml -------------------------------------------------------------------------------- /.github/frappe-books-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/.github/frappe-books-preview.png -------------------------------------------------------------------------------- /.github/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/.github/logo.png -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - '**.md' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | setup_and_build: 11 | runs-on: macos-13 12 | steps: 13 | - name: Setup node 14 | uses: actions/setup-node@v4 15 | with: 16 | node-version: '20.18.1' 17 | 18 | - name: Set yarn version 19 | run: yarn set version 1.22.18 20 | 21 | - name: Checkout Books 22 | uses: actions/checkout@v4 23 | 24 | - name: Install Dependencies 25 | run: yarn 26 | 27 | - name: Run build 28 | env: 29 | CSC_IDENTITY_AUTO_DISCOVERY: false 30 | APPLE_NOTARIZE: 0 31 | run: yarn build -mw --publish never 32 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: [$default-branch] 6 | paths-ignore: 7 | - '**.md' 8 | pull_request: 9 | paths-ignore: 10 | - '**.md' 11 | workflow_dispatch: 12 | 13 | jobs: 14 | setup_and_lint: 15 | runs-on: macos-latest 16 | steps: 17 | - name: Setup node 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: '20.18.1' 21 | 22 | - name: Set yarn version 23 | run: yarn set version 1.22.18 24 | 25 | - name: Checkout Books 26 | uses: actions/checkout@v4 27 | 28 | - name: Install Dependencies 29 | run: yarn 30 | 31 | - name: Lint 32 | run: yarn lint 33 | 34 | - name: Check Formatting 35 | run: yarn prettier --check . 36 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [$default-branch] 6 | paths-ignore: 7 | - '**.md' 8 | pull_request: 9 | paths-ignore: 10 | - '**.md' 11 | workflow_dispatch: 12 | 13 | jobs: 14 | setup_and_test: 15 | runs-on: macos-latest 16 | steps: 17 | - name: Setup node 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: '20.18.1' 21 | 22 | - name: Set yarn version 23 | run: yarn set version 1.22.18 24 | 25 | - name: Checkout Books 26 | uses: actions/checkout@v4 27 | 28 | - name: Install Dependencies 29 | run: yarn 30 | 31 | - name: Run Tests 32 | run: yarn test 33 | 34 | setup_and_uitest: 35 | runs-on: macos-latest 36 | steps: 37 | - name: Setup node 38 | uses: actions/setup-node@v4 39 | with: 40 | node-version: '20.18.1' 41 | 42 | - name: Set yarn version 43 | run: yarn set version 1.22.18 44 | 45 | - name: Checkout Books 46 | uses: actions/checkout@v4 47 | 48 | - name: Install Dependencies 49 | run: yarn 50 | 51 | - name: Build Source Files 52 | run: yarn build --nosign --nopackage 53 | 54 | - name: Run UI Tests 55 | run: yarn uitest 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dbs 4 | /dist 5 | /notebook 6 | 7 | # local env files 8 | .env 9 | .env.publish 10 | .env.local 11 | .env.*.local 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | 18 | # Editor directories and files 19 | .idea 20 | .vscode 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | *.code-workspace 27 | 28 | #Electron-builder output 29 | /dist_electron 30 | log_creds.txt -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/types.ts 2 | **/dist_electron 3 | **/dummy/*.json 4 | **/.github/ISSUE_TEMPLATE/*.yml 5 | **/patches/v0_21_0/* -------------------------------------------------------------------------------- /backend/patches/addUOMs.ts: -------------------------------------------------------------------------------- 1 | import { ModelNameEnum } from '../../models/types'; 2 | import { DatabaseManager } from '../database/manager'; 3 | import { getDefaultMetaFieldValueMap } from '../helpers'; 4 | 5 | const defaultUOMs = [ 6 | { 7 | name: `Unit`, 8 | isWhole: true, 9 | }, 10 | { 11 | name: `Kg`, 12 | isWhole: false, 13 | }, 14 | { 15 | name: `Gram`, 16 | isWhole: false, 17 | }, 18 | { 19 | name: `Meter`, 20 | isWhole: false, 21 | }, 22 | { 23 | name: `Hour`, 24 | isWhole: false, 25 | }, 26 | { 27 | name: `Day`, 28 | isWhole: false, 29 | }, 30 | ]; 31 | 32 | async function execute(dm: DatabaseManager) { 33 | for (const uom of defaultUOMs) { 34 | const defaults = getDefaultMetaFieldValueMap(); 35 | await dm.db?.insert(ModelNameEnum.UOM, { ...uom, ...defaults }); 36 | } 37 | } 38 | 39 | export default { execute }; 40 | -------------------------------------------------------------------------------- /backend/patches/createInventoryNumberSeries.ts: -------------------------------------------------------------------------------- 1 | import { getDefaultMetaFieldValueMap } from '../../backend/helpers'; 2 | import { DatabaseManager } from '../database/manager'; 3 | 4 | async function execute(dm: DatabaseManager) { 5 | const s = (await dm.db?.getAll('SingleValue', { 6 | fields: ['value'], 7 | filters: { fieldname: 'setupComplete' }, 8 | })) as { value: string }[]; 9 | 10 | if (!Number(s?.[0]?.value ?? '0')) { 11 | return; 12 | } 13 | 14 | const names: Record<string, string> = { 15 | StockMovement: 'SMOV-', 16 | PurchaseReceipt: 'PREC-', 17 | Shipment: 'SHPM-', 18 | }; 19 | 20 | for (const referenceType in names) { 21 | const name = names[referenceType]; 22 | await createNumberSeries(name, referenceType, dm); 23 | } 24 | } 25 | 26 | async function createNumberSeries( 27 | name: string, 28 | referenceType: string, 29 | dm: DatabaseManager 30 | ) { 31 | const exists = await dm.db?.exists('NumberSeries', name); 32 | if (exists) { 33 | return; 34 | } 35 | 36 | await dm.db?.insert('NumberSeries', { 37 | name, 38 | start: 1001, 39 | padZeros: 4, 40 | current: 0, 41 | referenceType, 42 | ...getDefaultMetaFieldValueMap(), 43 | }); 44 | } 45 | 46 | export default { execute, beforeMigrate: true }; 47 | -------------------------------------------------------------------------------- /backend/patches/fixItemHSNField.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseManager } from '../database/manager'; 2 | 3 | async function execute(dm: DatabaseManager) { 4 | const knexSchema = dm.db?.knex?.schema; 5 | 6 | await knexSchema?.alterTable('Item', (table) => { 7 | table.text('hsnCode').alter(); 8 | }); 9 | } 10 | 11 | export default { execute, beforeMigrate: true }; 12 | -------------------------------------------------------------------------------- /backend/patches/fixRoundOffAccount.ts: -------------------------------------------------------------------------------- 1 | import { ModelNameEnum } from '../../models/types'; 2 | import { DatabaseManager } from '../database/manager'; 3 | 4 | const FIELDNAME = 'roundOffAccount'; 5 | 6 | async function execute(dm: DatabaseManager) { 7 | const accounts = await dm.db!.getSingleValues(FIELDNAME); 8 | if (!accounts.length) { 9 | await testAndSetRoundOffAccount(dm); 10 | } 11 | 12 | await dm.db!.delete(ModelNameEnum.AccountingSettings, FIELDNAME); 13 | 14 | let isSet = false; 15 | for (const { parent, value } of accounts) { 16 | if (parent !== ModelNameEnum.AccountingSettings) { 17 | continue; 18 | } 19 | 20 | isSet = await setRoundOffAccountIfExists(value as string, dm); 21 | if (isSet) { 22 | break; 23 | } 24 | } 25 | 26 | if (!isSet) { 27 | await testAndSetRoundOffAccount(dm); 28 | } 29 | } 30 | 31 | async function testAndSetRoundOffAccount(dm: DatabaseManager) { 32 | const isSet = await setRoundOffAccountIfExists('Round Off', dm); 33 | if (!isSet) { 34 | await setRoundOffAccountIfExists('Rounded Off', dm); 35 | } 36 | 37 | return; 38 | } 39 | 40 | async function setRoundOffAccountIfExists( 41 | roundOffAccount: string, 42 | dm: DatabaseManager 43 | ) { 44 | const exists = await dm.db!.exists(ModelNameEnum.Account, roundOffAccount); 45 | if (!exists) { 46 | return false; 47 | } 48 | 49 | await dm.db!.insert(ModelNameEnum.AccountingSettings, { 50 | roundOffAccount, 51 | }); 52 | return true; 53 | } 54 | 55 | export default { execute, beforeMigrate: true }; 56 | -------------------------------------------------------------------------------- /backend/patches/index.ts: -------------------------------------------------------------------------------- 1 | import { Patch } from '../database/types'; 2 | import addUOMs from './addUOMs'; 3 | import createInventoryNumberSeries from './createInventoryNumberSeries'; 4 | import fixRoundOffAccount from './fixRoundOffAccount'; 5 | import testPatch from './testPatch'; 6 | import updateSchemas from './updateSchemas'; 7 | import setPaymentReferenceType from './setPaymentReferenceType'; 8 | import fixLedgerDateTime from './v0_21_0/fixLedgerDateTime'; 9 | import fixItemHSNField from './fixItemHSNField'; 10 | import createPaymentMethods from './createPaymentMethods'; 11 | 12 | export default [ 13 | { name: 'testPatch', version: '0.5.0-beta.0', patch: testPatch }, 14 | { 15 | name: 'updateSchemas', 16 | version: '0.5.0-beta.0', 17 | patch: updateSchemas, 18 | priority: 100, 19 | }, 20 | { 21 | name: 'addUOMs', 22 | version: '0.6.0-beta.0', 23 | patch: addUOMs, 24 | }, 25 | { 26 | name: 'fixRoundOffAccount', 27 | version: '0.6.3-beta.0', 28 | patch: fixRoundOffAccount, 29 | }, 30 | { 31 | name: 'createInventoryNumberSeries', 32 | version: '0.6.6-beta.0', 33 | patch: createInventoryNumberSeries, 34 | }, 35 | { 36 | name: 'setPaymentReferenceType', 37 | version: '0.20.1', 38 | patch: setPaymentReferenceType, 39 | }, 40 | { 41 | name: 'fixLedgerDateTime', 42 | version: '0.21.2', 43 | patch: fixLedgerDateTime, 44 | }, 45 | { name: 'fixItemHSNField', version: '0.24.0', patch: fixItemHSNField }, 46 | { 47 | name: 'createPaymentMethods', 48 | version: '0.25.1', 49 | patch: createPaymentMethods, 50 | }, 51 | ] as Patch[]; 52 | -------------------------------------------------------------------------------- /backend/patches/setPaymentReferenceType.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseManager } from '../database/manager'; 2 | 3 | async function execute(dm: DatabaseManager) { 4 | await dm.db!.knex!('Payment') 5 | .where({ referenceType: null, paymentType: 'Pay' }) 6 | .update({ referenceType: 'PurchaseInvoice' }); 7 | await dm.db!.knex!('Payment') 8 | .where({ referenceType: null, paymentType: 'Receive' }) 9 | .update({ referenceType: 'SalesInvoice' }); 10 | } 11 | 12 | export default { execute, beforeMigrate: true }; 13 | -------------------------------------------------------------------------------- /backend/patches/testPatch.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseManager } from '../database/manager'; 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 4 | async function execute(dm: DatabaseManager) { 5 | /** 6 | * Execute function will receive the DatabaseManager which is to be used 7 | * to apply database patches. 8 | */ 9 | } 10 | 11 | export default { execute, beforeMigrate: true }; 12 | -------------------------------------------------------------------------------- /backend/patches/v0_21_0/fixLedgerDateTime.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseManager } from '../../database/manager'; 2 | 3 | /* eslint-disable */ 4 | async function execute(dm: DatabaseManager) { 5 | 6 | const sourceTables = [ 7 | "PurchaseInvoice", 8 | "SalesInvoice", 9 | "JournalEntry", 10 | "Payment", 11 | "StockMovement", 12 | "StockTransfer" 13 | ]; 14 | 15 | await dm.db!.knex!('AccountingLedgerEntry') 16 | .select('name', 'date', 'referenceName') 17 | .then((trx: Array<{name: string; date: Date; referenceName: string;}> ) => { 18 | trx.forEach(async entry => { 19 | 20 | sourceTables.forEach(async table => { 21 | await dm.db!.knex! 22 | .select('name','date') 23 | .from(table) 24 | .where({ name: entry['referenceName'] }) 25 | .then(async (resp: Array<{name: string; date: Date;}>) => { 26 | if (resp.length !== 0) { 27 | 28 | const dateTimeValue = new Date(resp[0]['date']); 29 | await dm.db!.knex!('AccountingLedgerEntry') 30 | .where({ name: entry['name'] }) 31 | .update({ date: dateTimeValue.toISOString() }); 32 | } 33 | }) 34 | }); 35 | }); 36 | }); 37 | } 38 | 39 | export default { execute, beforeMigrate: true }; 40 | /* eslint-enable */ -------------------------------------------------------------------------------- /build/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 | <plist version="1.0"> 4 | <dict> 5 | <key>com.apple.security.cs.allow-jit</key> 6 | <true/> 7 | <key>com.apple.security.cs.allow-unsigned-executable-memory</key> 8 | <true/> 9 | <key>com.apple.security.cs.disable-library-validation</key> 10 | <true/> 11 | </dict> 12 | </plist> 13 | -------------------------------------------------------------------------------- /build/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/icon.icns -------------------------------------------------------------------------------- /build/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/icon.ico -------------------------------------------------------------------------------- /build/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/icon.png -------------------------------------------------------------------------------- /build/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/icons/128x128.png -------------------------------------------------------------------------------- /build/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/icons/16x16.png -------------------------------------------------------------------------------- /build/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/icons/256x256.png -------------------------------------------------------------------------------- /build/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/icons/32x32.png -------------------------------------------------------------------------------- /build/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/icons/48x48.png -------------------------------------------------------------------------------- /build/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/icons/512x512.png -------------------------------------------------------------------------------- /build/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/icons/64x64.png -------------------------------------------------------------------------------- /build/installerIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/installerIcon.ico -------------------------------------------------------------------------------- /build/uninstallerIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/build/uninstallerIcon.ico -------------------------------------------------------------------------------- /dummy/README.md: -------------------------------------------------------------------------------- 1 | # Dummy 2 | 3 | This will be used to generate dummy data for the purposes of tests and to create 4 | a demo instance. 5 | 6 | There are a few `.json` files here (eg: `items.json`) which have been generated, 7 | these are not to be edited. 8 | 9 | The generated data has some randomness, there is a `baseCount` arg to the 10 | exported `setupDummyInstance` function, the number of transactions generated are 11 | always more than this. 12 | -------------------------------------------------------------------------------- /dummy/tests/testDummy.spec.ts: -------------------------------------------------------------------------------- 1 | import { assertDoesNotThrow } from 'backend/database/tests/helpers'; 2 | import { purchaseItemPartyMap } from 'dummy/helpers'; 3 | import test from 'tape'; 4 | import { getTestDbPath, getTestFyo } from 'tests/helpers'; 5 | import { setupDummyInstance } from '..'; 6 | 7 | const dbPath = getTestDbPath(); 8 | const fyo = getTestFyo(); 9 | 10 | test('setupDummyInstance', async () => { 11 | await assertDoesNotThrow(async () => { 12 | await setupDummyInstance(dbPath, fyo, 1, 25); 13 | }, 'setup instance failed'); 14 | }); 15 | 16 | test('purchaseItemParty Existance', async (t) => { 17 | for (const item in purchaseItemPartyMap) { 18 | t.ok(await fyo.db.exists('Item', item), `item exists: ${item}`); 19 | 20 | const party = purchaseItemPartyMap[item]; 21 | t.ok(await fyo.db.exists('Party', party), `party exists: ${party}`); 22 | } 23 | }); 24 | 25 | test.onFinish(async () => { 26 | await fyo.close(); 27 | }); 28 | -------------------------------------------------------------------------------- /electron-builder.yml.disabled: -------------------------------------------------------------------------------- 1 | productName: Frappe Books 2 | appId: io.frappe.books 3 | asarUnpack: '**/*.node' 4 | extraResources: 5 | [ 6 | { from: 'log_creds.txt', to: '../creds/log_creds.txt' }, 7 | { from: 'translations', to: '../translations' }, 8 | { from: 'templates', to: '../templates' }, 9 | ] 10 | mac: 11 | type: distribution 12 | category: public.app-category.finance 13 | icon: build/icon.icns 14 | # notarize: 15 | # appBundleId: io.frappe.books 16 | hardenedRuntime: true 17 | gatekeeperAssess: false 18 | darkModeSupport: false 19 | entitlements: build/entitlements.mac.plist 20 | entitlementsInherit: build/entitlements.mac.plist 21 | publish: 22 | - github 23 | win: 24 | publisherName: Frappe Technologies Pvt. Ltd. 25 | signDlls: true 26 | icon: build/icon.ico 27 | publish: 28 | - github 29 | target: 30 | - portable 31 | - nsis 32 | nsis: 33 | oneClick: false 34 | perMachine: false 35 | allowToChangeInstallationDirectory: true 36 | installerIcon: build/installericon.ico 37 | uninstallerIcon: build/uninstallericon.ico 38 | publish: 39 | - github 40 | linux: 41 | icon: build/icons 42 | category: Finance 43 | publish: 44 | - github 45 | target: 46 | - deb 47 | - AppImage 48 | - rpm -------------------------------------------------------------------------------- /fyo/core/authHandler.ts: -------------------------------------------------------------------------------- 1 | import { Fyo } from 'fyo'; 2 | import { AuthDemux } from 'fyo/demux/auth'; 3 | import { AuthDemuxBase } from 'utils/auth/types'; 4 | import { Creds } from 'utils/types'; 5 | import { AuthDemuxConstructor } from './types'; 6 | 7 | interface AuthConfig { 8 | serverURL: string; 9 | backend: string; 10 | port: number; 11 | } 12 | 13 | interface Session { 14 | user: string; 15 | token: string; 16 | } 17 | 18 | export class AuthHandler { 19 | #config: AuthConfig; 20 | #session: Session; 21 | fyo: Fyo; 22 | #demux: AuthDemuxBase; 23 | #creds?: Creds; 24 | 25 | constructor(fyo: Fyo, Demux?: AuthDemuxConstructor) { 26 | this.fyo = fyo; 27 | this.#config = { 28 | serverURL: '', 29 | backend: 'sqlite', 30 | port: 8000, 31 | }; 32 | 33 | this.#session = { 34 | user: '', 35 | token: '', 36 | }; 37 | 38 | if (Demux !== undefined) { 39 | this.#demux = new Demux(fyo.isElectron); 40 | } else { 41 | this.#demux = new AuthDemux(fyo.isElectron); 42 | } 43 | } 44 | 45 | set user(value: string) { 46 | this.#session.user = value; 47 | } 48 | 49 | get user(): string { 50 | return this.#session.user; 51 | } 52 | 53 | get session(): Readonly<Session> { 54 | return { ...this.#session }; 55 | } 56 | 57 | get config(): Readonly<AuthConfig> { 58 | return { ...this.#config }; 59 | } 60 | 61 | init() { 62 | return null; 63 | } 64 | 65 | async getCreds(): Promise<Creds> { 66 | if (!this.#creds) { 67 | this.#creds = await this.#demux.getCreds(); 68 | } 69 | 70 | return this.#creds; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /fyo/core/types.ts: -------------------------------------------------------------------------------- 1 | import type { Doc } from 'fyo/model/doc'; 2 | import type { Money } from 'pesa'; 3 | import type { RawValue } from 'schemas/types'; 4 | import type { AuthDemuxBase } from 'utils/auth/types'; 5 | import type { DatabaseDemuxBase } from 'utils/db/types'; 6 | 7 | export type Attachment = { name: string; type: string; data: string }; 8 | export type DocValue = 9 | | string 10 | | number 11 | | boolean 12 | | Date 13 | | Money 14 | | null 15 | | Attachment 16 | | undefined; 17 | export type DocValueMap = Record<string, DocValue | Doc[] | DocValueMap[]>; 18 | export type RawValueMap = Record<string, RawValue | RawValueMap[]>; 19 | 20 | /** 21 | * DatabaseDemuxConstructor: type for a constructor that returns a DatabaseDemuxBase 22 | * it's typed this way because `typeof AbstractClass` is invalid as abstract classes 23 | * can't be initialized using `new`. 24 | * 25 | * AuthDemuxConstructor: same as the above but for AuthDemuxBase 26 | */ 27 | 28 | export type DatabaseDemuxConstructor = new ( 29 | isElectron?: boolean 30 | ) => DatabaseDemuxBase; 31 | 32 | export type AuthDemuxConstructor = new (isElectron?: boolean) => AuthDemuxBase; 33 | 34 | export type ConfigMap = { 35 | files: ConfigFile[]; 36 | lastSelectedFilePath: null | string; 37 | language: string 38 | deviceId: string 39 | }; 40 | 41 | export interface ConfigFile { 42 | id: string; 43 | companyName: string; 44 | dbPath: string; 45 | openCount: number; 46 | } 47 | 48 | export interface FyoConfig { 49 | DatabaseDemux?: DatabaseDemuxConstructor; 50 | AuthDemux?: AuthDemuxConstructor; 51 | isElectron?: boolean; 52 | isTest?: boolean; 53 | } 54 | -------------------------------------------------------------------------------- /fyo/demux/auth.ts: -------------------------------------------------------------------------------- 1 | import { AuthDemuxBase } from 'utils/auth/types'; 2 | import { Creds } from 'utils/types'; 3 | 4 | export class AuthDemux extends AuthDemuxBase { 5 | #isElectron = false; 6 | constructor(isElectron: boolean) { 7 | super(); 8 | this.#isElectron = isElectron; 9 | } 10 | 11 | async getCreds(): Promise<Creds> { 12 | if (this.#isElectron) { 13 | return await ipc.getCreds(); 14 | } else { 15 | return { errorLogUrl: '', tokenString: '', telemetryUrl: '' }; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /fyo/demux/config.ts: -------------------------------------------------------------------------------- 1 | import { ConfigMap } from 'fyo/core/types'; 2 | import type { IPC } from 'main/preload'; 3 | 4 | export class Config { 5 | config: Map<string, unknown> | IPC['store']; 6 | constructor(isElectron: boolean) { 7 | this.config = new Map(); 8 | if (isElectron) { 9 | this.config = ipc.store; 10 | } 11 | } 12 | 13 | get<K extends keyof ConfigMap>( 14 | key: K, 15 | defaultValue?: ConfigMap[K] 16 | ): ConfigMap[K] | undefined { 17 | const value = this.config.get(key) as ConfigMap[K] | undefined; 18 | return value ?? defaultValue; 19 | } 20 | 21 | set<K extends keyof ConfigMap>(key: K, value: ConfigMap[K]) { 22 | this.config.set(key, value); 23 | } 24 | 25 | delete(key: keyof ConfigMap) { 26 | this.config.delete(key); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /fyo/models/index.ts: -------------------------------------------------------------------------------- 1 | import { ModelMap } from 'fyo/model/types'; 2 | import NumberSeries from './NumberSeries'; 3 | import SystemSettings from './SystemSettings'; 4 | import { CustomField } from './CustomField'; 5 | import { CustomForm } from './CustomForm'; 6 | 7 | export const coreModels = { 8 | NumberSeries, 9 | SystemSettings, 10 | CustomForm, 11 | CustomField, 12 | } as ModelMap; 13 | -------------------------------------------------------------------------------- /fyo/telemetry/types.ts: -------------------------------------------------------------------------------- 1 | export type AppVersion = string; 2 | export type UniqueId = string; 3 | export type Timestamp = string; 4 | 5 | export enum Verb { 6 | Started = 'started', 7 | Completed = 'completed', 8 | Created = 'created', 9 | Deleted = 'deleted', 10 | Submitted = 'submitted', 11 | Cancelled = 'cancelled', 12 | Imported = 'imported', 13 | Exported = 'exported', 14 | Printed = 'printed', 15 | Closed = 'closed', 16 | Opened = 'opened', 17 | Resumed = 'resumed', 18 | } 19 | 20 | export enum ErrorLogEnum { 21 | IntegrationErrorLog = 'IntegrationErrorLog', 22 | } 23 | 24 | export type Noun = string; 25 | 26 | export interface Telemetry { 27 | deviceId: UniqueId; 28 | instanceId: UniqueId; 29 | platform?: string; 30 | country: string; 31 | language: string; 32 | version: AppVersion; 33 | timestamp: Timestamp; 34 | openCount: number; 35 | verb: Verb; 36 | noun: Noun; 37 | more?: Record<string, unknown>; 38 | } 39 | -------------------------------------------------------------------------------- /fyo/tests/helpers.ts: -------------------------------------------------------------------------------- 1 | import { AuthDemuxBase } from 'utils/auth/types'; 2 | import { Creds } from 'utils/types'; 3 | 4 | export class DummyAuthDemux extends AuthDemuxBase { 5 | // eslint-disable-next-line @typescript-eslint/require-await 6 | async getCreds(): Promise<Creds> { 7 | return { errorLogUrl: '', tokenString: '', telemetryUrl: '' }; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /fyo/tests/testFyo.spec.ts: -------------------------------------------------------------------------------- 1 | import { getRegionalModels, models } from 'models'; 2 | import { getSchemas } from 'schemas'; 3 | import test from 'tape'; 4 | import { getTestFyo } from 'tests/helpers'; 5 | 6 | test('Fyo Init', async (t) => { 7 | const fyo = getTestFyo(); 8 | t.equal(Object.keys(fyo.schemaMap).length, 0, 'zero schemas'); 9 | 10 | await fyo.db.createNewDatabase(':memory:', 'in'); 11 | await fyo.initializeAndRegister({}, {}); 12 | 13 | t.equal(Object.keys(fyo.schemaMap).length > 0, true, 'non zero schemas'); 14 | await fyo.close(); 15 | }); 16 | 17 | test('Fyo Docs', async (t) => { 18 | const countryCode = 'in'; 19 | const fyo = getTestFyo(); 20 | const schemaMap = getSchemas(countryCode, []); 21 | const regionalModels = await getRegionalModels(countryCode); 22 | await fyo.db.createNewDatabase(':memory:', countryCode); 23 | await fyo.initializeAndRegister(models, regionalModels); 24 | 25 | for (const schemaName in schemaMap) { 26 | const schema = schemaMap[schemaName]; 27 | if (schema?.isSingle) { 28 | continue; 29 | } 30 | 31 | const doc = fyo.doc.getNewDoc(schemaName); 32 | t.equal(doc.schemaName, schemaName, `equal schemaNames: ${schemaName}`); 33 | } 34 | 35 | await fyo.close(); 36 | }); 37 | -------------------------------------------------------------------------------- /fyo/utils/cacheManager.ts: -------------------------------------------------------------------------------- 1 | export default class CacheManager { 2 | _keyValueCache: Map<string, unknown | undefined>; 3 | _hashCache: Map<string, Map<string, unknown> | undefined>; 4 | 5 | constructor() { 6 | this._keyValueCache = new Map(); 7 | this._hashCache = new Map(); 8 | } 9 | 10 | // Regular Cache Ops 11 | getValue(key: string) { 12 | return this._keyValueCache.get(key); 13 | } 14 | 15 | setValue(key: string, value: unknown) { 16 | this._keyValueCache.set(key, value); 17 | } 18 | 19 | clearValue(key: string) { 20 | this._keyValueCache.delete(key); 21 | } 22 | 23 | // Hash Cache Ops 24 | hget(hashName: string, key: string) { 25 | const hc = this._hashCache.get(hashName); 26 | if (hc === undefined) { 27 | return hc; 28 | } 29 | return hc.get(key); 30 | } 31 | 32 | hset(hashName: string, key: string, value: unknown) { 33 | const hc = this._hashCache.get(hashName); 34 | if (hc === undefined) { 35 | this._hashCache.set(hashName, new Map()); 36 | } 37 | 38 | this._hashCache.get(hashName)!.set(key, value); 39 | } 40 | 41 | hclear(hashName: string, key?: string) { 42 | if (key) { 43 | this._hashCache.get(hashName)?.delete(key); 44 | } else { 45 | this._hashCache.get(hashName)?.clear(); 46 | } 47 | } 48 | 49 | hexists(hashName: string) { 50 | return this._hashCache.get(hashName) !== undefined; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fyo/utils/consts.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_INTERNAL_PRECISION = 11; 2 | export const DEFAULT_DISPLAY_PRECISION = 2; 3 | export const DEFAULT_DATE_FORMAT = 'MMM d, y'; 4 | export const DEFAULT_LOCALE = 'en-IN'; 5 | export const DEFAULT_COUNTRY_CODE = 'in'; 6 | export const DEFAULT_CURRENCY = 'INR'; 7 | export const DEFAULT_LANGUAGE = 'English'; 8 | export const DEFAULT_SERIES_START = 1001; 9 | export const DEFAULT_USER = 'Admin'; 10 | export const RTL_LANGUAGES = [ 11 | 'Arabic', 12 | 'Aramaic', 13 | 'Azeri', 14 | 'Dhivehi', 15 | 'Maldivian', 16 | 'Hebrew', 17 | 'Kurdish', 18 | 'Sorani', 19 | 'Persian', 20 | 'Farsi', 21 | 'Urdu', 22 | ]; 23 | -------------------------------------------------------------------------------- /fyo/utils/types.ts: -------------------------------------------------------------------------------- 1 | export interface ErrorLog { 2 | name: string; 3 | message: string; 4 | stack?: string; 5 | more?: Record<string, unknown>; 6 | } 7 | -------------------------------------------------------------------------------- /jobs/triggerErpNextSync.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | const { parentPort } = require('worker_threads'); 3 | 4 | if (parentPort) { 5 | // eslint-disable-next-line 6 | parentPort.postMessage({ type: 'trigger-erpnext-sync' }); 7 | } 8 | -------------------------------------------------------------------------------- /main/api.ts: -------------------------------------------------------------------------------- 1 | import fetch, { RequestInit } from 'node-fetch'; 2 | 3 | export async function sendAPIRequest( 4 | endpoint: string, 5 | options: RequestInit | undefined 6 | ) { 7 | return (await fetch(endpoint, options)).json() as unknown as { 8 | [key: string]: string | number | boolean; 9 | }[]; 10 | } 11 | -------------------------------------------------------------------------------- /main/getPrintTemplates.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs/promises'; 2 | import path from 'path'; 3 | import { TemplateFile } from 'utils/types'; 4 | 5 | export async function getTemplates(posTemplateWidth?: number) { 6 | const paths = await getPrintTemplatePaths(); 7 | if (!paths) { 8 | return []; 9 | } 10 | 11 | const templates: TemplateFile[] = []; 12 | for (const file of paths.files) { 13 | const filePath = path.join(paths.root, file); 14 | const template = await fs.readFile(filePath, 'utf-8'); 15 | const { mtime } = await fs.stat(filePath); 16 | const width = 17 | file?.split('-')[1]?.split('.')[0] === 'POS' ? posTemplateWidth ?? 0 : 0; 18 | const height = file?.split('-')[1]?.split('.')[0] === 'POS' ? 22 : 0; 19 | 20 | templates.push({ 21 | template, 22 | file, 23 | modified: mtime.toISOString(), 24 | width, 25 | height, 26 | }); 27 | } 28 | 29 | return templates; 30 | } 31 | 32 | async function getPrintTemplatePaths(): Promise<{ 33 | files: string[]; 34 | root: string; 35 | } | null> { 36 | let root = path.join(process.resourcesPath, `../templates`); 37 | 38 | try { 39 | const files = await fs.readdir(root); 40 | return { files, root }; 41 | } catch { 42 | root = path.join(__dirname, '..', '..', `templates`); 43 | } 44 | 45 | try { 46 | const files = await fs.readdir(root); 47 | return { files, root }; 48 | } catch { 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /main/initSheduler.ts: -------------------------------------------------------------------------------- 1 | import Bree from 'bree'; 2 | import path from 'path'; 3 | import main from 'main'; 4 | 5 | let bree: Bree; 6 | 7 | export async function initScheduler(interval: string) { 8 | const jobsRoot = path.join(__dirname, '..', '..', 'jobs'); 9 | 10 | if (bree) { 11 | await bree.stop(); 12 | } 13 | 14 | bree = new Bree({ 15 | root: jobsRoot, 16 | defaultExtension: 'ts', 17 | jobs: [ 18 | { 19 | name: 'triggerErpNextSync', 20 | interval: interval, 21 | worker: { 22 | workerData: { 23 | useTsNode: true, 24 | }, 25 | }, 26 | }, 27 | ], 28 | worker: { 29 | argv: ['--require', 'ts-node/register'], 30 | }, 31 | }); 32 | 33 | bree.on('worker created', () => { 34 | main.mainWindow?.webContents.send('trigger-erpnext-sync'); 35 | }); 36 | 37 | await bree.start(); 38 | } 39 | -------------------------------------------------------------------------------- /main/printHtmlDocument.ts: -------------------------------------------------------------------------------- 1 | import { App } from 'electron'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import { getInitializedPrintWindow } from './saveHtmlAsPdf'; 5 | 6 | export async function printHtmlDocument( 7 | html: string, 8 | app: App, 9 | width: number, 10 | height: number 11 | ): Promise<boolean> { 12 | const tempRoot = app.getPath('temp'); 13 | const tempFile = path.join(tempRoot, `temp-print.html`); 14 | await fs.writeFile(tempFile, html, { encoding: 'utf-8' }); 15 | 16 | const printWindow = await getInitializedPrintWindow(tempFile, width, height); 17 | 18 | const success = await new Promise<boolean>((resolve) => { 19 | printWindow.webContents.print( 20 | { silent: false, printBackground: true }, 21 | (success) => resolve(success) 22 | ); 23 | }); 24 | 25 | printWindow.close(); 26 | await fs.unlink(tempFile); 27 | return success; 28 | } 29 | -------------------------------------------------------------------------------- /main/registerAppLifecycleListeners.ts: -------------------------------------------------------------------------------- 1 | import { app } from 'electron'; 2 | import installExtension, { VUEJS3_DEVTOOLS } from 'electron-devtools-installer'; 3 | import { Main } from '../main'; 4 | import { rendererLog } from './helpers'; 5 | import { emitMainProcessError } from 'backend/helpers'; 6 | 7 | export default function registerAppLifecycleListeners(main: Main) { 8 | app.on('window-all-closed', () => { 9 | if (process.platform !== 'darwin') { 10 | app.quit(); 11 | } 12 | }); 13 | 14 | app.on('activate', () => { 15 | if (main.mainWindow === null) { 16 | main.createWindow().catch((err) => emitMainProcessError(err)); 17 | } 18 | }); 19 | 20 | app.on('ready', () => { 21 | if (main.isDevelopment && !main.isTest) { 22 | installDevTools(main).catch((err) => emitMainProcessError(err)); 23 | } 24 | 25 | main.createWindow().catch((err) => emitMainProcessError(err)); 26 | }); 27 | } 28 | 29 | async function installDevTools(main: Main) { 30 | try { 31 | await installExtension(VUEJS3_DEVTOOLS); 32 | } catch (e) { 33 | rendererLog(main, 'Vue Devtools failed to install', e); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main/registerProcessListeners.ts: -------------------------------------------------------------------------------- 1 | import { app } from 'electron'; 2 | import { CUSTOM_EVENTS, IPC_CHANNELS } from 'utils/messages'; 3 | import { Main } from '../main'; 4 | 5 | export default function registerProcessListeners(main: Main) { 6 | if (main.isDevelopment) { 7 | if (process.platform === 'win32') { 8 | process.on('message', (data) => { 9 | if (data === 'graceful-exit') { 10 | app.quit(); 11 | } 12 | }); 13 | } else { 14 | process.on('SIGTERM', () => { 15 | app.quit(); 16 | }); 17 | } 18 | } 19 | 20 | process.on(CUSTOM_EVENTS.MAIN_PROCESS_ERROR, (error, more) => { 21 | main.mainWindow!.webContents.send( 22 | IPC_CHANNELS.LOG_MAIN_PROCESS_ERROR, 23 | error, 24 | more 25 | ); 26 | }); 27 | 28 | process.on('unhandledRejection', (error) => { 29 | main.mainWindow!.webContents.send( 30 | IPC_CHANNELS.LOG_MAIN_PROCESS_ERROR, 31 | error 32 | ); 33 | }); 34 | 35 | process.on('uncaughtException', (error) => { 36 | main.mainWindow!.webContents.send( 37 | IPC_CHANNELS.LOG_MAIN_PROCESS_ERROR, 38 | error 39 | ); 40 | setTimeout(() => process.exit(1), 10000); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /models/Transactional/types.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { Money } from 'pesa'; 3 | 4 | export interface LedgerPostingOptions { 5 | reference: Doc; 6 | party?: string; 7 | } 8 | 9 | export interface LedgerEntry { 10 | account: string; 11 | party: string; 12 | date: string; 13 | referenceType: string; 14 | referenceName: string; 15 | reverted: boolean; 16 | debit: Money; 17 | credit: Money; 18 | } 19 | 20 | export type TransactionType = 'credit' | 'debit'; 21 | -------------------------------------------------------------------------------- /models/baseModels/Account/types.ts: -------------------------------------------------------------------------------- 1 | export enum AccountRootTypeEnum { 2 | 'Asset'='Asset', 3 | 'Liability'='Liability', 4 | 'Equity'='Equity', 5 | 'Income'='Income', 6 | 'Expense'='Expense', 7 | } 8 | 9 | export enum AccountTypeEnum { 10 | 'Accumulated Depreciation' = 'Accumulated Depreciation', 11 | 'Bank' = 'Bank', 12 | 'Cash' = 'Cash', 13 | 'Chargeable' = 'Chargeable', 14 | 'Cost of Goods Sold' = 'Cost of Goods Sold', 15 | 'Depreciation' = 'Depreciation', 16 | 'Equity' = 'Equity', 17 | 'Expense Account' = 'Expense Account', 18 | 'Expenses Included In Valuation' = 'Expenses Included In Valuation', 19 | 'Fixed Asset' = 'Fixed Asset', 20 | 'Income Account' = 'Income Account', 21 | 'Payable' = 'Payable', 22 | 'Receivable' = 'Receivable', 23 | 'Round Off' = 'Round Off', 24 | 'Stock' = 'Stock', 25 | 'Stock Adjustment' = 'Stock Adjustment', 26 | 'Stock Received But Not Billed' = 'Stock Received But Not Billed', 27 | 'Tax' = 'Tax', 28 | 'Temporary' = 'Temporary', 29 | } 30 | 31 | export type AccountType = keyof typeof AccountTypeEnum; 32 | export type AccountRootType = keyof typeof AccountRootTypeEnum 33 | 34 | export interface COARootAccount { 35 | rootType: AccountRootType; 36 | [key: string]: COAChildAccount | AccountRootType; 37 | } 38 | 39 | export interface COAChildAccount { 40 | accountType?: AccountType; 41 | accountNumber?: string; 42 | isGroup?: boolean; 43 | [key: string]: COAChildAccount | boolean | AccountType | string | undefined; 44 | } 45 | 46 | export interface COATree { 47 | [key: string]: COARootAccount; 48 | } 49 | -------------------------------------------------------------------------------- /models/baseModels/AccountingLedgerEntry/AccountingLedgerEntry.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { ListViewSettings } from 'fyo/model/types'; 3 | import { ModelNameEnum } from 'models/types'; 4 | import { Money } from 'pesa'; 5 | 6 | export class AccountingLedgerEntry extends Doc { 7 | date?: string | Date; 8 | account?: string; 9 | party?: string; 10 | debit?: Money; 11 | credit?: Money; 12 | referenceType?: string; 13 | referenceName?: string; 14 | reverted?: boolean; 15 | 16 | async revert() { 17 | if (this.reverted) { 18 | return; 19 | } 20 | 21 | await this.set('reverted', true); 22 | const revertedEntry = this.fyo.doc.getNewDoc( 23 | ModelNameEnum.AccountingLedgerEntry, 24 | { 25 | account: this.account, 26 | party: this.party, 27 | date: new Date(), 28 | referenceType: this.referenceType, 29 | referenceName: this.referenceName, 30 | debit: this.credit, 31 | credit: this.debit, 32 | reverted: true, 33 | reverts: this.name, 34 | } 35 | ); 36 | 37 | await this.sync(); 38 | await revertedEntry.sync(); 39 | } 40 | 41 | static getListViewSettings(): ListViewSettings { 42 | return { 43 | columns: ['date', 'account', 'party', 'debit', 'credit', 'referenceName'], 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /models/baseModels/AppliedCouponCodes/AppliedCouponCodes.ts: -------------------------------------------------------------------------------- 1 | import { DocValue } from 'fyo/core/types'; 2 | import { ValidationMap } from 'fyo/model/types'; 3 | import { InvoiceItem } from '../InvoiceItem/InvoiceItem'; 4 | import { validateCouponCode } from 'models/helpers'; 5 | 6 | export class AppliedCouponCodes extends InvoiceItem { 7 | coupons?: string; 8 | 9 | validations: ValidationMap = { 10 | coupons: async (value: DocValue) => { 11 | if (!value) { 12 | return; 13 | } 14 | 15 | await validateCouponCode(this as AppliedCouponCodes, value as string); 16 | }, 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /models/baseModels/CollectionRulesItems/CollectionRulesItems.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { Money } from 'pesa'; 3 | 4 | export class CollectionRulesItems extends Doc { 5 | tierName?: string; 6 | collectionFactor?: number; 7 | minimumTotalSpent?: Money; 8 | } 9 | -------------------------------------------------------------------------------- /models/baseModels/ERPNextSyncQueue/ERPNextSyncQueue.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { HiddenMap, ListViewSettings } from 'fyo/model/types'; 3 | 4 | export class ERPNextSyncQueue extends Doc { 5 | referenceType?: string; 6 | documentName?: string; 7 | 8 | hidden: HiddenMap = { 9 | name: () => true, 10 | }; 11 | 12 | static getListViewSettings(): ListViewSettings { 13 | return { 14 | columns: ['referenceType', 'documentName'], 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /models/baseModels/ERPNextSyncSettings/ERPNextSyncSettings.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { ChangeArg, HiddenMap } from 'fyo/model/types'; 3 | import { initERPNSync } from 'src/utils/erpnextSync'; 4 | 5 | export class ERPNextSyncSettings extends Doc { 6 | deviceID?: string; 7 | instanceName?: string; 8 | baseURL?: string; 9 | authToken?: string; 10 | integrationAppVersion?: string; 11 | isEnabled?: boolean; 12 | 13 | dataSyncInterval?: string; 14 | syncDataFromServer?: boolean; 15 | 16 | registerInstance?: string; 17 | syncSettings?: string; 18 | syncDataToERPNext?: string; 19 | fetchFromERPNextQueue?: string; 20 | clearSyncedDocsFromErpNextSyncQueue?: string; 21 | 22 | hidden: HiddenMap = { 23 | syncPriceList: () => { 24 | return !this.fyo.singles.AccountingSettings?.enablePriceList; 25 | }, 26 | priceListSyncType: () => { 27 | return !this.fyo.singles.AccountingSettings?.enablePriceList; 28 | }, 29 | syncSerialNumber: () => { 30 | return !this.fyo.singles.InventorySettings?.enableSerialNumber; 31 | }, 32 | serialNumberSyncType: () => { 33 | return !this.fyo.singles.InventorySettings?.enableSerialNumber; 34 | }, 35 | syncBatch: () => { 36 | return !this.fyo.singles.InventorySettings?.enableBatches; 37 | }, 38 | batchSyncType: () => { 39 | return !this.fyo.singles.InventorySettings?.enableBatches; 40 | }, 41 | }; 42 | 43 | async change(ch: ChangeArg) { 44 | if (ch.changed === 'syncDataFromServer') { 45 | await initERPNSync(this.fyo); 46 | ipc.reloadWindow(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /models/baseModels/FetchFromERPNextQueue/FetchFromERPNextQueue.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { HiddenMap, ListViewSettings } from 'fyo/model/types'; 3 | 4 | export class FetchFromERPNextQueue extends Doc { 5 | referenceType?: string; 6 | documentName?: string; 7 | 8 | hidden: HiddenMap = { 9 | name: () => true, 10 | }; 11 | 12 | static getListViewSettings(): ListViewSettings { 13 | return { 14 | columns: ['referenceType', 'documentName'], 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /models/baseModels/IntegrationErrorLog/IntegrationErrorLog.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | 3 | export class IntegrationErrorLog extends Doc {} 4 | -------------------------------------------------------------------------------- /models/baseModels/Invoice/types.ts: -------------------------------------------------------------------------------- 1 | import { PricingRule } from '../PricingRule/PricingRule'; 2 | 3 | export interface ApplicablePricingRules { 4 | applyOnItem: string; 5 | pricingRule: PricingRule; 6 | } 7 | 8 | export interface ApplicableCouponCodes { 9 | pricingRule: string; 10 | coupon: string; 11 | } 12 | -------------------------------------------------------------------------------- /models/baseModels/JournalEntryAccount/JournalEntryAccount.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { FiltersMap, FormulaMap } from 'fyo/model/types'; 3 | import { Money } from 'pesa'; 4 | 5 | export class JournalEntryAccount extends Doc { 6 | getAutoDebitCredit(type: 'debit' | 'credit') { 7 | const currentValue = this.get(type) as Money; 8 | if (!currentValue.isZero()) { 9 | return; 10 | } 11 | 12 | const otherType = type === 'debit' ? 'credit' : 'debit'; 13 | const otherTypeValue = this.get(otherType) as Money; 14 | if (!otherTypeValue.isZero()) { 15 | return this.fyo.pesa(0); 16 | } 17 | 18 | const totalType = this.parentdoc!.getSum('accounts', type, false) as Money; 19 | const totalOtherType = this.parentdoc!.getSum( 20 | 'accounts', 21 | otherType, 22 | false 23 | ) as Money; 24 | 25 | if (totalType.lt(totalOtherType)) { 26 | return totalOtherType.sub(totalType); 27 | } 28 | } 29 | 30 | formulas: FormulaMap = { 31 | debit: { 32 | formula: () => this.getAutoDebitCredit('debit'), 33 | }, 34 | credit: { 35 | formula: () => this.getAutoDebitCredit('credit'), 36 | }, 37 | }; 38 | 39 | static filters: FiltersMap = { 40 | account: () => ({ isGroup: false }), 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /models/baseModels/Lead/Lead.ts: -------------------------------------------------------------------------------- 1 | import { Fyo } from 'fyo'; 2 | import { Doc } from 'fyo/model/doc'; 3 | import { 4 | Action, 5 | LeadStatus, 6 | ListViewSettings, 7 | ValidationMap, 8 | } from 'fyo/model/types'; 9 | import { getLeadActions, getLeadStatusColumn } from 'models/helpers'; 10 | import { 11 | validateEmail, 12 | validatePhoneNumber, 13 | } from 'fyo/model/validationFunction'; 14 | import { ModelNameEnum } from 'models/types'; 15 | 16 | export class Lead extends Doc { 17 | status?: LeadStatus; 18 | 19 | validations: ValidationMap = { 20 | email: validateEmail, 21 | mobile: validatePhoneNumber, 22 | }; 23 | 24 | createCustomer() { 25 | return this.fyo.doc.getNewDoc(ModelNameEnum.Party, { 26 | ...this.getValidDict(), 27 | fromLead: this.name, 28 | phone: this.mobile as string, 29 | role: 'Customer', 30 | }); 31 | } 32 | 33 | createSalesQuote() { 34 | const data: { party: string | undefined; referenceType: string } = { 35 | party: this.name, 36 | referenceType: ModelNameEnum.Lead, 37 | }; 38 | 39 | return this.fyo.doc.getNewDoc(ModelNameEnum.SalesQuote, data); 40 | } 41 | 42 | static getActions(fyo: Fyo): Action[] { 43 | return getLeadActions(fyo); 44 | } 45 | 46 | static getListViewSettings(): ListViewSettings { 47 | return { 48 | columns: ['name', getLeadStatusColumn(), 'email', 'mobile'], 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /models/baseModels/LoyaltyPointEntry/LoyaltyPointEntry.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { ListViewSettings } from 'fyo/model/types'; 3 | 4 | export class LoyaltyPointEntry extends Doc { 5 | loyaltyProgram?: string; 6 | customer?: string; 7 | invoice?: string; 8 | purchaseAmount?: number; 9 | expiryDate?: Date; 10 | 11 | static override getListViewSettings(): ListViewSettings { 12 | return { 13 | columns: [ 14 | 'loyaltyProgram', 15 | 'customer', 16 | 'purchaseAmount', 17 | 'loyaltyPoints', 18 | ], 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /models/baseModels/LoyaltyProgram/LoyaltyProgram.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { FiltersMap, ListViewSettings } from 'fyo/model/types'; 3 | import { CollectionRulesItems } from '../CollectionRulesItems/CollectionRulesItems'; 4 | import { AccountRootTypeEnum } from '../Account/types'; 5 | 6 | export class LoyaltyProgram extends Doc { 7 | collectionRules?: CollectionRulesItems[]; 8 | expiryDuration?: number; 9 | 10 | static filters: FiltersMap = { 11 | expenseAccount: () => ({ 12 | rootType: AccountRootTypeEnum.Expense, 13 | isGroup: false, 14 | }), 15 | }; 16 | 17 | static getListViewSettings(): ListViewSettings { 18 | return { 19 | columns: ['name', 'fromDate', 'toDate', 'expiryDuration'], 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /models/baseModels/Misc.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { HiddenMap } from 'fyo/model/types'; 3 | 4 | export class Misc extends Doc { 5 | openCount?: number; 6 | useFullWidth?: boolean; 7 | override hidden: HiddenMap = {}; 8 | } 9 | -------------------------------------------------------------------------------- /models/baseModels/Party/types.ts: -------------------------------------------------------------------------------- 1 | export type PartyRole = 'Both' | 'Supplier' | 'Customer'; 2 | export enum PartyRoleEnum { 3 | 'Both' = 'Both', 4 | 'Supplier' = 'Supplier', 5 | 'Customer' = 'Customer', 6 | } 7 | -------------------------------------------------------------------------------- /models/baseModels/Payment/types.ts: -------------------------------------------------------------------------------- 1 | export type PaymentType = 'Receive' | 'Pay'; 2 | export type PaymentMethod = 'Cash' | 'Cheque' | 'Transfer'; 3 | 4 | export enum PaymentTypeEnum{ 5 | Receive = 'Receive', 6 | Pay = 'Pay' 7 | } 8 | 9 | export enum AccountFieldEnum{ 10 | Account = 'account', 11 | PaymentAccount = 'paymentAccount' 12 | } 13 | -------------------------------------------------------------------------------- /models/baseModels/PaymentMethod/PaymentMethod.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { Account } from '../Account/Account'; 3 | import { ListViewSettings } from 'fyo/model/types'; 4 | import { PaymentMethodType } from 'models/types'; 5 | 6 | export class PaymentMethod extends Doc { 7 | name?: string; 8 | account?: Account; 9 | type?: PaymentMethodType; 10 | 11 | static getListViewSettings(): ListViewSettings { 12 | return { 13 | columns: ['name', 'type'], 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /models/baseModels/PriceList/PriceList.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { ListViewSettings } from 'fyo/model/types'; 3 | import { PriceListItem } from './PriceListItem'; 4 | import { 5 | getIsDocEnabledColumn, 6 | getPriceListStatusColumn, 7 | } from 'models/helpers'; 8 | 9 | export class PriceList extends Doc { 10 | isEnabled?: boolean; 11 | isSales?: boolean; 12 | isPurchase?: boolean; 13 | priceListItem?: PriceListItem[]; 14 | 15 | static getListViewSettings(): ListViewSettings { 16 | return { 17 | columns: ['name', getIsDocEnabledColumn(), getPriceListStatusColumn()], 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /models/baseModels/PriceList/PriceListItem.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import type { FormulaMap } from 'fyo/model/types'; 3 | import { ModelNameEnum } from 'models/types'; 4 | import type { Money } from 'pesa'; 5 | import type { PriceList } from './PriceList'; 6 | 7 | export class PriceListItem extends Doc { 8 | item?: string; 9 | unit?: string; 10 | rate?: Money; 11 | parentdoc?: PriceList; 12 | 13 | formulas: FormulaMap = { 14 | unit: { 15 | formula: async () => { 16 | if (!this.item) { 17 | return; 18 | } 19 | 20 | return await this.fyo.getValue(ModelNameEnum.Item, this.item, 'unit'); 21 | }, 22 | dependsOn: ['item'], 23 | }, 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /models/baseModels/PricingRuleDetail/PricingRuleDetail.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | 3 | export class PricingRuleDetail extends Doc { 4 | referenceName?: string; 5 | referenceItem?: string; 6 | } 7 | -------------------------------------------------------------------------------- /models/baseModels/PricingRuleItem/PricingRuleItem.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { FormulaMap } from 'fyo/model/types'; 3 | import { ModelNameEnum } from 'models/types'; 4 | 5 | export class PricingRuleItem extends Doc { 6 | item?: string; 7 | unit?: string; 8 | 9 | formulas: FormulaMap = { 10 | unit: { 11 | formula: () => { 12 | if (!this.item) { 13 | return; 14 | } 15 | return this.fyo.getValue(ModelNameEnum.Item, this.item, 'unit'); 16 | }, 17 | }, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /models/baseModels/PrintSettings/PrintSettings.ts: -------------------------------------------------------------------------------- 1 | import { Attachment } from 'fyo/core/types'; 2 | import { Doc } from 'fyo/model/doc'; 3 | import { HiddenMap } from 'fyo/model/types'; 4 | 5 | export class PrintSettings extends Doc { 6 | logo?: Attachment; 7 | email?: string; 8 | phone?: string; 9 | address?: string; 10 | companyName?: string; 11 | color?: string; 12 | font?: string; 13 | displayLogo?: boolean; 14 | displayTime?: boolean; 15 | posPrintWidth?: number; 16 | amountInWords?: boolean; 17 | override hidden: HiddenMap = {}; 18 | } 19 | -------------------------------------------------------------------------------- /models/baseModels/PurchaseInvoiceItem/PurchaseInvoiceItem.ts: -------------------------------------------------------------------------------- 1 | import { InvoiceItem } from '../InvoiceItem/InvoiceItem'; 2 | 3 | export class PurchaseInvoiceItem extends InvoiceItem {} 4 | -------------------------------------------------------------------------------- /models/baseModels/SalesInvoiceItem/SalesInvoiceItem.ts: -------------------------------------------------------------------------------- 1 | import { InvoiceItem } from '../InvoiceItem/InvoiceItem'; 2 | 3 | export class SalesInvoiceItem extends InvoiceItem {} 4 | -------------------------------------------------------------------------------- /models/baseModels/SalesQuoteItem/SalesQuoteItem.ts: -------------------------------------------------------------------------------- 1 | import { InvoiceItem } from '../InvoiceItem/InvoiceItem'; 2 | 3 | export class SalesQuoteItem extends InvoiceItem {} 4 | -------------------------------------------------------------------------------- /models/baseModels/Tax/Tax.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { ListViewSettings } from 'fyo/model/types'; 3 | 4 | export class Tax extends Doc { 5 | static getListViewSettings(): ListViewSettings { 6 | return { columns: ['name'] }; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /models/baseModels/TaxSummary/TaxSummary.ts: -------------------------------------------------------------------------------- 1 | import { Fyo } from 'fyo'; 2 | import { DocValueMap } from 'fyo/core/types'; 3 | import { Doc } from 'fyo/model/doc'; 4 | import { CurrenciesMap } from 'fyo/model/types'; 5 | import { DEFAULT_CURRENCY } from 'fyo/utils/consts'; 6 | import { Money } from 'pesa'; 7 | import { FieldTypeEnum, Schema } from 'schemas/types'; 8 | import { Invoice } from '../Invoice/Invoice'; 9 | 10 | export class TaxSummary extends Doc { 11 | account?: string; 12 | from_account?: string; 13 | rate?: number; 14 | amount?: Money; 15 | parentdoc?: Invoice; 16 | 17 | get exchangeRate() { 18 | return this.parentdoc?.exchangeRate ?? 1; 19 | } 20 | 21 | get currency() { 22 | return this.parentdoc?.currency ?? DEFAULT_CURRENCY; 23 | } 24 | 25 | constructor(schema: Schema, data: DocValueMap, fyo: Fyo) { 26 | super(schema, data, fyo); 27 | this._setGetCurrencies(); 28 | } 29 | 30 | getCurrencies: CurrenciesMap = {}; 31 | _getCurrency() { 32 | if (this.exchangeRate === 1) { 33 | return this.fyo.singles.SystemSettings?.currency ?? DEFAULT_CURRENCY; 34 | } 35 | 36 | return this.currency; 37 | } 38 | _setGetCurrencies() { 39 | const currencyFields = this.schema.fields.filter( 40 | ({ fieldtype }) => fieldtype === FieldTypeEnum.Currency 41 | ); 42 | 43 | const getCurrency = this._getCurrency.bind(this); 44 | 45 | for (const { fieldname } of currencyFields) { 46 | this.getCurrencies[fieldname] ??= getCurrency; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /models/exchangeRate.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/models/exchangeRate.ts -------------------------------------------------------------------------------- /models/inventory/Batch.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { ListViewSettings } from 'fyo/model/types'; 3 | 4 | export class Batch extends Doc { 5 | static getListViewSettings(): ListViewSettings { 6 | return { 7 | columns: ['name', 'expiryDate', 'manufactureDate'], 8 | }; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /models/inventory/Location.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | 3 | export class Location extends Doc { 4 | item?: string; 5 | } 6 | -------------------------------------------------------------------------------- /models/inventory/Point of Sale/CashDenominations.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { Money } from 'pesa'; 3 | 4 | export abstract class CashDenominations extends Doc { 5 | denomination?: Money; 6 | } 7 | -------------------------------------------------------------------------------- /models/inventory/Point of Sale/ClosingAmounts.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { FormulaMap } from 'fyo/model/types'; 3 | import { Money } from 'pesa'; 4 | 5 | export class ClosingAmounts extends Doc { 6 | closingAmount?: Money; 7 | differenceAmount?: Money; 8 | expectedAmount?: Money; 9 | openingAmount?: Money; 10 | paymentMethod?: string; 11 | 12 | formulas: FormulaMap = { 13 | differenceAmount: { 14 | formula: () => { 15 | if (!this.closingAmount) { 16 | return this.fyo.pesa(0); 17 | } 18 | 19 | if (!this.expectedAmount) { 20 | return this.fyo.pesa(0); 21 | } 22 | 23 | return this.closingAmount.sub(this.expectedAmount); 24 | }, 25 | }, 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /models/inventory/Point of Sale/ClosingCash.ts: -------------------------------------------------------------------------------- 1 | import { CashDenominations } from './CashDenominations'; 2 | 3 | export class ClosingCash extends CashDenominations { 4 | count?: number; 5 | } 6 | -------------------------------------------------------------------------------- /models/inventory/Point of Sale/DefaultCashDenominations.ts: -------------------------------------------------------------------------------- 1 | import { CashDenominations } from './CashDenominations'; 2 | 3 | export class DefaultCashDenominations extends CashDenominations {} 4 | -------------------------------------------------------------------------------- /models/inventory/Point of Sale/OpeningAmounts.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { Money } from 'pesa'; 3 | 4 | export class OpeningAmounts extends Doc { 5 | amount?: Money; 6 | paymentMethod?: 'Cash' | 'Transfer'; 7 | 8 | get openingCashAmount() { 9 | return this.parentdoc?.openingCashAmount as Money; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /models/inventory/Point of Sale/OpeningCash.ts: -------------------------------------------------------------------------------- 1 | import { CashDenominations } from './CashDenominations'; 2 | 3 | export class OpeningCash extends CashDenominations { 4 | count?: number; 5 | } 6 | -------------------------------------------------------------------------------- /models/inventory/Point of Sale/POSClosingShift.ts: -------------------------------------------------------------------------------- 1 | import { ListViewSettings } from 'fyo/model/types'; 2 | import { ClosingAmounts } from './ClosingAmounts'; 3 | import { ClosingCash } from './ClosingCash'; 4 | import { Doc } from 'fyo/model/doc'; 5 | 6 | export class POSClosingShift extends Doc { 7 | closingAmounts?: ClosingAmounts[]; 8 | closingCash?: ClosingCash[]; 9 | closingDate?: Date; 10 | openingShift?: string; 11 | 12 | get closingCashAmount() { 13 | if (!this.closingCash) { 14 | return this.fyo.pesa(0); 15 | } 16 | 17 | let closingAmount = this.fyo.pesa(0); 18 | 19 | this.closingCash.map((row: ClosingCash) => { 20 | const denomination = row.denomination ?? this.fyo.pesa(0); 21 | const count = row.count ?? 0; 22 | 23 | const amount = denomination.mul(count); 24 | closingAmount = closingAmount.add(amount); 25 | }); 26 | return closingAmount; 27 | } 28 | 29 | static getListViewSettings(): ListViewSettings { 30 | return { 31 | columns: ['name', 'closingDate'], 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /models/inventory/Point of Sale/POSOpeningShift.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { OpeningAmounts } from './OpeningAmounts'; 3 | import { OpeningCash } from './OpeningCash'; 4 | import { ListViewSettings } from 'fyo/model/types'; 5 | 6 | export class POSOpeningShift extends Doc { 7 | openingAmounts?: OpeningAmounts[]; 8 | openingCash?: OpeningCash[]; 9 | openingDate?: Date; 10 | 11 | get openingCashAmount() { 12 | if (!this.openingCash) { 13 | return this.fyo.pesa(0); 14 | } 15 | 16 | let openingAmount = this.fyo.pesa(0); 17 | 18 | this.openingCash.map((row: OpeningCash) => { 19 | const denomination = row.denomination ?? this.fyo.pesa(0); 20 | const count = row.count ?? 0; 21 | 22 | const amount = denomination.mul(count); 23 | openingAmount = openingAmount.add(amount); 24 | }); 25 | return openingAmount; 26 | } 27 | 28 | get openingTransferAmount() { 29 | if (!this.openingAmounts) { 30 | return this.fyo.pesa(0); 31 | } 32 | 33 | const transferAmountRow = this.openingAmounts.filter( 34 | (row) => row.paymentMethod === 'Transfer' 35 | )[0]; 36 | 37 | return transferAmountRow.amount ?? this.fyo.pesa(0); 38 | } 39 | 40 | static getListViewSettings(): ListViewSettings { 41 | return { 42 | columns: ['name', 'openingDate'], 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /models/inventory/PurchaseReceipt.ts: -------------------------------------------------------------------------------- 1 | import { Action, ListViewSettings } from 'fyo/model/types'; 2 | import { 3 | getStockTransferActions, 4 | getTransactionStatusColumn, 5 | } from 'models/helpers'; 6 | import { PurchaseReceiptItem } from './PurchaseReceiptItem'; 7 | import { StockTransfer } from './StockTransfer'; 8 | import { Fyo } from 'fyo'; 9 | import { ModelNameEnum } from 'models/types'; 10 | 11 | export class PurchaseReceipt extends StockTransfer { 12 | items?: PurchaseReceiptItem[]; 13 | 14 | static getListViewSettings(): ListViewSettings { 15 | return { 16 | columns: [ 17 | 'name', 18 | getTransactionStatusColumn(), 19 | 'party', 20 | 'date', 21 | 'grandTotal', 22 | ], 23 | }; 24 | } 25 | 26 | static getActions(fyo: Fyo): Action[] { 27 | return getStockTransferActions(fyo, ModelNameEnum.PurchaseReceipt); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /models/inventory/PurchaseReceiptItem.ts: -------------------------------------------------------------------------------- 1 | import { StockTransferItem } from './StockTransferItem'; 2 | 3 | export class PurchaseReceiptItem extends StockTransferItem {} 4 | -------------------------------------------------------------------------------- /models/inventory/SerialNumber.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { ListViewSettings } from 'fyo/model/types'; 3 | import { getSerialNumberStatusColumn } from 'models/helpers'; 4 | import { SerialNumberStatus } from './types'; 5 | 6 | export class SerialNumber extends Doc { 7 | name?: string; 8 | item?: string; 9 | description?: string; 10 | status?: SerialNumberStatus; 11 | 12 | static getListViewSettings(): ListViewSettings { 13 | return { 14 | columns: [ 15 | 'name', 16 | getSerialNumberStatusColumn(), 17 | 'item', 18 | 'description', 19 | 'party', 20 | ], 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /models/inventory/Shipment.ts: -------------------------------------------------------------------------------- 1 | import { Fyo } from 'fyo'; 2 | import { Action, ListViewSettings } from 'fyo/model/types'; 3 | import { 4 | getStockTransferActions, 5 | getTransactionStatusColumn, 6 | } from 'models/helpers'; 7 | import { ModelNameEnum } from 'models/types'; 8 | import { ShipmentItem } from './ShipmentItem'; 9 | import { StockTransfer } from './StockTransfer'; 10 | 11 | export class Shipment extends StockTransfer { 12 | items?: ShipmentItem[]; 13 | 14 | static getListViewSettings(): ListViewSettings { 15 | return { 16 | columns: [ 17 | 'name', 18 | getTransactionStatusColumn(), 19 | 'party', 20 | 'date', 21 | 'grandTotal', 22 | ], 23 | }; 24 | } 25 | 26 | static getActions(fyo: Fyo): Action[] { 27 | return getStockTransferActions(fyo, ModelNameEnum.Shipment); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /models/inventory/ShipmentItem.ts: -------------------------------------------------------------------------------- 1 | import { StockTransferItem } from './StockTransferItem'; 2 | 3 | export class ShipmentItem extends StockTransferItem {} 4 | -------------------------------------------------------------------------------- /models/inventory/StockLedgerEntry.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import { ListViewSettings } from 'fyo/model/types'; 3 | import { Money } from 'pesa'; 4 | 5 | export class StockLedgerEntry extends Doc { 6 | date?: Date; 7 | item?: string; 8 | rate?: Money; 9 | quantity?: number; 10 | location?: string; 11 | referenceName?: string; 12 | referenceType?: string; 13 | batch?: string; 14 | serialNumber?: string; 15 | 16 | static override getListViewSettings(): ListViewSettings { 17 | return { 18 | columns: [ 19 | 'date', 20 | 'item', 21 | 'location', 22 | 'rate', 23 | 'quantity', 24 | 'referenceName', 25 | ], 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /models/inventory/TransferItem.ts: -------------------------------------------------------------------------------- 1 | import { Doc } from 'fyo/model/doc'; 2 | import type { Transfer } from './Transfer'; 3 | import type { Money } from 'pesa'; 4 | 5 | export class TransferItem extends Doc { 6 | item?: string; 7 | 8 | unit?: string; 9 | transferUnit?: string; 10 | quantity?: number; 11 | transferQuantity?: number; 12 | unitConversionFactor?: number; 13 | 14 | rate?: Money; 15 | amount?: Money; 16 | 17 | batch?: string; 18 | serialNumber?: string; 19 | 20 | parentdoc?: Transfer; 21 | } 22 | -------------------------------------------------------------------------------- /models/inventory/types.ts: -------------------------------------------------------------------------------- 1 | import { Money } from 'pesa'; 2 | 3 | export enum ValuationMethod { 4 | 'FIFO' = 'FIFO', 5 | 'MovingAverage' = 'MovingAverage', 6 | } 7 | 8 | export enum MovementTypeEnum { 9 | 'MaterialIssue' = 'MaterialIssue', 10 | 'MaterialReceipt' = 'MaterialReceipt', 11 | 'MaterialTransfer' = 'MaterialTransfer', 12 | 'Manufacture' = 'Manufacture', 13 | } 14 | 15 | export type MovementType = 16 | | 'MaterialIssue' 17 | | 'MaterialReceipt' 18 | | 'MaterialTransfer' 19 | | 'Manufacture'; 20 | 21 | export type SerialNumberStatus = 22 | | 'Inactive' 23 | | 'Active' 24 | | 'Delivered'; 25 | 26 | export interface SMDetails { 27 | date: Date; 28 | referenceName: string; 29 | referenceType: string; 30 | } 31 | 32 | export interface SMTransferDetails { 33 | item: string; 34 | rate: Money; 35 | quantity: number; 36 | batch?: string; 37 | serialNumber?: string; 38 | fromLocation?: string; 39 | toLocation?: string; 40 | isReturn?: boolean; 41 | } 42 | 43 | export interface ReturnBalanceItemQty { 44 | item?: string; 45 | quantity: number; 46 | batch?: string | undefined; 47 | serialNumber?: string; 48 | } 49 | 50 | export interface DocItem { 51 | item: string; 52 | quantity: number; 53 | batch?: string | undefined; 54 | serialNumber?: string; 55 | } 56 | 57 | export interface ReturnDocItem { 58 | quantity: number; 59 | batches?: Record<string, { quantity: number, serialNumbers?: string[] }> | undefined; 60 | serialNumbers?: string[] | undefined; 61 | } 62 | 63 | export interface SMIDetails extends SMDetails, SMTransferDetails {} 64 | -------------------------------------------------------------------------------- /models/regionalModels/in/Address.ts: -------------------------------------------------------------------------------- 1 | import { FormulaMap, ListsMap } from 'fyo/model/types'; 2 | import { Address as BaseAddress } from 'models/baseModels/Address/Address'; 3 | import { codeStateMap } from 'regional/in'; 4 | 5 | export class Address extends BaseAddress { 6 | formulas: FormulaMap = { 7 | addressDisplay: { 8 | formula: () => { 9 | return [ 10 | this.addressLine1, 11 | this.addressLine2, 12 | this.city, 13 | this.state, 14 | this.country, 15 | this.postalCode, 16 | ] 17 | .filter(Boolean) 18 | .join(', '); 19 | }, 20 | dependsOn: [ 21 | 'addressLine1', 22 | 'addressLine2', 23 | 'city', 24 | 'state', 25 | 'country', 26 | 'postalCode', 27 | ], 28 | }, 29 | 30 | pos: { 31 | formula: () => { 32 | const stateList = Object.values(codeStateMap).sort(); 33 | const state = this.state as string; 34 | if (stateList.includes(state)) { 35 | return state; 36 | } 37 | return ''; 38 | }, 39 | dependsOn: ['state'], 40 | }, 41 | }; 42 | 43 | static lists: ListsMap = { 44 | ...BaseAddress.lists, 45 | pos: () => { 46 | return Object.values(codeStateMap).sort(); 47 | }, 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /models/regionalModels/in/Party.ts: -------------------------------------------------------------------------------- 1 | import { HiddenMap } from 'fyo/model/types'; 2 | import { Party as BaseParty } from 'models/baseModels/Party/Party'; 3 | import { GSTType } from './types'; 4 | import { PartyRole } from 'models/baseModels/Party/types'; 5 | 6 | export class Party extends BaseParty { 7 | gstin?: string; 8 | role?: PartyRole; 9 | gstType?: GSTType; 10 | loyaltyProgram?: string; 11 | 12 | // eslint-disable-next-line @typescript-eslint/require-await 13 | async beforeSync() { 14 | const gstin = this.get('gstin') as string | undefined; 15 | const gstType = this.get('gstType') as GSTType; 16 | 17 | if (gstin && gstType !== 'Registered Regular') { 18 | this.gstin = ''; 19 | } 20 | } 21 | 22 | hidden: HiddenMap = { 23 | gstin: () => (this.gstType as GSTType) !== 'Registered Regular', 24 | loyaltyProgram: () => { 25 | if (!this.fyo.singles.AccountingSettings?.enableLoyaltyProgram) { 26 | return true; 27 | } 28 | return this.role === 'Supplier'; 29 | }, 30 | loyaltyPoints: () => !this.loyaltyProgram || this.role === 'Supplier', 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /models/regionalModels/in/types.ts: -------------------------------------------------------------------------------- 1 | export type GSTType = 'Unregistered' | 'Registered Regular' | 'Consumer'; 2 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require('tailwindcss'), require('autoprefixer')], 3 | }; 4 | -------------------------------------------------------------------------------- /regional/in.ts: -------------------------------------------------------------------------------- 1 | export const codeStateMap = { 2 | '01': 'Jammu and Kashmir', 3 | '02': 'Himachal Pradesh', 4 | '03': 'Punjab', 5 | '04': 'Chandigarh', 6 | '05': 'Uttarakhand', 7 | '06': 'Haryana', 8 | '07': 'Delhi', 9 | '08': 'Rajasthan', 10 | '09': 'Uttar Pradesh', 11 | '10': 'Bihar', 12 | '11': 'Sikkim', 13 | '12': 'Arunachal Pradesh', 14 | '13': 'Nagaland', 15 | '14': 'Manipur', 16 | '15': 'Mizoram', 17 | '16': 'Tripura', 18 | '17': 'Meghalaya', 19 | '18': 'Assam', 20 | '19': 'West Bengal', 21 | '20': 'Jharkhand', 22 | '21': 'Odisha', 23 | '22': 'Chattisgarh', 24 | '23': 'Madhya Pradesh', 25 | '24': 'Gujarat', 26 | '26': 'Dadra and Nagar Haveli and Daman and Diu', 27 | '27': 'Maharashtra', 28 | '29': 'Karnataka', 29 | '30': 'Goa', 30 | '31': 'Lakshadweep', 31 | '32': 'Kerala', 32 | '33': 'Tamil Nadu', 33 | '34': 'Puducherry', 34 | '35': 'Andaman and Nicobar Islands', 35 | '36': 'Telangana', 36 | '37': 'Andhra Pradesh', 37 | '38': 'Ladakh', 38 | } as Record<string, string>; 39 | -------------------------------------------------------------------------------- /reports/GoodsAndServiceTax/GSTR1.ts: -------------------------------------------------------------------------------- 1 | import { BaseGSTR } from './BaseGSTR'; 2 | import { GSTRType } from './types'; 3 | 4 | export class GSTR1 extends BaseGSTR { 5 | static title = 'GSTR1'; 6 | static reportName = 'gstr-1'; 7 | 8 | gstrType: GSTRType = 'GSTR-1'; 9 | } 10 | -------------------------------------------------------------------------------- /reports/GoodsAndServiceTax/GSTR2.ts: -------------------------------------------------------------------------------- 1 | import { BaseGSTR } from './BaseGSTR'; 2 | import { GSTRType } from './types'; 3 | 4 | export class GSTR2 extends BaseGSTR { 5 | static title = 'GSTR2'; 6 | static reportName = 'gstr-2'; 7 | 8 | gstrType: GSTRType = 'GSTR-2'; 9 | } 10 | -------------------------------------------------------------------------------- /reports/GoodsAndServiceTax/types.ts: -------------------------------------------------------------------------------- 1 | export enum TransferTypeEnum { 2 | 'B2B' = 'B2B', 3 | 'B2CL' = 'B2CL', 4 | 'B2CS' = 'B2CS', 5 | 'NR' = 'NR', 6 | } 7 | 8 | export type TransferType = keyof typeof TransferTypeEnum; 9 | export type GSTRType = 'GSTR-1' | 'GSTR-2'; 10 | export interface GSTRRow { 11 | gstin: string; 12 | partyName: string; 13 | invNo: string; 14 | invDate: Date; 15 | rate: number; 16 | reverseCharge: 'Y' | 'N'; 17 | inState: boolean; 18 | place: string; 19 | invAmt: number; 20 | taxVal: number; 21 | igstAmt?: number; 22 | cgstAmt?: number; 23 | sgstAmt?: number; 24 | exempt?: boolean; 25 | nonGST?: boolean; 26 | nilRated?: boolean; 27 | } 28 | -------------------------------------------------------------------------------- /reports/README.md: -------------------------------------------------------------------------------- 1 | # Reports 2 | 3 | Reports are a view of stored data, the code here doesn't alter any data. 4 | 5 | All reports should extend the `Report` class in `reports/Report.ts`, depending 6 | on the report it may have custom `.vue` files. Check the `type.ts` file for the 7 | shape of the report data. 8 | -------------------------------------------------------------------------------- /reports/index.ts: -------------------------------------------------------------------------------- 1 | import { BalanceSheet } from './BalanceSheet/BalanceSheet'; 2 | import { GeneralLedger } from './GeneralLedger/GeneralLedger'; 3 | import { GSTR1 } from './GoodsAndServiceTax/GSTR1'; 4 | import { GSTR2 } from './GoodsAndServiceTax/GSTR2'; 5 | import { ProfitAndLoss } from './ProfitAndLoss/ProfitAndLoss'; 6 | import { TrialBalance } from './TrialBalance/TrialBalance'; 7 | import { StockBalance } from './inventory/StockBalance'; 8 | import { StockLedger } from './inventory/StockLedger'; 9 | 10 | export const reports = { 11 | GeneralLedger, 12 | ProfitAndLoss, 13 | BalanceSheet, 14 | TrialBalance, 15 | GSTR1, 16 | GSTR2, 17 | StockLedger, 18 | StockBalance, 19 | } as const; 20 | -------------------------------------------------------------------------------- /reports/inventory/types.ts: -------------------------------------------------------------------------------- 1 | import { ModelNameEnum } from "models/types"; 2 | 3 | export interface RawStockLedgerEntry { 4 | name: string; 5 | date: string; 6 | item: string; 7 | rate: string; 8 | batch: string | null; 9 | serialNumber: string | null; 10 | quantity: number; 11 | location: string; 12 | referenceName: string; 13 | referenceType: string; 14 | [key: string]: unknown; 15 | } 16 | 17 | 18 | export interface ComputedStockLedgerEntry{ 19 | name: number; 20 | date: Date; 21 | 22 | item: string; 23 | location:string; 24 | batch: string; 25 | serialNumber: string; 26 | 27 | quantity: number; 28 | balanceQuantity: number; 29 | 30 | incomingRate: number; 31 | valuationRate:number; 32 | 33 | balanceValue:number; 34 | valueChange:number; 35 | 36 | referenceName: string; 37 | referenceType: string; 38 | } 39 | 40 | 41 | export interface StockBalanceEntry{ 42 | name: number; 43 | 44 | item: string; 45 | location:string; 46 | batch: string; 47 | 48 | balanceQuantity: number; 49 | balanceValue: number; 50 | 51 | openingQuantity: number; 52 | openingValue:number; 53 | 54 | incomingQuantity:number; 55 | incomingValue:number; 56 | 57 | outgoingQuantity:number; 58 | outgoingValue:number; 59 | 60 | valuationRate:number; 61 | } 62 | 63 | export type ReferenceType = 64 | | ModelNameEnum.StockMovement 65 | | ModelNameEnum.Shipment 66 | | ModelNameEnum.PurchaseReceipt 67 | | 'All'; 68 | -------------------------------------------------------------------------------- /schemas/app/AppliedCouponCodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AppliedCouponCodes", 3 | "label": "Applied Coupon Codes", 4 | "isChild": true, 5 | "fields": [ 6 | { 7 | "fieldname": "coupons", 8 | "label": "Coupons", 9 | "fieldtype": "Link", 10 | "target": "CouponCode" 11 | } 12 | ], 13 | "tableFields": ["coupons"], 14 | "quickEditFields": ["coupons"] 15 | } 16 | -------------------------------------------------------------------------------- /schemas/app/Batch.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Batch", 3 | "label": "Batch", 4 | "naming": "manual", 5 | "fields": [ 6 | { 7 | "fieldname": "name", 8 | "fieldtype": "Data", 9 | "label": "Batch", 10 | "required": true 11 | }, 12 | { 13 | "fieldname": "item", 14 | "fieldtype": "Link", 15 | "target": "Item", 16 | "create": true, 17 | "label": "Item" 18 | }, 19 | { 20 | "fieldname": "expiryDate", 21 | "fieldtype": "Date", 22 | "label": "Expiry Date", 23 | "required": false 24 | }, 25 | { 26 | "fieldname": "manufactureDate", 27 | "fieldtype": "Date", 28 | "label": "Manufacture Date", 29 | "required": false 30 | } 31 | ], 32 | "quickEditFields": ["expiryDate", "manufactureDate"], 33 | "keywordFields": ["name"] 34 | } 35 | -------------------------------------------------------------------------------- /schemas/app/CollectionRulesItems.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CollectionRulesItems", 3 | "label": "Collection Rules", 4 | "isChild": true, 5 | "fields": [ 6 | { 7 | "fieldname": "tierName", 8 | "label": "Tier Name", 9 | "fieldtype": "Data" 10 | }, 11 | { 12 | "fieldname": "collectionFactor", 13 | "label": "Collection Factor (=1 LP)", 14 | "fieldtype": "Float" 15 | }, 16 | { 17 | "fieldname": "minimumTotalSpent", 18 | "label": "Minimum Total Spent", 19 | "fieldtype": "Currency" 20 | } 21 | ], 22 | "tableFields": ["tierName", "collectionFactor", "minimumTotalSpent"] 23 | } 24 | -------------------------------------------------------------------------------- /schemas/app/Color.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Color", 3 | "label": "Color", 4 | "naming": "manual", 5 | "fields": [ 6 | { 7 | "fieldname": "name", 8 | "fieldtype": "Data", 9 | "label": "Color", 10 | "required": true 11 | }, 12 | { 13 | "fieldname": "hexvalue", 14 | "fieldtype": "Data", 15 | "label": "Hex Value", 16 | "required": true 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /schemas/app/Currency.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Currency", 3 | "label": "Currency", 4 | "naming": "manual", 5 | "isSingle": false, 6 | "fields": [ 7 | { 8 | "fieldname": "name", 9 | "label": "Currency Name", 10 | "fieldtype": "Data", 11 | "required": true 12 | }, 13 | { 14 | "fieldname": "fraction", 15 | "label": "Fraction", 16 | "fieldtype": "Data" 17 | }, 18 | { 19 | "fieldname": "fractionUnits", 20 | "label": "Fraction Units", 21 | "fieldtype": "Int" 22 | }, 23 | { 24 | "label": "Smallest Currency Fraction Value", 25 | "fieldname": "smallestValue", 26 | "fieldtype": "Currency" 27 | }, 28 | { 29 | "label": "Symbol", 30 | "fieldname": "symbol", 31 | "fieldtype": "Data" 32 | } 33 | ], 34 | "quickEditFields": ["symbol"] 35 | } 36 | -------------------------------------------------------------------------------- /schemas/app/IntegrationErrorLog.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IntegrationErrorLog", 3 | "label": "Integration Error Log", 4 | "naming": "autoincrement", 5 | 6 | "fields": [ 7 | { 8 | "fieldname": "name", 9 | "label": "Entry No.", 10 | "fieldtype": "Data", 11 | "required": true 12 | }, 13 | { 14 | "fieldname": "data", 15 | "label": "Data", 16 | "readonly": true, 17 | "fieldtype": "Text", 18 | "section": "Default" 19 | }, 20 | { 21 | "fieldname": "error", 22 | "label": "Error", 23 | "readonly": true, 24 | "fieldtype": "Text", 25 | "section": "Default" 26 | } 27 | ], 28 | "keywordFields": ["name"] 29 | } 30 | -------------------------------------------------------------------------------- /schemas/app/JournalEntryAccount.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JournalEntryAccount", 3 | "label": "Journal Entry Account", 4 | "isChild": true, 5 | "fields": [ 6 | { 7 | "fieldname": "account", 8 | "label": "Account", 9 | "placeholder": "Account", 10 | "fieldtype": "Link", 11 | "target": "Account", 12 | "required": true, 13 | "groupBy": "rootType" 14 | }, 15 | { 16 | "fieldname": "debit", 17 | "label": "Debit", 18 | "fieldtype": "Currency" 19 | }, 20 | { 21 | "fieldname": "credit", 22 | "label": "Credit", 23 | "fieldtype": "Currency" 24 | } 25 | ], 26 | "tableFields": ["account", "debit", "credit"], 27 | "keywordFields": ["account"] 28 | } 29 | -------------------------------------------------------------------------------- /schemas/app/Misc.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Misc", 3 | "label": "Misc", 4 | "isSingle": true, 5 | "isChild": false, 6 | "fields": [ 7 | { 8 | "fieldname": "openCount", 9 | "label": "Open Count", 10 | "fieldtype": "Int" 11 | }, 12 | { 13 | "fieldname": "useFullWidth", 14 | "label": "Use Full Width", 15 | "fieldtype": "Check", 16 | "default": false 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /schemas/app/PaymentFor.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PaymentFor", 3 | "label": "Payment For", 4 | "isSingle": false, 5 | "isChild": true, 6 | "fields": [ 7 | { 8 | "fieldname": "referenceType", 9 | "label": "Type", 10 | "placeholder": "Type", 11 | "fieldtype": "Select", 12 | "options": [ 13 | { 14 | "value": "SalesInvoice", 15 | "label": "Sales" 16 | }, 17 | { 18 | "value": "PurchaseInvoice", 19 | "label": "Purchase" 20 | } 21 | ], 22 | "required": true 23 | }, 24 | { 25 | "fieldname": "referenceName", 26 | "label": "Name", 27 | "fieldtype": "DynamicLink", 28 | "references": "referenceType", 29 | "placeholder": "Name", 30 | "required": true 31 | }, 32 | { 33 | "fieldname": "amount", 34 | "label": "Amount", 35 | "fieldtype": "Currency", 36 | "required": true 37 | } 38 | ], 39 | "tableFields": ["referenceType", "referenceName", "amount"], 40 | "keywordFields": ["referenceName", "referenceType"] 41 | } 42 | -------------------------------------------------------------------------------- /schemas/app/PaymentMethod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PaymentMethod", 3 | "label": "Payment Method", 4 | "naming": "manual", 5 | "fields": [ 6 | { 7 | "fieldname": "name", 8 | "label": "Name", 9 | "fieldtype": "Data" 10 | }, 11 | { 12 | "fieldname": "type", 13 | "label": "Type", 14 | "fieldtype": "Select", 15 | "required": true, 16 | "options": [ 17 | { 18 | "value": "Cash", 19 | "label": "Cash" 20 | }, 21 | { 22 | "value": "Bank", 23 | "label": "Bank" 24 | }, 25 | { 26 | "value": "Transfer", 27 | "label": "Transfer" 28 | } 29 | ] 30 | }, 31 | { 32 | "fieldname": "account", 33 | "label": "Account", 34 | "fieldtype": "Link", 35 | "target": "Account" 36 | } 37 | ], 38 | "quickEditFields": ["name", "type", "account"] 39 | } 40 | -------------------------------------------------------------------------------- /schemas/app/PriceList.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PriceList", 3 | "label": "Price List", 4 | "naming": "manual", 5 | "fields": [ 6 | { 7 | "fieldname": "name", 8 | "label": "Name", 9 | "fieldtype": "Data", 10 | "required": true 11 | }, 12 | { 13 | "fieldname": "isEnabled", 14 | "label": "Is Price List Enabled", 15 | "fieldtype": "Check", 16 | "default": true 17 | }, 18 | { 19 | "fieldname": "isSales", 20 | "label": "For Sales", 21 | "fieldtype": "Check", 22 | "default": true 23 | }, 24 | { 25 | "fieldname": "isPurchase", 26 | "label": "For Purchase", 27 | "fieldtype": "Check", 28 | "default": false 29 | }, 30 | { 31 | "fieldname": "priceListItem", 32 | "label": "Item Prices", 33 | "fieldtype": "Table", 34 | "target": "PriceListItem", 35 | "section": "Item Prices" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /schemas/app/PriceListItem.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PriceListItem", 3 | "label": "Price List Item", 4 | "isChild": true, 5 | "fields": [ 6 | { 7 | "fieldname": "item", 8 | "label": "Item", 9 | "fieldtype": "Link", 10 | "target": "Item", 11 | "required": true, 12 | "create": true, 13 | "section": "Item" 14 | }, 15 | { 16 | "fieldname": "unit", 17 | "label": "Unit Type", 18 | "fieldtype": "Link", 19 | "target": "UOM", 20 | "section": "Item" 21 | }, 22 | { 23 | "fieldname": "rate", 24 | "label": "Rate", 25 | "fieldtype": "Currency", 26 | "required": true, 27 | "section": "Item" 28 | } 29 | ], 30 | "tableFields": ["item", "unit", "rate"] 31 | } 32 | -------------------------------------------------------------------------------- /schemas/app/PricingRuleDetail.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PricingRuleDetail", 3 | "label": "Pricing Rule Detail", 4 | "isSingle": false, 5 | "isChild": true, 6 | "fields": [ 7 | { 8 | "label": "Pricing Rule", 9 | "fieldname": "referenceName", 10 | "fieldtype": "Link", 11 | "target": "PricingRule", 12 | "readOnly": true 13 | }, 14 | { 15 | "label": "Item", 16 | "fieldname": "referenceItem", 17 | "fieldtype": "Link", 18 | "target": "Item", 19 | "readOnly": true 20 | } 21 | ], 22 | "tableFields": ["referenceName", "referenceItem"] 23 | } 24 | -------------------------------------------------------------------------------- /schemas/app/PricingRuleItem.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PricingRuleItem", 3 | "label": "Pricing Rule Item", 4 | "isChild": true, 5 | "fields": [ 6 | { 7 | "fieldname": "item", 8 | "label": "Item", 9 | "fieldtype": "Link", 10 | "target": "Item", 11 | "required": true 12 | }, 13 | { 14 | "fieldname": "unit", 15 | "label": "Unit Type", 16 | "placeholder": "Unit Type", 17 | "fieldtype": "Link", 18 | "target": "UOM", 19 | "create": true, 20 | "section": "Default" 21 | } 22 | ], 23 | "tableFields": ["item", "unit"], 24 | "quickEditFields": ["item", "unit"] 25 | } 26 | -------------------------------------------------------------------------------- /schemas/app/PrintTemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PrintTemplate", 3 | "label": "Print Template", 4 | "naming": "manual", 5 | "isSingle": false, 6 | "fields": [ 7 | { 8 | "fieldname": "name", 9 | "label": "Template Name", 10 | "fieldtype": "Data", 11 | "required": true 12 | }, 13 | { 14 | "fieldname": "type", 15 | "label": "Template Type", 16 | "fieldtype": "AutoComplete", 17 | "default": "SalesInvoice", 18 | "required": true 19 | }, 20 | { 21 | "fieldname": "template", 22 | "label": "Template", 23 | "fieldtype": "Text", 24 | "required": true 25 | }, 26 | { 27 | "fieldname": "height", 28 | "label": "Height (in cm)", 29 | "fieldtype": "Float", 30 | "default": 29.7 31 | }, 32 | { 33 | "fieldname": "width", 34 | "label": "Width (in cm)", 35 | "fieldtype": "Float", 36 | "default": 21 37 | }, 38 | { 39 | "fieldname": "isCustom", 40 | "label": "Is Custom", 41 | "fieldtype": "Check", 42 | "default": true, 43 | "readOnly": true 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /schemas/app/PurchaseInvoiceItem.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PurchaseInvoiceItem", 3 | "label": "Purchase Invoice Item", 4 | "extends": "InvoiceItem" 5 | } 6 | -------------------------------------------------------------------------------- /schemas/app/SalesInvoiceItem.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SalesInvoiceItem", 3 | "label": "Sales Invoice Item", 4 | "extends": "InvoiceItem", 5 | "fields": [ 6 | { 7 | "fieldname": "isFreeItem", 8 | "fieldtype": "Check", 9 | "default": false, 10 | "hidden": true 11 | }, 12 | { 13 | "fieldname": "pricingRule", 14 | "fieldtype": "Data", 15 | "hidden": true 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /schemas/app/SalesQuote.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SalesQuote", 3 | "label": "Quote", 4 | "extends": "Invoice", 5 | "naming": "numberSeries", 6 | "showTitle": true, 7 | "fields": [ 8 | { 9 | "fieldname": "numberSeries", 10 | "label": "Number Series", 11 | "fieldtype": "Link", 12 | "target": "NumberSeries", 13 | "create": true, 14 | "required": true, 15 | "default": "SQUOT-", 16 | "section": "Default" 17 | }, 18 | { 19 | "fieldname": "referenceType", 20 | "label": "Type", 21 | "placeholder": "Type", 22 | "fieldtype": "Select", 23 | "default": "Party", 24 | "options": [ 25 | { 26 | "value": "Party", 27 | "label": "Party" 28 | }, 29 | { 30 | "value": "Lead", 31 | "label": "Lead" 32 | } 33 | ], 34 | "required": true 35 | }, 36 | { 37 | "fieldname": "party", 38 | "label": "Customer", 39 | "fieldtype": "DynamicLink", 40 | "references": "referenceType", 41 | "create": true, 42 | "required": true, 43 | "section": "Default" 44 | }, 45 | { 46 | "fieldname": "items", 47 | "label": "Items", 48 | "fieldtype": "Table", 49 | "target": "SalesQuoteItem", 50 | "required": true, 51 | "edit": true, 52 | "section": "Items" 53 | } 54 | ], 55 | "keywordFields": ["name", "party"], 56 | "removeFields": [ 57 | "account", 58 | "stockNotTransferred", 59 | "backReference", 60 | "makeAutoStockTransfer", 61 | "returnAgainst", 62 | "isReturned" 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /schemas/app/SalesQuoteItem.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SalesQuoteItem", 3 | "label": "Sales Quote Item", 4 | "extends": "InvoiceItem" 5 | } 6 | -------------------------------------------------------------------------------- /schemas/app/Tax.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tax", 3 | "label": "Tax Template", 4 | "naming": "manual", 5 | "isSingle": false, 6 | "isChild": false, 7 | "fields": [ 8 | { 9 | "fieldname": "name", 10 | "label": "Name", 11 | "fieldtype": "Data", 12 | "required": true 13 | }, 14 | { 15 | "fieldname": "details", 16 | "label": "Details", 17 | "fieldtype": "Table", 18 | "target": "TaxDetail", 19 | "required": true 20 | } 21 | ], 22 | "keywordFields": ["name"], 23 | "quickEditFields": ["details"] 24 | } 25 | -------------------------------------------------------------------------------- /schemas/app/TaxDetail.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TaxDetail", 3 | "label": "Tax Detail", 4 | "isSingle": false, 5 | "isChild": true, 6 | "fields": [ 7 | { 8 | "fieldname": "account", 9 | "label": "Tax Invoice Account", 10 | "fieldtype": "Link", 11 | "target": "Account", 12 | "create": true, 13 | "required": true 14 | }, 15 | { 16 | "fieldname": "payment_account", 17 | "label": "Tax Payment Account", 18 | "fieldtype": "Link", 19 | "target": "Account", 20 | "create": true, 21 | "required": false 22 | }, 23 | { 24 | "fieldname": "rate", 25 | "label": "Rate", 26 | "fieldtype": "Float", 27 | "required": true, 28 | "placeholder": "0%" 29 | } 30 | ], 31 | "tableFields": ["account", "payment_account", "rate"] 32 | } 33 | -------------------------------------------------------------------------------- /schemas/app/TaxSummary.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TaxSummary", 3 | "label": "Tax Summary", 4 | "isChild": true, 5 | "fields": [ 6 | { 7 | "fieldname": "account", 8 | "label": "Tax Account", 9 | "fieldtype": "Link", 10 | "target": "Account", 11 | "required": true 12 | }, 13 | { 14 | "fieldname": "from_account", 15 | "label": "Tax Invoice Account", 16 | "fieldtype": "Link", 17 | "target": "Account", 18 | "required": false, 19 | "hidden": true 20 | }, 21 | { 22 | "fieldname": "rate", 23 | "label": "Tax Rate", 24 | "fieldtype": "Float", 25 | "required": true, 26 | "readOnly": true 27 | }, 28 | { 29 | "fieldname": "amount", 30 | "label": "Tax Amount", 31 | "fieldtype": "Currency", 32 | "required": true, 33 | "readOnly": true 34 | } 35 | ], 36 | "tableFields": ["account", "rate", "amount"] 37 | } 38 | -------------------------------------------------------------------------------- /schemas/app/UOM.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UOM", 3 | "label": "UOM", 4 | "isSingle": false, 5 | "naming": "manual", 6 | "fields": [ 7 | { 8 | "fieldname": "name", 9 | "label": "UOM", 10 | "fieldtype": "Data", 11 | "placeholder": "Item Name", 12 | "required": true 13 | }, 14 | { 15 | "fieldname": "isWhole", 16 | "label": "Is Whole", 17 | "fieldtype": "Check", 18 | "default": false 19 | } 20 | ], 21 | "quickEditFields": ["isWhole"] 22 | } 23 | -------------------------------------------------------------------------------- /schemas/app/inventory/Location.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Location", 3 | "label": "Location", 4 | "isSingle": false, 5 | "isChild": false, 6 | "naming": "manual", 7 | "fields": [ 8 | { 9 | "fieldname": "name", 10 | "label": "Location Name", 11 | "fieldtype": "Data", 12 | "required": true 13 | }, 14 | { 15 | "fieldname": "address", 16 | "label": "Address", 17 | "fieldtype": "Link", 18 | "target": "Address", 19 | "create": true 20 | } 21 | ], 22 | "quickEditFields": ["item", "address"] 23 | } 24 | -------------------------------------------------------------------------------- /schemas/app/inventory/Point of Sale/CashDenominations.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CashDenominations", 3 | "label": "Cash Denominations", 4 | "isAbstract": true, 5 | "fields": [ 6 | { 7 | "fieldname": "denomination", 8 | "fieldtype": "Currency", 9 | "label": "Denomination", 10 | "placeholder": "Denomination", 11 | "required": true 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /schemas/app/inventory/Point of Sale/ClosingAmounts.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ClosingAmounts", 3 | "label": "Closing Amount", 4 | "isChild": true, 5 | "extends": "POSShiftAmounts", 6 | "fields": [ 7 | { 8 | "fieldname": "openingAmount", 9 | "fieldtype": "Currency", 10 | "label": "Opening Amount", 11 | "placeholder": "Opening Amount", 12 | "readOnly": true 13 | }, 14 | { 15 | "fieldname": "closingAmount", 16 | "fieldtype": "Currency", 17 | "label": "Closing Amount", 18 | "placeholder": "Closing Amount" 19 | }, 20 | { 21 | "fieldname": "expectedAmount", 22 | "fieldtype": "Currency", 23 | "label": "Expected Amount", 24 | "placeholder": "Expected Amount", 25 | "readOnly": true 26 | }, 27 | { 28 | "fieldname": "differenceAmount", 29 | "fieldtype": "Currency", 30 | "label": "Difference Amount", 31 | "placeholder": "Difference Amount", 32 | "readOnly": true 33 | } 34 | ], 35 | "tableFields": [ 36 | "paymentMethod", 37 | "openingAmount", 38 | "closingAmount", 39 | "expectedAmount", 40 | "differenceAmount" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /schemas/app/inventory/Point of Sale/ClosingCash.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ClosingCash", 3 | "label": "Closing Cash In Denominations", 4 | "isChild": true, 5 | "extends": "CashDenominations", 6 | "fields": [ 7 | { 8 | "fieldname": "count", 9 | "label": "Count", 10 | "placeholder": "Count", 11 | "fieldtype": "Int", 12 | "default": 0, 13 | "required": true 14 | } 15 | ], 16 | "tableFields": ["denomination", "count"] 17 | } 18 | -------------------------------------------------------------------------------- /schemas/app/inventory/Point of Sale/DefaultCashDenominations.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DefaultCashDenominations", 3 | "label": "Default Cash Denominations", 4 | "isChild": true, 5 | "extends": "CashDenominations", 6 | "tableFields": ["denomination"] 7 | } 8 | -------------------------------------------------------------------------------- /schemas/app/inventory/Point of Sale/OpeningAmounts.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OpeningAmounts", 3 | "label": "Opening Amount", 4 | "isChild": true, 5 | "extends": "POSShiftAmounts", 6 | "fields": [ 7 | { 8 | "fieldname": "amount", 9 | "label": "Amount", 10 | "fieldtype": "Currency", 11 | "section": "Defaults" 12 | } 13 | ], 14 | "tableFields": ["paymentMethod", "amount"] 15 | } 16 | -------------------------------------------------------------------------------- /schemas/app/inventory/Point of Sale/OpeningCash.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OpeningCash", 3 | "label": "Opening Cash In Denominations", 4 | "isChild": true, 5 | "extends": "CashDenominations", 6 | "fields": [ 7 | { 8 | "fieldname": "count", 9 | "label": "Count", 10 | "placeholder": "Count", 11 | "fieldtype": "Int", 12 | "default": 0, 13 | "required": true 14 | } 15 | ], 16 | "tableFields": ["denomination", "count"] 17 | } 18 | -------------------------------------------------------------------------------- /schemas/app/inventory/Point of Sale/POSClosingShift.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "POSClosingShift", 3 | "label": "POS Closing Shift", 4 | "naming": "random", 5 | "fields": [ 6 | { 7 | "fieldname": "closingDate", 8 | "label": "Closing Date", 9 | "fieldtype": "Datetime" 10 | }, 11 | { 12 | "fieldname": "closingCash", 13 | "fieldtype": "Table", 14 | "label": "Closing Cash", 15 | "target": "ClosingCash" 16 | }, 17 | { 18 | "fieldname": "closingAmounts", 19 | "fieldtype": "Table", 20 | "label": "Closing Amounts", 21 | "target": "ClosingAmounts" 22 | }, 23 | { 24 | "fieldname": "openingShift", 25 | "fieldtype": "Link", 26 | "label": "Opening Shift", 27 | "target": "POSOpeningShift" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /schemas/app/inventory/Point of Sale/POSOpeningShift.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "POSOpeningShift", 3 | "label": "POS Opening Shift", 4 | "naming": "random", 5 | "fields": [ 6 | { 7 | "fieldname": "openingDate", 8 | "label": "Opening Date", 9 | "fieldtype": "Datetime" 10 | }, 11 | { 12 | "fieldname": "openingCash", 13 | "fieldtype": "Table", 14 | "label": "Opening Cash", 15 | "target": "OpeningCash" 16 | }, 17 | { 18 | "fieldname": "openingAmounts", 19 | "fieldtype": "Table", 20 | "label": "Opening Amounts", 21 | "target": "OpeningAmounts" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /schemas/app/inventory/Point of Sale/POSShiftAmounts.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "POSShiftAmounts", 3 | "label": "POS Shift Amount", 4 | "isChild": true, 5 | "isAbstract": true, 6 | "fields": [ 7 | { 8 | "fieldname": "paymentMethod", 9 | "label": "Payment Method", 10 | "placeholder": "Payment Method", 11 | "fieldtype": "Select", 12 | "options": [ 13 | { 14 | "value": "Cash", 15 | "label": "Cash" 16 | }, 17 | { 18 | "value": "Transfer", 19 | "label": "Transfer" 20 | }, 21 | { 22 | "value": "Bank", 23 | "label": "Bank" 24 | } 25 | ], 26 | "required": true 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /schemas/app/inventory/PurchaseReceipt.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PurchaseReceipt", 3 | "label": "Purchase Receipt", 4 | "extends": "StockTransfer", 5 | "naming": "numberSeries", 6 | "showTitle": true, 7 | "fields": [ 8 | { 9 | "fieldname": "numberSeries", 10 | "label": "Number Series", 11 | "fieldtype": "Link", 12 | "target": "NumberSeries", 13 | "create": true, 14 | "required": true, 15 | "default": "PREC-", 16 | "section": "Default" 17 | }, 18 | { 19 | "fieldname": "backReference", 20 | "label": "Back Reference", 21 | "fieldtype": "Link", 22 | "target": "PurchaseInvoice", 23 | "section": "References" 24 | }, 25 | { 26 | "fieldname": "items", 27 | "label": "Items", 28 | "fieldtype": "Table", 29 | "target": "PurchaseReceiptItem", 30 | "required": true, 31 | "edit": true 32 | }, 33 | { 34 | "fieldname": "returnAgainst", 35 | "fieldtype": "Link", 36 | "target": "PurchaseReceipt", 37 | "label": "Return Against", 38 | "section": "References" 39 | } 40 | ], 41 | "keywordFields": ["name", "party"] 42 | } 43 | -------------------------------------------------------------------------------- /schemas/app/inventory/PurchaseReceiptItem.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PurchaseReceiptItem", 3 | "label": "Purchase Receipt Item", 4 | "extends": "StockTransferItem", 5 | "fields": [ 6 | { 7 | "fieldname": "location", 8 | "label": "To Loc.", 9 | "fieldtype": "Link", 10 | "target": "Location", 11 | "required": true, 12 | "create": true 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /schemas/app/inventory/SerialNumber.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SerialNumber", 3 | "label": "Serial Number", 4 | "naming": "manual", 5 | "fields": [ 6 | { 7 | "fieldname": "name", 8 | "fieldtype": "Data", 9 | "label": "Serial Number", 10 | "create": true, 11 | "required": true 12 | }, 13 | { 14 | "fieldname": "item", 15 | "fieldtype": "Link", 16 | "label": "Item", 17 | "target": "Item", 18 | "create": true, 19 | "required": true 20 | }, 21 | { 22 | "fieldname": "description", 23 | "label": "Description", 24 | "placeholder": "Serial Number Description", 25 | "fieldtype": "Text" 26 | }, 27 | { 28 | "fieldname": "status", 29 | "label": "Status", 30 | "fieldtype": "Select", 31 | "default": "Inactive", 32 | "readOnly": true, 33 | "options": [ 34 | { 35 | "value": "Inactive", 36 | "label": "Inactive" 37 | }, 38 | { 39 | "value": "Active", 40 | "label": "Active" 41 | }, 42 | { 43 | "value": "Delivered", 44 | "label": "Delivered" 45 | } 46 | ] 47 | } 48 | ], 49 | "quickEditFields": ["item", "description"], 50 | "keywordFields": ["name"] 51 | } 52 | -------------------------------------------------------------------------------- /schemas/app/inventory/Shipment.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Shipment", 3 | "label": "Shipment", 4 | "extends": "StockTransfer", 5 | "naming": "numberSeries", 6 | "showTitle": true, 7 | "fields": [ 8 | { 9 | "fieldname": "numberSeries", 10 | "label": "Number Series", 11 | "fieldtype": "Link", 12 | "target": "NumberSeries", 13 | "create": true, 14 | "required": true, 15 | "default": "SHPM-", 16 | "section": "Default" 17 | }, 18 | { 19 | "fieldname": "backReference", 20 | "label": "Back Reference", 21 | "fieldtype": "Link", 22 | "target": "SalesInvoice", 23 | "section": "References" 24 | }, 25 | { 26 | "fieldname": "items", 27 | "label": "Items", 28 | "fieldtype": "Table", 29 | "target": "ShipmentItem", 30 | "required": true, 31 | "edit": true, 32 | "section": "Items" 33 | }, 34 | { 35 | "fieldname": "returnAgainst", 36 | "fieldtype": "Link", 37 | "target": "Shipment", 38 | "label": "Return Against", 39 | "section": "References" 40 | } 41 | ], 42 | "keywordFields": ["name", "party"] 43 | } 44 | -------------------------------------------------------------------------------- /schemas/app/inventory/ShipmentItem.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ShipmentItem", 3 | "label": "Shipment Item", 4 | "extends": "StockTransferItem", 5 | "fields": [ 6 | { 7 | "fieldname": "location", 8 | "label": "From Loc.", 9 | "fieldtype": "Link", 10 | "target": "Location", 11 | "required": true, 12 | "create": true 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /schemas/app/inventory/UOMConversionItem.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UOMConversionItem", 3 | "label": "UOM Conversion Item", 4 | "isChild": true, 5 | "fields": [ 6 | { 7 | "fieldname": "uom", 8 | "label": "UOM", 9 | "fieldtype": "Link", 10 | "target": "UOM", 11 | "create": true, 12 | "required": true 13 | }, 14 | { 15 | "fieldname": "conversionFactor", 16 | "label": "Conversion Factor", 17 | "fieldtype": "Float", 18 | "default": 1, 19 | "required": true 20 | } 21 | ], 22 | "tableFields": ["uom", "conversionFactor"] 23 | } 24 | -------------------------------------------------------------------------------- /schemas/core/CustomForm.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CustomForm", 3 | "label": "Custom Form", 4 | "naming": "manual", 5 | "fields": [ 6 | { 7 | "fieldname": "name", 8 | "fieldtype": "AutoComplete", 9 | "label": "Form Type", 10 | "options": [], 11 | "required": true 12 | }, 13 | { 14 | "fieldname": "customFields", 15 | "label": "Custom Fields", 16 | "fieldtype": "Table", 17 | "target": "CustomField", 18 | "required": true, 19 | "edit": true 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /schemas/core/PatchRun.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PatchRun", 3 | "label": "Patch Run", 4 | "naming": "manual", 5 | "fields": [ 6 | { 7 | "fieldname": "name", 8 | "fieldtype": "Data", 9 | "label": "Name", 10 | "required": true 11 | }, 12 | { 13 | "fieldname": "failed", 14 | "fieldtype": "Check", 15 | "label": "Failed", 16 | "default": false 17 | }, 18 | { 19 | "fieldname": "version", 20 | "fieldtype": "Data", 21 | "label": "Version", 22 | "default": "0.0.0" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /schemas/core/SingleValue.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SingleValue", 3 | "label": "Single Value", 4 | "isSingle": false, 5 | "isChild": false, 6 | "fields": [ 7 | { 8 | "fieldname": "parent", 9 | "label": "Parent", 10 | "fieldtype": "Data", 11 | "required": true 12 | }, 13 | { 14 | "fieldname": "fieldname", 15 | "label": "Fieldname", 16 | "fieldtype": "Data", 17 | "required": true 18 | }, 19 | { 20 | "fieldname": "value", 21 | "label": "Value", 22 | "fieldtype": "Data", 23 | "required": true 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /schemas/meta/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base", 3 | "fields": [ 4 | { 5 | "fieldname": "createdBy", 6 | "label": "Created By", 7 | "fieldtype": "Data", 8 | "required": true, 9 | "meta": true, 10 | "section": "System" 11 | }, 12 | { 13 | "fieldname": "modifiedBy", 14 | "label": "Modified By", 15 | "fieldtype": "Data", 16 | "required": true, 17 | "meta": true, 18 | "section": "System" 19 | }, 20 | { 21 | "fieldname": "created", 22 | "label": "Created", 23 | "fieldtype": "Datetime", 24 | "required": true, 25 | "meta": true, 26 | "section": "System" 27 | }, 28 | { 29 | "fieldname": "modified", 30 | "label": "Modified", 31 | "fieldtype": "Datetime", 32 | "required": true, 33 | "meta": true, 34 | "section": "System" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /schemas/meta/child.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "child", 3 | "fields": [ 4 | { 5 | "fieldname": "idx", 6 | "fieldtype": "Int", 7 | "required": true, 8 | "meta": true 9 | }, 10 | { 11 | "fieldname": "parent", 12 | "fieldtype": "Data", 13 | "required": true, 14 | "meta": true 15 | }, 16 | { 17 | "fieldname": "parentSchemaName", 18 | "fieldtype": "Data", 19 | "required": true, 20 | "meta": true 21 | }, 22 | { 23 | "fieldname": "parentFieldname", 24 | "fieldtype": "Data", 25 | "required": true, 26 | "meta": true 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /schemas/meta/submittable.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "submittable", 3 | "fields": [ 4 | { 5 | "fieldname": "submitted", 6 | "label": "Submitted", 7 | "fieldtype": "Check", 8 | "required": true, 9 | "meta": true, 10 | "section": "System" 11 | }, 12 | { 13 | "fieldname": "cancelled", 14 | "label": "Cancelled", 15 | "fieldtype": "Check", 16 | "required": true, 17 | "meta": true, 18 | "section": "System" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /schemas/meta/tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree", 3 | "fields": [ 4 | { 5 | "fieldname": "lft", 6 | "label": "Left Index", 7 | "fieldtype": "Int", 8 | "required": true, 9 | "meta": true 10 | }, 11 | { 12 | "fieldname": "rgt", 13 | "label": "Right Index", 14 | "fieldtype": "Int", 15 | "required": true, 16 | "meta": true 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /schemas/regional/ch/AccountingSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AccountingSettings", 3 | "fields": [ 4 | { 5 | "fieldname": "taxId", 6 | "label": "Tax ID", 7 | "fieldtype": "Data", 8 | "placeholder": "CHE-123.456.789", 9 | "section": "Default" 10 | } 11 | ], 12 | "quickEditFields": [ 13 | "fullname", 14 | "email", 15 | "companyName", 16 | "country", 17 | "fiscalYearStart", 18 | "fiscalYearEnd", 19 | "taxId" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /schemas/regional/ch/index.ts: -------------------------------------------------------------------------------- 1 | import { SchemaStub } from '../../types'; 2 | import AccountingSettings from './AccountingSettings.json'; 3 | 4 | export default [AccountingSettings] as SchemaStub[]; 5 | -------------------------------------------------------------------------------- /schemas/regional/in/AccountingSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AccountingSettings", 3 | "fields": [ 4 | { 5 | "fieldname": "gstin", 6 | "label": "GSTIN", 7 | "fieldtype": "Data", 8 | "placeholder": "27AAAAA0000A1Z5", 9 | "section": "Default" 10 | } 11 | ], 12 | "quickEditFields": [ 13 | "fullname", 14 | "email", 15 | "companyName", 16 | "country", 17 | "currency", 18 | "fiscalYearStart", 19 | "fiscalYearEnd", 20 | "gstin" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /schemas/regional/in/Address.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Address", 3 | "fields": [ 4 | { 5 | "fieldname": "pos", 6 | "label": "Place of Supply", 7 | "fieldtype": "AutoComplete", 8 | "placeholder": "Place of Supply", 9 | "section": "Miscellaneous" 10 | } 11 | ], 12 | "quickEditFields": [ 13 | "addressLine1", 14 | "addressLine2", 15 | "city", 16 | "country", 17 | "state", 18 | "postalCode", 19 | "pos" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /schemas/regional/in/Party.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Party", 3 | "fields": [ 4 | { 5 | "fieldname": "gstType", 6 | "label": "GST Registration", 7 | "placeholder": "GST Registration", 8 | "fieldtype": "Select", 9 | "default": "Unregistered", 10 | "options": [ 11 | { 12 | "value": "Unregistered", 13 | "label": "Unregistered" 14 | }, 15 | { 16 | "value": "Registered Regular", 17 | "label": "Registered Regular" 18 | }, 19 | { 20 | "value": "Consumer", 21 | "label": "Consumer" 22 | } 23 | ], 24 | "section": "Billing" 25 | }, 26 | { 27 | "fieldname": "gstin", 28 | "label": "GSTIN No.", 29 | "fieldtype": "Data", 30 | "section": "Billing" 31 | } 32 | ], 33 | "quickEditFields": [ 34 | "email", 35 | "phone", 36 | "address", 37 | "defaultAccount", 38 | "currency", 39 | "role", 40 | "gstType", 41 | "gstin" 42 | ], 43 | "removeFields": ["taxId"] 44 | } 45 | -------------------------------------------------------------------------------- /schemas/regional/in/index.ts: -------------------------------------------------------------------------------- 1 | import { SchemaStub } from '../../types'; 2 | import AccountingSettings from './AccountingSettings.json'; 3 | import Address from './Address.json'; 4 | import Party from './Party.json'; 5 | 6 | export default [AccountingSettings, Address, Party] as SchemaStub[]; 7 | -------------------------------------------------------------------------------- /schemas/regional/index.ts: -------------------------------------------------------------------------------- 1 | import { SchemaStub } from 'schemas/types'; 2 | import IndianSchemas from './in'; 3 | import SwissSchemas from './ch'; 4 | 5 | /** 6 | * Regional Schemas are exported by country code. 7 | */ 8 | export default { in: IndianSchemas, ch: SwissSchemas } as Record< 9 | string, 10 | SchemaStub[] 11 | >; 12 | -------------------------------------------------------------------------------- /schemas/tests/Customer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Customer", 3 | "label": "Customer", 4 | "extends": "Party" 5 | } 6 | -------------------------------------------------------------------------------- /scripts/profile.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env zsh 2 | 3 | # https://nodejs.org/en/docs/guides/simple-profiling/ 4 | 5 | export TS_NODE_COMPILER_OPTIONS='{"module":"commonjs"}' 6 | 7 | rm ./isolate-*-v8.log 2> /dev/null 8 | rm ./profiler-output.log 2> /dev/null 9 | 10 | export ELECTRON_RUN_AS_NODE=true 11 | alias electron_node=./node_modules/.bin/electron 12 | 13 | echo "running profile.ts" 14 | electron_node --require ts-node/register --require tsconfig-paths/register --prof ./scripts/profile.ts 15 | 16 | echo "processing tick file" 17 | electron_node --prof-process ./isolate-*-v8.log > ./profiler-output.log && echo "generated profiler-output.log" 18 | rm ./isolate-*-v8.log -------------------------------------------------------------------------------- /scripts/profile.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseManager } from 'backend/database/manager'; 2 | import { setupDummyInstance } from 'dummy'; 3 | import { unlink } from 'fs/promises'; 4 | import { Fyo } from 'fyo'; 5 | import { DummyAuthDemux } from 'fyo/tests/helpers'; 6 | import { getTestDbPath } from 'tests/helpers'; 7 | 8 | async function run() { 9 | const fyo = new Fyo({ 10 | DatabaseDemux: DatabaseManager, 11 | AuthDemux: DummyAuthDemux, 12 | isTest: true, 13 | isElectron: false, 14 | }); 15 | const dbPath = getTestDbPath(); 16 | 17 | await setupDummyInstance(dbPath, fyo, 1, 100); 18 | await fyo.close(); 19 | await unlink(dbPath); 20 | } 21 | 22 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 23 | run(); 24 | -------------------------------------------------------------------------------- /scripts/publish-mac-arm.sh: -------------------------------------------------------------------------------- 1 | # #! /bin/zsh 2 | 3 | set -e 4 | 5 | # Check node and yarn versions 6 | YARN_VERSION=$(yarn --version) 7 | if [ "$YARN_VERSION" != "1.22.18" ]; then 8 | echo "Incorrect yarn version: $YARN_VERSION" 9 | exit 1 10 | fi 11 | 12 | # Source secrets 13 | source .env.publish 14 | 15 | # Create folder for the publish build 16 | cd ../ 17 | rm -rf build_publish 18 | mkdir build_publish 19 | cd build_publish 20 | 21 | # Clone and cd into books 22 | git clone https://github.com/frappe/books --depth 1 23 | cd books 24 | 25 | # Copy creds to log_creds.txt 26 | echo $ERR_LOG_KEY > log_creds.txt 27 | echo $ERR_LOG_SECRET >> log_creds.txt 28 | echo $ERR_LOG_URL >> log_creds.txt 29 | echo $TELEMETRY_URL >> log_creds.txt 30 | 31 | 32 | # Install Dependencies 33 | yarn install 34 | 35 | # Set .env and build 36 | export GH_TOKEN=$GH_TOKEN && 37 | export CSC_IDENTITY_AUTO_DISCOVERY=true && 38 | export APPLE_ID=$APPLE_ID && 39 | export APPLE_TEAM_ID=$APPLE_TEAM_ID && 40 | export APPLE_APP_SPECIFIC_PASSWORD=$APPLE_APP_SPECIFIC_PASSWORD && 41 | yarn build --mac --publish=always 42 | 43 | cd ../books 44 | -------------------------------------------------------------------------------- /scripts/runner.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env zsh 2 | 3 | # basically uses electron's node to prevent 4 | # mismatch in NODE_MODULE_VERSION when running 5 | # better-sqlite3 6 | 7 | export TS_NODE_COMPILER_OPTIONS='{"module":"commonjs"}' 8 | export ELECTRON_RUN_AS_NODE=true 9 | alias electron_node="./node_modules/.bin/electron --require ts-node/register --require tsconfig-paths/register" 10 | electron_node $@ -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | TEST_PATH=$@ 2 | 3 | if [ $# -eq 0 ] 4 | then 5 | TEST_PATH=./**/tests/**/*.spec.ts 6 | fi 7 | 8 | export IS_TEST=true 9 | ./scripts/runner.sh ./node_modules/.bin/tape $TEST_PATH | ./node_modules/.bin/tap-spec -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # src 2 | 3 | This is where all the frontend code lives 4 | 5 | ## Fyo Initialization 6 | 7 | The initialization flows are different when the instance is new or is existing. 8 | All of them are triggered from `src/App.vue`. 9 | 10 | **New Instance** 11 | 12 | 1. Run _Setup Wizard_ for init values (eg: `country`). 13 | 2. Call `setupInstance.ts/setupInstance` using init values. 14 | 15 | **Existing Instance** 16 | 17 | 1. Connect to db. 18 | 2. Check if _Setup Wizard_ has been completed, if not, jump to **New Instance** 19 | 3. Call `initFyo/initializeInstance` with `dbPath` and `countryCode` 20 | 21 | ## Global Fyo 22 | 23 | Global fyo is exported from `initFyo.ts`. Only code that isn't going to be unit 24 | tested using `mocha` should use this, i.e. code in `src` 25 | -------------------------------------------------------------------------------- /src/assets/fonts/Inter.var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/books/81333cd487622f63310c59bced9a973b1f7ca080/src/assets/fonts/Inter.var.woff2 -------------------------------------------------------------------------------- /src/assets/img/list-empty-state.svg: -------------------------------------------------------------------------------- 1 | <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 78 85"><rect width="64" height="72" x="13" y="12" stroke="#C7C7C7" stroke-width="2" rx="7"/><path fill="#C7C7C7" fill-rule="evenodd" d="M8 2H58C61.3137 2 64 4.68629 64 8V9H66V8C66 3.58172 62.4183 0 58 0H8C3.58172 0 0 3.58172 0 8V66C0 70.4183 3.58172 74 8 74H10V72H8C4.68629 72 2 69.3137 2 66V8C2 4.68629 4.68629 2 8 2Z" clip-rule="evenodd"/><path stroke="#C7C7C7" stroke-linecap="round" stroke-width="2" d="M42 31H66"/><path stroke="#C7C7C7" stroke-linecap="round" stroke-width="2" d="M42 51H66"/><path stroke="#C7C7C7" stroke-linecap="round" stroke-width="2" d="M42 25H55"/><path stroke="#C7C7C7" stroke-linecap="round" stroke-width="2" d="M42 45H55"/><rect width="10" height="10" x="24" y="23" stroke="#7C7C7C" stroke-width="2" rx="2"/><rect width="10" height="10" x="24" y="43" stroke="#7C7C7C" stroke-width="2" rx="2"/></svg> -------------------------------------------------------------------------------- /src/components/Avatar.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div class="rounded-full overflow-hidden" :class="sizeClasses"> 3 | <img 4 | v-if="imageURL" 5 | :src="imageURL" 6 | class="object-cover" 7 | :class="sizeClasses" 8 | /> 9 | <div 10 | v-else 11 | class=" 12 | bg-gray-500 13 | flex 14 | h-full 15 | items-center 16 | justify-center 17 | text-white 18 | dark:text-gray-900 19 | w-full 20 | text-base 21 | uppercase 22 | " 23 | > 24 | {{ label && label[0] }} 25 | </div> 26 | </div> 27 | </template> 28 | 29 | <script> 30 | export default { 31 | name: 'Avatar', 32 | props: { 33 | imageURL: String, 34 | label: String, 35 | size: { 36 | default: 'md', 37 | }, 38 | }, 39 | computed: { 40 | sizeClasses() { 41 | return { 42 | sm: 'w-5 h-5', 43 | md: 'w-7 h-7', 44 | lg: 'w-9 h-9', 45 | }[this.size]; 46 | }, 47 | }, 48 | }; 49 | </script> 50 | -------------------------------------------------------------------------------- /src/components/Badge.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div 3 | class="inline-block rounded-md px-2 py-1 truncate select-none" 4 | :class="colorClass" 5 | > 6 | <slot></slot> 7 | </div> 8 | </template> 9 | 10 | <script> 11 | import { getBgTextColorClass } from 'src/utils/colors'; 12 | 13 | export default { 14 | name: 'Badge', 15 | props: { 16 | color: { 17 | default: 'gray', 18 | }, 19 | }, 20 | computed: { 21 | colorClass() { 22 | return getBgTextColorClass(this.color); 23 | }, 24 | }, 25 | }; 26 | </script> 27 | -------------------------------------------------------------------------------- /src/components/Button.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <button 3 | class="rounded-md flex justify-center items-center text-sm" 4 | :disabled="disabled" 5 | :class="_class" 6 | v-bind="$attrs" 7 | > 8 | <slot></slot> 9 | </button> 10 | </template> 11 | <script lang="ts"> 12 | import { defineComponent } from 'vue'; 13 | 14 | export default defineComponent({ 15 | name: 'Button', 16 | props: { 17 | type: { 18 | type: String, 19 | default: 'secondary', 20 | }, 21 | icon: { 22 | type: Boolean, 23 | default: false, 24 | }, 25 | disabled: { 26 | type: Boolean, 27 | default: false, 28 | }, 29 | padding: { 30 | type: Boolean, 31 | default: true, 32 | }, 33 | background: { 34 | type: Boolean, 35 | default: true, 36 | }, 37 | }, 38 | computed: { 39 | _class() { 40 | return { 41 | 'opacity-50 cursor-not-allowed pointer-events-none': this.disabled, 42 | 'text-white dark:text-black': this.type === 'primary', 43 | 'bg-black dark:bg-gray-300 dark:font-semibold': 44 | this.type === 'primary' && this.background, 45 | 'text-gray-700 dark:text-gray-200': this.type !== 'primary', 46 | 'bg-gray-200 dark:bg-gray-900': 47 | this.type !== 'primary' && this.background, 48 | 'h-8': this.background, 49 | 'px-3': this.padding && this.icon, 50 | 'px-6': this.padding && !this.icon, 51 | }; 52 | }, 53 | }, 54 | }); 55 | </script> 56 | <style scoped> 57 | button:focus { 58 | filter: brightness(0.95); 59 | } 60 | </style> 61 | -------------------------------------------------------------------------------- /src/components/Controls/Button.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div 3 | :class="[containerClasses]" 4 | class=" 5 | mt-6 6 | p-2 7 | w-full 8 | text-gray-900 9 | dark:text-gray-100 10 | text-base 11 | focus:outline-none 12 | " 13 | > 14 | <label class="flex items-center justify-between w-full"> 15 | <div v-if="showLabel && !labelRight" :class="labelClasses"> 16 | {{ df.label }} 17 | </div> 18 | <button 19 | :class="['flex items-center justify-center']" 20 | @click="onClick" 21 | :disabled="isReadOnly" 22 | type="button" 23 | ></button> 24 | </label> 25 | </div> 26 | </template> 27 | 28 | <script lang="ts"> 29 | import { defineComponent } from 'vue'; 30 | import Base from './Base.vue'; 31 | 32 | export default defineComponent({ 33 | name: 'Button', 34 | extends: Base, 35 | props: { 36 | spaceBetween: { 37 | type: Boolean, 38 | default: true, 39 | }, 40 | labelRight: { 41 | type: Boolean, 42 | default: false, 43 | }, 44 | labelClass: { 45 | type: String, 46 | default: '', 47 | }, 48 | showLabel: { 49 | type: Boolean, 50 | default: true, 51 | }, 52 | }, 53 | computed: { 54 | labelClasses() { 55 | return this.labelClass || 'text-gray-600 text-base'; 56 | }, 57 | }, 58 | methods: { 59 | onClick(e: Event) { 60 | if (this.isReadOnly) return; 61 | this.triggerChange(true); 62 | }, 63 | }, 64 | }); 65 | </script> 66 | -------------------------------------------------------------------------------- /src/components/Controls/Data.vue: -------------------------------------------------------------------------------- 1 | <script lang="ts"> 2 | import Base from './Base.vue'; 3 | import { defineComponent } from 'vue'; 4 | 5 | export default defineComponent({ 6 | name: 'Data', 7 | extends: Base, 8 | computed: { 9 | inputType() { 10 | return 'text'; 11 | }, 12 | }, 13 | }); 14 | </script> 15 | -------------------------------------------------------------------------------- /src/components/Controls/Datetime.vue: -------------------------------------------------------------------------------- 1 | <script lang="ts"> 2 | import { defineComponent } from 'vue'; 3 | import DateVue from './Date.vue'; 4 | import { DateTime } from 'luxon'; 5 | 6 | export default defineComponent({ 7 | extends: DateVue, 8 | computed: { 9 | inputType() { 10 | return 'datetime-local'; 11 | }, 12 | inputValue(): string { 13 | let value = this.value; 14 | if (typeof value === 'string') { 15 | value = new Date(value); 16 | } 17 | 18 | if (value instanceof Date && !Number.isNaN(value.valueOf())) { 19 | return DateTime.fromJSDate(value).toISO().split('.')[0]; 20 | } 21 | 22 | return ''; 23 | }, 24 | }, 25 | }); 26 | </script> 27 | -------------------------------------------------------------------------------- /src/components/Controls/Float.vue: -------------------------------------------------------------------------------- 1 | <script lang="ts"> 2 | import { safeParseFloat } from 'utils/index'; 3 | import { defineComponent } from 'vue'; 4 | import Int from './Int.vue'; 5 | 6 | export default defineComponent({ 7 | name: 'Float', 8 | extends: Int, 9 | computed: { 10 | inputType() { 11 | return 'number'; 12 | }, 13 | }, 14 | methods: { 15 | parse(value: unknown): number { 16 | return safeParseFloat(value); 17 | }, 18 | }, 19 | }); 20 | </script> 21 | -------------------------------------------------------------------------------- /src/components/Controls/Int.vue: -------------------------------------------------------------------------------- 1 | <script lang="ts"> 2 | import Data from './Data.vue'; 3 | import { defineComponent } from 'vue'; 4 | import { safeParseInt } from 'utils/index'; 5 | 6 | export default defineComponent({ 7 | name: 'Int', 8 | extends: Data, 9 | computed: { 10 | inputType() { 11 | return 'number'; 12 | }, 13 | }, 14 | methods: { 15 | parse(value: unknown): number { 16 | return safeParseInt(value); 17 | }, 18 | }, 19 | }); 20 | </script> 21 | -------------------------------------------------------------------------------- /src/components/Controls/Secret.vue: -------------------------------------------------------------------------------- 1 | <script lang="ts"> 2 | import Base from './Base.vue'; 3 | import { defineComponent } from 'vue'; 4 | 5 | export default defineComponent({ 6 | name: 'Secret', 7 | extends: Base, 8 | computed: { 9 | inputType() { 10 | return 'password'; 11 | }, 12 | }, 13 | }); 14 | </script> 15 | -------------------------------------------------------------------------------- /src/components/Controls/Text.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div> 3 | <div v-if="showLabel" :class="labelClasses"> 4 | {{ df.label }} 5 | </div> 6 | <div :class="showMandatory ? 'show-mandatory' : ''"> 7 | <textarea 8 | ref="input" 9 | :rows="rows" 10 | :class="['resize-none bg-transparent', inputClasses, containerClasses]" 11 | :value="value" 12 | :placeholder="inputPlaceholder" 13 | style="vertical-align: top" 14 | :readonly="isReadOnly" 15 | :tabindex="isReadOnly ? '-1' : '0'" 16 | @blur="(e) => triggerChange(e.target.value)" 17 | @focus="(e) => $emit('focus', e)" 18 | @input="(e) => $emit('input', e)" 19 | ></textarea> 20 | </div> 21 | </div> 22 | </template> 23 | 24 | <script> 25 | import Base from './Base.vue'; 26 | 27 | export default { 28 | name: 'Text', 29 | extends: Base, 30 | props: { rows: { type: Number, default: 3 } }, 31 | emits: ['focus', 'input'], 32 | }; 33 | </script> 34 | -------------------------------------------------------------------------------- /src/components/ErrorBoundary.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <slot></slot> 3 | </template> 4 | <script lang="ts"> 5 | import { defineComponent } from 'vue'; 6 | export default defineComponent({ 7 | props: { propagate: { type: Boolean, default: true } }, 8 | emits: ['error-captured'], 9 | errorCaptured(error) { 10 | this.$emit('error-captured', error); 11 | return this.propagate; 12 | }, 13 | }); 14 | </script> 15 | -------------------------------------------------------------------------------- /src/components/FeatherIcon.vue: -------------------------------------------------------------------------------- 1 | <script> 2 | import feather from 'feather-icons'; 3 | import { h } from 'vue'; 4 | 5 | const validIcons = Object.keys(feather.icons); 6 | 7 | export default { 8 | props: { 9 | name: { 10 | type: String, 11 | required: true, 12 | validator: (value) => validIcons.includes(value), 13 | }, 14 | }, 15 | render() { 16 | const icon = feather.icons[this.name]; 17 | const svg = h('svg', { 18 | ...Object.assign({}, icon.attrs, { 19 | fill: 'none', 20 | stroke: 'currentColor', 21 | 'stroke-linecap': 'round', 22 | 'stroke-linejoin': 'round', 23 | 'stroke-width': 1.5, 24 | width: null, 25 | height: null, 26 | }), 27 | class: [icon.attrs.class], 28 | innerHTML: icon.contents, 29 | }); 30 | 31 | return svg; 32 | }, 33 | }; 34 | </script> 35 | -------------------------------------------------------------------------------- /src/components/FormHeader.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div 3 | class=" 4 | px-4 5 | text-xl 6 | font-semibold 7 | flex 8 | justify-between 9 | h-row-large 10 | items-center 11 | flex-shrink-0 12 | " 13 | > 14 | <h1 v-if="formTitle" class="dark:text-gray-25">{{ formTitle }}</h1> 15 | <slot /> 16 | <p v-if="formSubTitle" class="text-gray-600 dark:text-gray-400"> 17 | {{ formSubTitle }} 18 | </p> 19 | </div> 20 | </template> 21 | <script lang="ts"> 22 | import { defineComponent } from 'vue'; 23 | 24 | export default defineComponent({ 25 | props: { 26 | formTitle: { type: String, default: '' }, 27 | formSubTitle: { type: String, default: '' }, 28 | }, 29 | }); 30 | </script> 31 | -------------------------------------------------------------------------------- /src/components/HowTo.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <button class="flex items-center z-10" @click="openHelpLink"> 3 | <p class="me-1"><slot></slot></p> 4 | <FeatherIcon 5 | v-if="icon" 6 | class="h-5 w-5 ms-3 text-blue-400" 7 | name="help-circle" 8 | /> 9 | </button> 10 | </template> 11 | <script> 12 | import FeatherIcon from './FeatherIcon.vue'; 13 | 14 | export default { 15 | components: { FeatherIcon }, 16 | props: { 17 | link: String, 18 | icon: { 19 | default: true, 20 | type: Boolean, 21 | }, 22 | }, 23 | methods: { 24 | openHelpLink() { 25 | ipc.openLink(this.link); 26 | }, 27 | }, 28 | }; 29 | </script> 30 | -------------------------------------------------------------------------------- /src/components/Icon.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <component 3 | v-bind="$attrs" 4 | :is="iconComponent" 5 | :class="iconClasses" 6 | :active="active" 7 | :darkMode="darkMode" 8 | /> 9 | </template> 10 | 11 | <script lang="ts"> 12 | import icons12 from './Icons/12'; 13 | import icons18 from './Icons/18'; 14 | import icons24 from './Icons/24'; 15 | import icons8 from './Icons/8'; 16 | 17 | const components = { 18 | 8: icons8, 19 | 12: icons12, 20 | 18: icons18, 21 | 24: icons24, 22 | } as const; 23 | 24 | type IconSize = '8' | '12' | '18' | '24'; 25 | export default { 26 | name: 'Icon', 27 | props: { 28 | name: { type: String, required: true }, 29 | active: { type: Boolean, default: false }, 30 | darkMode: { type: Boolean, default: false }, 31 | size: { 32 | type: String, 33 | required: true, 34 | }, 35 | height: Number, 36 | }, 37 | computed: { 38 | iconComponent() { 39 | const map = components[this.size as IconSize]; 40 | return map[this.name as keyof typeof map] ?? null; 41 | }, 42 | iconClasses() { 43 | let sizeClass = { 44 | 8: 'w-2 h-2', 45 | 12: 'w-3 h-3', 46 | 16: 'w-4 h-4', 47 | 18: 'w-5 h-5', 48 | 24: 'w-6 h-6', 49 | }[this.size]; 50 | 51 | if (this.height) { 52 | sizeClass = `w-${this.height} h-${this.height}`; 53 | } 54 | 55 | return [sizeClass, 'fill-current']; 56 | }, 57 | }, 58 | }; 59 | </script> 60 | -------------------------------------------------------------------------------- /src/components/Icons/12/arrow-left-right.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 11"> 3 | <path 4 | stroke-linecap="round" 5 | stroke-linejoin="round" 6 | d="M2.25 2.25L9 2.25 2.25 2.25 2.25 4.5 0 2.25 2.25 0 2.25 2.25zM8.75 7.875L2 7.875 8.75 7.875 8.75 5.625 11 7.875 8.75 10.125 8.75 7.875z" 7 | transform="translate(.5)" 8 | /> 9 | </svg> 10 | </template> 11 | -------------------------------------------------------------------------------- /src/components/Icons/12/drag-handle.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 12"> 3 | <path 4 | d="M3.375,3 C2.54657288,3 1.875,2.32842712 1.875,1.5 C1.875,0.671572875 2.54657288,0 3.375,0 C4.20342712,0 4.875,0.671572875 4.875,1.5 C4.875,2.32842712 4.20342712,3 3.375,3 Z M3.375,12 C2.54657288,12 1.875,11.3284271 1.875,10.5 C1.875,9.67157288 2.54657288,9 3.375,9 C4.20342712,9 4.875,9.67157288 4.875,10.5 C4.875,11.3284271 4.20342712,12 3.375,12 Z M3.375,7.5 C2.54657288,7.5 1.875,6.82842712 1.875,6 C1.875,5.17157288 2.54657288,4.5 3.375,4.5 C4.20342712,4.5 4.875,5.17157288 4.875,6 C4.875,6.82842712 4.20342712,7.5 3.375,7.5 Z M8.625,3 C7.79657288,3 7.125,2.32842712 7.125,1.5 C7.125,0.671572875 7.79657288,0 8.625,0 C9.45342712,0 10.125,0.671572875 10.125,1.5 C10.125,2.32842712 9.45342712,3 8.625,3 Z M8.625,12 C7.79657288,12 7.125,11.3284271 7.125,10.5 C7.125,9.67157288 7.79657288,9 8.625,9 C9.45342712,9 10.125,9.67157288 10.125,10.5 C10.125,11.3284271 9.45342712,12 8.625,12 Z M8.625,7.5 C7.79657288,7.5 7.125,6.82842712 7.125,6 C7.125,5.17157288 7.79657288,4.5 8.625,4.5 C9.45342712,4.5 10.125,5.17157288 10.125,6 C10.125,6.82842712 9.45342712,7.5 8.625,7.5 Z" 5 | transform="translate(-1)" 6 | /> 7 | </svg> 8 | </template> 9 | -------------------------------------------------------------------------------- /src/components/Icons/12/filter.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 10"> 3 | <path 4 | stroke-linecap="round" 5 | stroke-linejoin="round" 6 | d="M11.2 1.5L.8 1.5M3 5.75L9 5.75M5 10.5L7 10.5" 7 | transform="translate(0 -2)" 8 | /> 9 | </svg> 10 | </template> 11 | -------------------------------------------------------------------------------- /src/components/Icons/12/index.ts: -------------------------------------------------------------------------------- 1 | import ArrowLeftRight from './arrow-left-right.vue'; 2 | import DragHandle from './drag-handle.vue'; 3 | import Filter from './filter.vue'; 4 | import List from './list.vue'; 5 | import Select from './select.vue'; 6 | import Sidebar from './sidebar.vue'; 7 | 8 | // prettier-ignore 9 | export default { 10 | 'arrow-left-right': ArrowLeftRight, 11 | 'drag-handle': DragHandle, 12 | 'filter': Filter, 13 | 'list': List, 14 | 'select': Select, 15 | 'sidebar': Sidebar, 16 | }; 17 | -------------------------------------------------------------------------------- /src/components/Icons/12/list.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 10"> 3 | <path 4 | stroke-linecap="round" 5 | stroke-linejoin="round" 6 | d="M1.5,0.5 L10.5,0.5 C11.0522847,0.5 11.5,0.94771525 11.5,1.5 L11.5,8.5 C11.5,9.05228475 11.0522847,9.5 10.5,9.5 L1.5,9.5 C0.94771525,9.5 0.5,9.05228475 0.5,8.5 L0.5,1.5 C0.5,0.94771525 0.94771525,0.5 1.5,0.5 Z M7.5,0.5 L7.5,9.5" 7 | /> 8 | </svg> 9 | </template> 10 | -------------------------------------------------------------------------------- /src/components/Icons/12/select.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 10"> 3 | <path 4 | stroke-linecap="round" 5 | stroke-linejoin="round" 6 | d="M4,3.63636364 L5.63636364,2 L7.27272727,3.63636364 M4,8.36363636 L5.63636364,10 L7.27272727,8.36363636" 7 | transform="translate(-3 -1)" 8 | /> 9 | </svg> 10 | </template> 11 | -------------------------------------------------------------------------------- /src/components/Icons/12/sidebar.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 10"> 3 | <path 4 | stroke-linecap="round" 5 | stroke-linejoin="round" 6 | d="M1.5,0.5 L10.5,0.5 C11.0522847,0.5 11.5,0.94771525 11.5,1.5 L11.5,8.5 C11.5,9.05228475 11.0522847,9.5 10.5,9.5 L1.5,9.5 C0.94771525,9.5 0.5,9.05228475 0.5,8.5 L0.5,1.5 C0.5,0.94771525 0.94771525,0.5 1.5,0.5 Z" 7 | /> 8 | </svg> 9 | </template> 10 | -------------------------------------------------------------------------------- /src/components/Icons/16/account-in.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 12"> 3 | <g 4 | fill="none" 5 | fill-rule="evenodd" 6 | stroke-linecap="round" 7 | stroke-linejoin="round" 8 | transform="translate(0 1)" 9 | > 10 | <g transform="rotate(180 4.5 3.5)"> 11 | <polyline points="0 2 2 0 4 2" /> 12 | <path d="M2,0 L2,7" /> 13 | </g> 14 | <path 15 | d="M3.5,1.5 L1.5,1.5 C0.94771525,1.5 0.5,1.94771525 0.5,2.5 L0.5,9.5 C0.5,10.0522847 0.94771525,10.5 1.5,10.5 L12.5,10.5 C13.0522847,10.5 13.5,10.0522847 13.5,9.5 L13.5,2.5 C13.5,1.94771525 13.0522847,1.5 12.5,1.5 L10.5,1.5" 16 | /> 17 | </g> 18 | </svg> 19 | </template> 20 | -------------------------------------------------------------------------------- /src/components/Icons/16/address.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 16"> 3 | <path 4 | fill="#415668" 5 | d="M8.04916643,0 C4.12177371,0 1,3.09516381 1,6.98907956 C1,8.88611544 1.70491664,10.6833073 3.11474993,11.9812793 C3.21545231,12.0811232 7.24354741,15.675507 7.34424979,15.775351 C7.7470593,16.074883 8.35127356,16.074883 8.6533807,15.775351 C8.75408308,15.675507 12.8828806,12.0811232 12.8828806,11.9812793 C14.2927138,10.6833073 14.9976305,8.88611544 14.9976305,6.98907956 C15.0983329,3.09516381 11.9765592,0 8.04916643,0 Z M8,9 C6.9,9 6,8.1 6,7 C6,5.9 6.9,5 8,5 C9.1,5 10,5.9 10,7 C10,8.1 9.1,9 8,9 Z" 6 | transform="translate(-1)" 7 | /> 8 | </svg> 9 | </template> 10 | -------------------------------------------------------------------------------- /src/components/Icons/16/assets.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | fill="#415668" 6 | fill-rule="nonzero" 7 | d="M15.3333333,5.33333333 L0.666666667,5.33333333 C0.298666667,5.33333333 0,5.632 0,6 L0,15.3333333 C0,15.7013333 0.298666667,16 0.666666667,16 L15.3333333,16 C15.7013333,16 16,15.7013333 16,15.3333333 L16,6 C16,5.632 15.7013333,5.33333333 15.3333333,5.33333333 Z M8,12.6666667 C6.89533333,12.6666667 6,11.7713333 6,10.6666667 C6,9.562 6.89533333,8.66666667 8,8.66666667 C9.10466667,8.66666667 10,9.562 10,10.6666667 C10,11.7713333 9.10466667,12.6666667 8,12.6666667 Z" 8 | /> 9 | <path 10 | fill="#A1ABB4" 11 | d="M2,2.66666667 L14,2.66666667 L14,4 L2,4 L2,2.66666667 Z M4.66666667,1.51767487e-13 L11.3333333,1.51767487e-13 L11.3333333,1.33333333 L4.66666667,1.33333333 L4.66666667,1.51767487e-13 Z" 12 | /> 13 | </g> 14 | </svg> 15 | </template> 16 | -------------------------------------------------------------------------------- /src/components/Icons/16/calendar.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 12"> 3 | <g fill="none" fill-rule="evenodd" transform="translate(.5 -1.5)"> 4 | <path 5 | d="M.5 5.5L.5 10.4361148C.5 11.1734491.553979991 11.4529804.708039303 11.7410463.835746496 11.979838 1.02016204 12.1642535 1.25895373 12.2919607 1.54701959 12.44602 1.82655093 12.5 2.5638852 12.5L10.4361148 12.5C11.1734491 12.5 11.4529804 12.44602 11.7410463 12.2919607 11.979838 12.1642535 12.1642535 11.979838 12.2919607 11.7410463 12.44602 11.4529804 12.5 11.1734491 12.5 10.4361148L12.5 5.5.5 5.5zM.5 5.5L12.5 5.5 12.5 4.5638852C12.5 3.82655093 12.44602 3.54701959 12.2919607 3.25895373 12.1642535 3.02016204 11.979838 2.8357465 11.7410463 2.7080393 11.4529804 2.55397999 11.1734491 2.5 10.4361148 2.5L2.5638852 2.5C1.82655093 2.5 1.54701959 2.55397999 1.25895373 2.7080393 1.02016204 2.8357465.835746496 3.02016204.708039303 3.25895373.553979991 3.54701959.5 3.82655093.5 4.5638852L.5 5.5z" 6 | /> 7 | <path /> 8 | <path /> 9 | </g> 10 | </svg> 11 | </template> 12 | -------------------------------------------------------------------------------- /src/components/Icons/16/circle.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"> 3 | <circle cx="8" cy="8" r="3.5" fill="none" transform="translate(-4 -4)" /> 4 | </svg> 5 | </template> 6 | -------------------------------------------------------------------------------- /src/components/Icons/16/down-small.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 4"> 3 | <polyline 4 | fill="none" 5 | stroke-linecap="round" 6 | stroke-linejoin="round" 7 | points="4 6 8 10 12 6" 8 | transform="translate(-3 -6)" 9 | /> 10 | </svg> 11 | </template> 12 | -------------------------------------------------------------------------------- /src/components/Icons/16/down.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 9"> 3 | <polyline 4 | fill="none" 5 | stroke-linecap="round" 6 | stroke-linejoin="round" 7 | stroke-width="1.5" 8 | points="1 5 8 12 15 5" 9 | transform="translate(0 -4)" 10 | /> 11 | </svg> 12 | </template> 13 | -------------------------------------------------------------------------------- /src/components/Icons/16/expenses.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 16"> 3 | <path 4 | fill="#415668" 5 | d="M12.0016667,2 L14.6683333,0 L14.6683333,15.3333333 C14.6683333,15.702 14.3696667,16 14.0016667,16 L2.00166667,16 C1.63366667,16 1.335,15.702 1.335,15.3333333 L1.335,0 L4.00166667,2 L6.00166667,0 L8.00166667,2 L10.0016667,0 L12.0016667,2 Z M10.9599376,6.36932491 L9.74971145,6.36932491 C9.68579106,5.97727983 9.54090483,5.62358698 9.31931413,5.32529181 L10.6616424,5.33381453 L10.9641989,4.272736 L5.38607925,4.272736 L5.075,5.40625765 L6.69431664,5.40625765 C7.62755438,5.40625765 8.16874705,5.76847321 8.36476959,6.36932491 L5.37329517,6.36932491 L5.075,7.42188072 L8.39886047,7.42188072 C8.22414472,8.09517554 7.63181574,8.46591382 6.69431664,8.46591382 L5.18153399,8.46591382 L5.19005671,9.29687894 L8.24119016,13.0000004 L9.79658641,13.0000004 L9.79658641,12.9275573 L7.00965725,9.51420827 C8.76107603,9.40767428 9.58351843,8.57244781 9.75823417,7.42188072 L10.6616424,7.42188072 L10.9599376,6.36932491 Z" 6 | transform="translate(-1)" 7 | /> 8 | </svg> 9 | </template> 10 | -------------------------------------------------------------------------------- /src/components/Icons/16/index.ts: -------------------------------------------------------------------------------- 1 | import AccountIn from './account-in.vue'; 2 | import Address from './address.vue'; 3 | import Assets from './assets.vue'; 4 | import Calendar from './calendar.vue'; 5 | import Circle from './circle.vue'; 6 | import DownSmall from './down-small.vue'; 7 | import Down from './down.vue'; 8 | import Expenses from './expenses.vue'; 9 | import Income from './income.vue'; 10 | import Items from './items.vue'; 11 | import Liabilities from './liabilities.vue'; 12 | import Mail from './mail.vue'; 13 | import Normal from './normal.vue'; 14 | import Opened from './opened.vue'; 15 | import Phone from './phone.vue'; 16 | import Plus from './plus.vue'; 17 | import Search from './search.vue'; 18 | 19 | // prettier-ignore 20 | export default { 21 | 'account-in': AccountIn, 22 | 'address': Address, 23 | 'assets': Assets, 24 | 'calendar': Calendar, 25 | 'circle': Circle, 26 | 'down-small': DownSmall, 27 | 'down': Down, 28 | 'expenses': Expenses, 29 | 'income': Income, 30 | 'items': Items, 31 | 'liabilities': Liabilities, 32 | 'mail': Mail, 33 | 'normal': Normal, 34 | 'opened': Opened, 35 | 'phone': Phone, 36 | 'plus': Plus, 37 | 'search': Search, 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Icons/16/items.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | fill="#A1ABB4" 6 | d="M15.3333333,0.5 L0.666666667,0.5 C0.298666667,0.5 0,0.798666667 0,1.16666667 L0,3.83333333 C0,4.20133333 0.298666667,4.5 0.666666667,4.5 L15.3333333,4.5 C15.7013333,4.5 16,4.20133333 16,3.83333333 L16,1.16666667 C16,0.798666667 15.7013333,0.5 15.3333333,0.5 Z" 7 | /> 8 | <path 9 | fill="#415668" 10 | fill-rule="nonzero" 11 | d="M14.6666667,5.83333333 L1.33333333,5.83333333 L1.33333333,14.5 C1.33333333,14.868 1.632,15.1666667 2,15.1666667 L14,15.1666667 C14.368,15.1666667 14.6666667,14.868 14.6666667,14.5 L14.6666667,5.83333333 Z M10.6666667,11.1666667 L5.33333333,11.1666667 L5.33333333,8.5 L10.6666667,8.5 L10.6666667,11.1666667 Z" 12 | /> 13 | </g> 14 | </svg> 15 | </template> 16 | -------------------------------------------------------------------------------- /src/components/Icons/16/liabilities.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 3 | <g fill="none" fill-rule="evenodd" transform="translate(.665)"> 4 | <path 5 | fill="#415668" 6 | fill-rule="nonzero" 7 | d="M6.66666667,11.3593333 L11.3333333,7.626 L13.3333333,9.226 L13.3333333,0.666666667 C13.3333333,0.298476833 13.0348565,2.2545125e-17 12.6666667,0 L0.666666667,0 C0.298476833,-2.2545125e-17 3.0375702e-13,0.298476833 3.0375702e-13,0.666666667 L3.0375702e-13,15.3333333 C3.0375702e-13,15.7015232 0.298476833,16 0.666666667,16 L6.66666667,16 L6.66666667,11.3593333 Z M2.66666667,4 L10.6353333,4 L10.6353333,5.33333333 L2.66666667,5.33333333 L2.66666667,4 Z M5.33333333,12 L2.66666667,12 L2.66666667,10.6666667 L5.33333333,10.6666667 L5.33333333,12 Z M2.66666667,8.66666667 L2.66666667,7.33333333 L8,7.33333333 L8,8.66666667 L2.66666667,8.66666667 Z" 8 | /> 9 | <path 10 | fill="#A1ABB4" 11 | d="M14.6666667,12 L11.3333333,9.33333333 L8,12 L8,15.3333333 C8,15.7015232 8.29847683,16 8.66666667,16 L10.6666667,16 L10.6666667,14 L12,14 L12,16 L14,16 C14.3681898,16 14.6666667,15.7015232 14.6666667,15.3333333 L14.6666667,12 Z" 12 | /> 13 | </g> 14 | </svg> 15 | </template> 16 | -------------------------------------------------------------------------------- /src/components/Icons/16/mail.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 14"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | fill="#A1ABB4" 6 | d="M15,0 L1,0 C0.4,0 0,0.4 0,1 L0,2.4 L8,6.9 L16,2.5 L16,1 C16,0.4 15.6,0 15,0 Z" 7 | /> 8 | <path 9 | fill="#415668" 10 | fill-rule="nonzero" 11 | d="M7.5,8.9 L0,4.7 L0,13 C0,13.6 0.4,14 1,14 L15,14 C15.6,14 16,13.6 16,13 L16,4.7 L8.5,8.9 C8.22,9.04 7.78,9.04 7.5,8.9 Z" 12 | /> 13 | </g> 14 | </svg> 15 | </template> 16 | -------------------------------------------------------------------------------- /src/components/Icons/16/normal.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 3 | <path 4 | fill="#415668" 5 | d="M8.33333333,2.66666667 L6.33333333,0 L0.666666667,0 C0.298476833,0 4.50902501e-17,0.298476833 0,0.666666667 L0,12.6666667 C1.3527075e-16,13.7712362 0.8954305,14.6666667 2,14.6666667 L14,14.6666667 C15.1045695,14.6666667 16,13.7712362 16,12.6666667 L16,3.33333333 C16,2.9651435 15.7015232,2.66666667 15.3333333,2.66666667 L8.33333333,2.66666667 Z" 6 | transform="translate(0 .7)" 7 | /> 8 | </svg> 9 | </template> 10 | -------------------------------------------------------------------------------- /src/components/Icons/16/opened.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 3 | <path 4 | fill="#415668" 5 | d="M15.87,4.93733333 C15.7442388,4.76701257 15.5450526,4.66655343 15.3333333,4.66666667 L3.33333333,4.66666667 C3.04059338,4.66660971 2.78206554,4.85753089 2.696,5.13733333 L1.33333333,9.566 L1.33333333,1.33333333 L4.97666667,1.33333333 L6.11,3.03666667 C6.23407563,3.22263727 6.44310609,3.33403674 6.66666667,3.33333333 L12,3.33333333 L12,4 L13.3333333,4 L13.3333333,2.66666667 C13.3333333,2.29847683 13.0348565,2 12.6666667,2 L7.02333333,2 L5.89,0.296666667 C5.76592437,0.110696061 5.55689391,-0.000703403422 5.33333333,4.44088363e-16 L0.666666667,4.44088363e-16 C0.298476833,4.44088363e-16 4.50902501e-17,0.298476833 0,0.666666667 L0,14 L0.0166666667,14 C0.0107575763,14.1407672 0.0505118079,14.2796731 0.13,14.396 C0.255761209,14.5663208 0.454947439,14.6667799 0.666666667,14.6666667 L12.6666667,14.6666667 C12.9594066,14.6667236 13.2179345,14.4758024 13.304,14.196 L15.9706667,5.52933333 C16.0329159,5.32720357 15.995561,5.1075269 15.87,4.93733333 Z" 6 | transform="translate(0 .7)" 7 | /> 8 | </svg> 9 | </template> 10 | -------------------------------------------------------------------------------- /src/components/Icons/16/phone.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | fill="#415668" 6 | fill-rule="nonzero" 7 | d="M15.285,12.305 L12.707,9.711 C12.317,9.318 11.682,9.318 11.291,9.709 L9,12 L4,7 L6.294,4.706 C6.684,4.316 6.685,3.683 6.295,3.292 L3.715,0.708 C3.324,0.317 2.691,0.317 2.3,0.708 L0.004,3.003 L0,3 C0,10.18 5.82,16 13,16 L15.283,13.717 C15.673,13.327 15.674,12.696 15.285,12.305 Z" 8 | /> 9 | <path 10 | fill="#A1ABB4" 11 | d="M16,8 L14,8 C14,4.691 11.309,2 8,2 L8,0 C12.411,0 16,3.589 16,8 Z M12,8 L10,8 C10,6.897 9.103,6 8,6 L8,4 C10.206,4 12,5.794 12,8 Z" 12 | /> 13 | </g> 14 | </svg> 15 | </template> 16 | -------------------------------------------------------------------------------- /src/components/Icons/16/plus.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 3 | <path 4 | fill="#112B42" 5 | d="M15,7 L9,7 L9,1 C9,0.4 8.6,0 8,0 C7.4,0 7,0.4 7,1 L7,7 L1,7 C0.4,7 0,7.4 0,8 C0,8.6 0.4,9 1,9 L7,9 L7,15 C7,15.6 7.4,16 8,16 C8.6,16 9,15.6 9,15 L9,9 L15,9 C15.6,9 16,8.6 16,8 C16,7.4 15.6,7 15,7 Z" 6 | /> 7 | </svg> 8 | </template> 9 | -------------------------------------------------------------------------------- /src/components/Icons/16/search.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 15"> 3 | <path 4 | fill="none" 5 | stroke-linecap="round" 6 | stroke-linejoin="round" 7 | d="M7,13 C10.3137085,13 13,10.3137085 13,7 C13,3.6862915 10.3137085,1 7,1 C3.6862915,1 1,3.6862915 1,7 C1,10.3137085 3.6862915,13 7,13 Z M15,15 L11.242,11.242" 8 | /> 9 | </svg> 10 | </template> 11 | -------------------------------------------------------------------------------- /src/components/Icons/18/check.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | fill="#92D336" 6 | fill-rule="nonzero" 7 | d="M9,0 C4.02943725,-3.04359188e-16 6.08718376e-16,4.02943725 0,9 C-6.08718376e-16,13.9705627 4.02943725,18 9,18 C13.9705627,18 18,13.9705627 18,9 C17.985583,4.03542125 13.9645788,0.0144170383 9,0 Z" 8 | /> 9 | <polyline 10 | stroke="#FFF" 11 | stroke-linecap="round" 12 | stroke-linejoin="round" 13 | stroke-width="1.5" 14 | points="5 9.733 7.222 12.133 13 6" 15 | /> 16 | </g> 17 | </svg> 18 | </template> 19 | <script> 20 | import Base from '../base.vue'; 21 | export default { 22 | extends: Base, 23 | }; 24 | </script> 25 | -------------------------------------------------------------------------------- /src/components/Icons/18/common-entries.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg 3 | width="18" 4 | height="18" 5 | viewBox="0 0 18 18" 6 | fill="none" 7 | xmlns="http://www.w3.org/2000/svg" 8 | > 9 | <path 10 | fill-rule="evenodd" 11 | clip-rule="evenodd" 12 | d="M1.28577 0.728572C1.28577 0.326193 1.61196 0 2.01434 0H15.5572C15.9596 0 16.2858 0.326193 16.2858 0.728571V8.78571H12.1V12.5317H8.50592L12.9132 18H2.01434C1.61196 18 1.28577 17.6738 1.28577 17.2714V0.728572ZM13.5001 5.89286H4.71434V4.39286H13.5001V5.89286ZM4.71434 9.75H10.7143V8.25H4.71434V9.75ZM7.28577 13.6071H4.71434V12.1071H7.28577V13.6071Z" 13 | :fill="darkColor" 14 | /> 15 | <path 16 | d="M16.2858 13.7317V15.6992L14.4429 17.9857L11.0143 13.7317H13.3V9.98571H15.5857V13.7317H16.2858Z" 17 | :fill="darkColor" 18 | /> 19 | <path 20 | d="M13.3 13.7317V9.98572H14.4429H15.5857V13.7317H17.8714L14.4429 17.9857L11.0143 13.7317H13.3Z" 21 | :fill="lightColor" 22 | /> 23 | </svg> 24 | </template> 25 | <script> 26 | import Base from '../base.vue'; 27 | export default { 28 | name: 'IconPurchase', 29 | extends: Base, 30 | }; 31 | </script> 32 | -------------------------------------------------------------------------------- /src/components/Icons/18/customer.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> 3 | <path 4 | fill="#FED73A" 5 | fill-rule="evenodd" 6 | d="M13.8000002,0 L0.600000009,0 C0.268800004,0 0,0.268800004 0,0.600000009 L0,13.8000002 C0,14.1312002 0.268800004,14.4000002 0.600000009,14.4000002 L13.8000002,14.4000002 C14.1312002,14.4000002 14.4000002,14.1312002 14.4000002,13.8000002 L14.4000002,0.600000009 C14.4000002,0.268800004 14.1312002,0 13.8000002,0 Z M4.80000007,4.80000007 C4.80000007,3.47460005 5.87460009,2.40000004 7.20000011,2.40000004 C8.52540013,2.40000004 9.60000014,3.47460005 9.60000014,4.80000007 L9.60000014,5.40000008 C9.60000014,6.7254001 8.52540013,7.80000012 7.20000011,7.80000012 C5.87460009,7.80000012 4.80000007,6.7254001 4.80000007,5.40000008 L4.80000007,4.80000007 Z M10.8000002,12.6000002 L3.60000005,12.6000002 C3.26880005,12.6000002 3.00000004,12.3312002 3.00000004,12.0000002 C3.00000004,10.3434002 4.34340006,9.00000013 6.00000009,9.00000013 L8.40000013,9.00000013 C10.0566001,9.00000013 11.4000002,10.3434002 11.4000002,12.0000002 C11.4000002,12.3312002 11.1312002,12.6000002 10.8000002,12.6000002 Z" 7 | transform="translate(2 2)" 8 | /> 9 | </svg> 10 | </template> 11 | <script> 12 | import Base from '../base.vue'; 13 | export default { 14 | extends: Base, 15 | }; 16 | </script> 17 | -------------------------------------------------------------------------------- /src/components/Icons/18/general.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg 3 | width="18" 4 | height="18" 5 | viewBox="0 0 18 18" 6 | fill="none" 7 | xmlns="http://www.w3.org/2000/svg" 8 | > 9 | <path 10 | fill-rule="evenodd" 11 | clip-rule="evenodd" 12 | d="M6.81398 5.75342L5.67204 6.73249L3.9698 5.03025L3.00005 6L5.09165e-05 3L3.00005 0L6.00005 3L5.0303 3.96975L6.81398 5.75342ZM14.2051 10.455L17.2238 13.473C18.2596 14.5088 18.2596 16.1873 17.2238 17.223C16.1881 18.2588 14.5096 18.2588 13.4738 17.223L10.0853 13.8345L12.8161 10.4602C13.0441 10.4865 13.2713 10.5 13.5001 10.5C13.7386 10.5 13.9733 10.482 14.2051 10.455Z" 13 | :fill="lightColor" 14 | /> 15 | <path 16 | d="M15.2033 5.07825L12.9218 2.79675L15.3278 0.39075C14.7691 0.14175 14.1518 0 13.5001 0C11.0146 0 9.00005 2.0145 9.00005 4.5C9.00005 4.9455 9.0668 5.3745 9.18755 5.781L1.0958 12.3285C0.427551 12.9187 0.0285509 13.7678 0.00155092 14.658C-0.0261991 15.549 0.319551 16.4212 0.949551 17.0505C1.56155 17.6632 2.3753 18 3.2408 18C4.17005 18 5.05655 17.601 5.67155 16.9042L12.2191 8.8125C12.6256 8.93325 13.0546 9 13.5001 9C15.9856 9 18.0001 6.9855 18.0001 4.5C18.0001 3.84825 17.8583 3.231 17.6093 2.6715L15.2033 5.07825Z" 17 | :fill="lightColor" 18 | /> 19 | </svg> 20 | </template> 21 | <script> 22 | import Base from '../base.vue'; 23 | export default { 24 | name: 'IconGeneral', 25 | extends: Base, 26 | }; 27 | </script> 28 | -------------------------------------------------------------------------------- /src/components/Icons/18/inventory.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg 3 | width="20" 4 | height="20" 5 | viewBox="0 0 18 18" 6 | fill="none" 7 | xmlns="http://www.w3.org/2000/svg" 8 | > 9 | <path 10 | d="M7.4943 0.151836C7.88385 -0.0506121 8.33973 -0.0506121 8.72928 0.151836L17.0957 4.49986L14.7724 5.70728L5.78847 1.03835L7.4943 0.151836Z" 11 | :fill="lightColor" 12 | /> 13 | <path 14 | d="M9.88794 8.24572L0.904053 3.57679L4.11554 1.90778L13.0994 6.57671L9.88794 8.24572Z" 15 | :fill="lightColor" 16 | /> 17 | <path 18 | d="M10.6569 9.58498L17.9952 5.77127C17.9984 5.81193 18 5.85301 18 5.89441V12.453C18 12.9868 17.7304 13.4692 17.3093 13.7319L10.6569 17.8806L10.6569 9.58498Z" 19 | :fill="darkColor" 20 | /> 21 | <path 22 | d="M9.11923 9.58498L9.11923 18L0.78452 13.6684C0.313082 13.4234 0 12.9121 0 12.336V4.97134C0 4.92994 0.00161725 4.88887 0.00479439 4.84821L9.11923 9.58498Z" 23 | :fill="darkColor" 24 | /> 25 | </svg> 26 | </template> 27 | <script> 28 | import Base from '../base.vue'; 29 | export default { 30 | name: 'IconInventory', 31 | extends: Base, 32 | }; 33 | </script> 34 | -------------------------------------------------------------------------------- /src/components/Icons/18/invoice.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg 3 | width="18" 4 | height="18" 5 | viewBox="0 0 18 18" 6 | fill="none" 7 | xmlns="http://www.w3.org/2000/svg" 8 | > 9 | <path 10 | fill-rule="evenodd" 11 | clip-rule="evenodd" 12 | d="M2.475 6.3L0.3375 4.1625C-0.1125 3.7125 -0.1125 3.0375 0.3375 2.5875L2.5875 0.3375C3.0375 -0.1125 3.7125 -0.1125 4.1625 0.3375L6.3 2.475L2.475 6.3ZM15.525 11.7L17.6625 13.8375C17.8875 14.0625 18 14.2875 18 14.625V18H14.625C14.2875 18 14.0625 17.8875 13.8375 17.6625L11.7 15.525L15.525 11.7Z" 13 | :fill="lightColor" 14 | /> 15 | <path 16 | d="M17.6625 4.8375L13.1625 0.3375C12.7125 -0.1125 12.0375 -0.1125 11.5875 0.3375L10.125 1.8L12.0375 3.7125L10.4625 5.2875L8.55 3.375L6.75 5.175L8.6625 7.0875L7.0875 8.6625L5.175 6.75L3.375 8.55L5.2875 10.4625L3.7125 12.0375L1.8 10.125L0.3375 11.5875C-0.1125 12.0375 -0.1125 12.7125 0.3375 13.1625L4.8375 17.6625C5.2875 18.1125 5.9625 18.1125 6.4125 17.6625L17.6625 6.4125C18.1125 5.9625 18.1125 5.2875 17.6625 4.8375Z" 17 | :fill="lightColor" 18 | /> 19 | </svg> 20 | </template> 21 | <script> 22 | import Base from '../base.vue'; 23 | export default { 24 | name: 'IconInvoice', 25 | extends: Base, 26 | }; 27 | </script> 28 | -------------------------------------------------------------------------------- /src/components/Icons/18/item.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> 3 | <path 4 | fill="#F46181" 5 | d="M13.7999992,0 C14.1311992,0 14.3999991,0.268799984 14.3999991,0.599999964 L14.3999991,2.99999982 C14.3999991,3.3311998 14.1311992,3.59999979 13.7999992,3.59999979 L0.599999964,3.59999979 C0.268799984,3.59999979 0,3.3311998 0,2.99999982 L0,0.599999964 C0,0.268799984 0.268799984,0 0.599999964,0 L13.7999992,0 Z M13.1999992,4.79999971 L13.1999992,12.5999992 C13.1999992,12.9311992 12.9311992,13.1999992 12.5999992,13.1999992 L1.79999989,13.1999992 C1.46879991,13.1999992 1.19999993,12.9311992 1.19999993,12.5999992 L1.19999993,4.79999971 L13.1999992,4.79999971 Z M9.59999943,9.59999943 L9.59999943,7.19999957 L4.79999971,7.19999957 L4.79999971,9.59999943 L9.59999943,9.59999943 Z" 6 | transform="translate(2 2.2)" 7 | /> 8 | </svg> 9 | </template> 10 | <script> 11 | import Base from '../base.vue'; 12 | export default { 13 | extends: Base, 14 | }; 15 | </script> 16 | -------------------------------------------------------------------------------- /src/components/Icons/18/mail.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> 3 | <path 4 | fill="#6881DF" 5 | d="M10.05,9.45 L18,5.175 L18,12.75 C18,13.9926407 16.9926407,15 15.75,15 L2.25,15 C1.00735931,15 -8.42477746e-13,13.9926407 -8.42629924e-13,12.75 L-8.42629924e-13,5.175 L7.95,9.45 C8.25827686,9.66106884 8.62674743,9.76634614 9,9.75 C9.37325257,9.76634614 9.74172314,9.66106884 10.05,9.45 Z M15.75,-5.55111512e-16 C16.9926407,1.44328993e-15 18,1.00735931 18,2.25 L18,3 C18.0014575,3.27504919 17.8593023,3.53092845 17.625,3.675 L9.375,8.175 C9.25984373,8.23562752 9.12961637,8.261673 9,8.25 C8.87038363,8.261673 8.74015627,8.23562752 8.625,8.175 L0.375,3.675 C0.140697691,3.53092845 -0.00145745239,3.27504919 -1.08668614e-12,3 L-1.08668614e-12,2.25 C-1.08653396e-12,1.00735931 1.00735931,-8.8817842e-16 2.25,-5.55111512e-16 L15.75,-5.55111512e-16 Z" 6 | transform="translate(0 2)" 7 | /> 8 | </svg> 9 | </template> 10 | <script> 11 | import Base from '../base.vue'; 12 | export default { 13 | extends: Base, 14 | }; 15 | </script> 16 | -------------------------------------------------------------------------------- /src/components/Icons/18/opening-ac.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> 3 | <path 4 | fill="#FD9E64" 5 | d="M17.25,3 L3,3 L2.25,3 C1.83675,3 1.5,2.66325 1.5,2.25 C1.5,1.83675 1.83675,1.5 2.25,1.5 L13.5,1.5 L13.5,2.25 L15,2.25 L15,0.75 C15,0.336 14.664,0 14.25,0 L2.25,0 C1.00725,0 0,1.00725 0,2.25 L0,15 C0,16.65675 1.34325,18 3,18 L17.25,18 C17.664,18 18,17.664 18,17.25 L18,3.75 C18,3.336 17.664,3 17.25,3 Z M13.5,12 C12.67125,12 12,11.32875 12,10.5 C12,9.67125 12.67125,9 13.5,9 C14.32875,9 15,9.67125 15,10.5 C15,11.32875 14.32875,12 13.5,12 Z" 6 | /> 7 | </svg> 8 | </template> 9 | <script> 10 | import Base from '../base.vue'; 11 | export default { 12 | extends: Base, 13 | }; 14 | </script> 15 | -------------------------------------------------------------------------------- /src/components/Icons/18/percentage.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> 3 | <path 4 | fill="#D279E6" 5 | fill-rule="evenodd" 6 | d="M9,0 C4.03725,0 0,4.03725 0,9 C0,13.96275 4.03725,18 9,18 C13.96275,18 18,13.96275 18,9 C18,4.03725 13.96275,0 9,0 Z M4.5,6 C4.5,5.17125 5.17125,4.5 6,4.5 C6.82875,4.5 7.5,5.17125 7.5,6 C7.5,6.82875 6.82875,7.5 6,7.5 C5.17125,7.5 4.5,6.82875 4.5,6 Z M6,13.0605 L4.9395,12 L12,4.9395 L13.0605,6 L6,13.0605 Z M12,13.5 C11.17125,13.5 10.5,12.82875 10.5,12 C10.5,11.17125 11.17125,10.5 12,10.5 C12.82875,10.5 13.5,11.17125 13.5,12 C13.5,12.82875 12.82875,13.5 12,13.5 Z" 7 | /> 8 | </svg> 9 | </template> 10 | <script> 11 | import Base from '../base.vue'; 12 | export default { 13 | extends: Base, 14 | }; 15 | </script> 16 | -------------------------------------------------------------------------------- /src/components/Icons/18/pos.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> 3 | <path fill="none" d="M0 0h24v24H0z"></path> 4 | <path 5 | :fill="darkColor" 6 | d="M21 13V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V13H2V11L3 6H21L22 11V13H21ZM5 13V19H19V13H5ZM6 14H14V17H6V14ZM3 3H21V5H3V3Z" 7 | ></path> 8 | </svg> 9 | </template> 10 | <script> 11 | import Base from '../base.vue'; 12 | export default { 13 | extends: Base, 14 | }; 15 | </script> 16 | -------------------------------------------------------------------------------- /src/components/Icons/18/property.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | fill="#415668" 6 | fill-rule="nonzero" 7 | d="M15.5246667,2.028 L8.858,0.028 C8.65616136,-0.0324717007 8.43761545,0.00603303671 8.2685973,0.131844525 C8.09957915,0.257656014 7.99998537,0.455963879 8,0.666666667 L8,4 L9.33333333,4 L9.33333333,1.56266667 L14.6666667,3.16266667 L14.6666667,14.6666667 L10.6666667,14.6666667 L10.6666667,6 C10.6666667,5.63181017 10.3681898,5.33333333 10,5.33333333 L3.33333333,5.33333333 C2.9651435,5.33333333 2.66666667,5.63181017 2.66666667,6 L2.66666667,14.6666667 L1.33333333,14.6666667 L1.33333333,8 L0.666666667,8 C0.298476833,8 4.50902501e-17,8.29847683 0,8.66666667 L0,15.3333333 C4.50902501e-17,15.7015232 0.298476833,16 0.666666667,16 L15.3333333,16 C15.7015232,16 16,15.7015232 16,15.3333333 L16,2.66666667 C16.0000204,2.3721545 15.8067889,2.11252499 15.5246667,2.028 Z M8,14 L5.33333333,14 L5.33333333,12.6666667 L8,12.6666667 L8,14 Z M8,11.3333333 L5.33333333,11.3333333 L5.33333333,10 L8,10 L8,11.3333333 Z M8,8.66666667 L5.33333333,8.66666667 L5.33333333,7.33333333 L8,7.33333333 L8,8.66666667 Z" 8 | /> 9 | <rect width="1.333" height="8" x="12" y="5.333" fill="#A1ABB4" /> 10 | </g> 11 | </svg> 12 | </template> 13 | -------------------------------------------------------------------------------- /src/components/Icons/18/purchase-invoice.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> 3 | <path 4 | fill="#4ADAFC" 5 | d="M14.25,0 L0.75,0 C0.336,0 0,0.33525 0,0.75 L0,18 L3,15.75 L5.25,18 L7.5,15.75 L9.75,18 L12,15.75 L15,18 L15,0.75 C15,0.33525 14.664,0 14.25,0 Z M8.25,12 L3,12 L3,10.5 L8.25,10.5 L8.25,12 Z M8.25,9 L3,9 L3,7.5 L8.25,7.5 L8.25,9 Z M8.25,6 L3,6 L3,4.5 L8.25,4.5 L8.25,6 Z M12,12 L9.75,12 L9.75,10.5 L12,10.5 L12,12 Z M12,9 L9.75,9 L9.75,7.5 L12,7.5 L12,9 Z M12,6 L9.75,6 L9.75,4.5 L12,4.5 L12,6 Z" 6 | transform="translate(1.5)" 7 | /> 8 | </svg> 9 | </template> 10 | <script> 11 | import Base from '../base.vue'; 12 | export default { 13 | extends: Base, 14 | }; 15 | </script> 16 | -------------------------------------------------------------------------------- /src/components/Icons/18/purchase.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 18"> 3 | <g fill="none" fill-rule="evenodd" transform="translate(0 .5)"> 4 | <path 5 | :fill="darkColor" 6 | fill-rule="nonzero" 7 | d="M0,4.3972168 L16,4.3972168 L16,15 C16,16.1045695 15.1045695,17 14,17 L2,17 C0.8954305,17 1.3527075e-16,16.1045695 0,15 L0,4.3972168 Z M8,12.2222222 C10.4,12.2222222 12.3636364,10.2722222 12.3636364,7.88888889 C12.3636364,7.45555556 12.0727273,7.16666667 11.6363636,7.16666667 C11.2,7.16666667 10.9090909,7.45555556 10.9090909,7.88888889 C10.9090909,9.47777778 9.6,10.7777778 8,10.7777778 C6.4,10.7777778 5.09090909,9.47777778 5.09090909,7.88888889 C5.09090909,7.45555556 4.8,7.16666667 4.36363636,7.16666667 C3.92727273,7.16666667 3.63636364,7.45555556 3.63636364,7.88888889 C3.63636364,10.2722222 5.6,12.2222222 8,12.2222222 Z" 8 | /> 9 | <path 10 | :fill="lightColor" 11 | d="M0,3 L1.49222874,1.12925513 C2.06146543,0.415626854 2.92465315,1.0558663e-15 3.83750348,8.8817842e-16 L12.1683182,-4.4408921e-16 C13.0831751,-6.12145698e-16 13.9480333,0.417447486 14.5171563,1.13373106 L16,3 L0,3 Z" 12 | /> 13 | </g> 14 | </svg> 15 | </template> 16 | <script> 17 | import Base from '../base.vue'; 18 | export default { 19 | name: 'IconPurchase', 20 | extends: Base, 21 | }; 22 | </script> 23 | -------------------------------------------------------------------------------- /src/components/Icons/18/reports.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 16"> 3 | <g fill-rule="evenodd"> 4 | <path 5 | :fill="lightColor" 6 | d="M16.2857143,2.5 L16.2857143,7.26902668 L11.9220779,7.26902668 C11.7032818,7.26906827 11.4982329,7.36737803 11.3613275,7.53318731 L11.2988052,7.62157447 L9.87116883,10 L7.50680519,4.09173512 C7.40433373,3.83523222 7.16527403,3.6589327 6.88983266,3.63673596 C6.64882146,3.61731381 6.41639286,3.71881295 6.26674592,3.90311574 L6.2078961,3.98706113 L4.23771429,7.26902668 L0.285714286,7.26902668 L0.285714286,2.5 C0.285714286,1.11928813 1.40500241,2.53632657e-16 2.78571429,0 L13.7857143,0 C15.1664262,-1.47995115e-15 16.2857143,1.11928813 16.2857143,2.5 Z" 7 | /> 8 | <path 9 | :fill="darkColor" 10 | d="M0.285714286,13.5 L0.285714286,8.73097332 L4.64935065,8.73097332 C4.86814675,8.73093173 5.07319563,8.63262197 5.2101011,8.46681269 L5.27262338,8.37842553 L6.70025974,6 L9.06462338,11.9104456 C9.15434161,12.1350283 9.34876191,12.2981168 9.58061609,12.3501369 L9.68207792,12.3654867 L9.74025974,12.3654867 C9.95905584,12.3654451 10.1641047,12.2671353 10.3010102,12.101326 L10.3635325,12.0129389 L12.3337143,8.73097332 L16.2857143,8.73097332 L16.2857143,13.5 C16.2857143,14.8807119 15.1664262,16 13.7857143,16 L2.78571429,16 C1.40500241,16 0.285714286,14.8807119 0.285714286,13.5 Z" 11 | /> 12 | </g> 13 | </svg> 14 | </template> 15 | <script> 16 | import Base from '../base.vue'; 17 | export default { 18 | name: 'IconReports', 19 | extends: Base, 20 | }; 21 | </script> 22 | -------------------------------------------------------------------------------- /src/components/Icons/18/review-ac.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> 3 | <path 4 | fill="#126FE1" 5 | d="M7.125,9.75 C6.08946609,9.75 5.25,8.91053391 5.25,7.875 C5.25,6.83946609 6.08946609,6 7.125,6 C8.16053391,6 9,6.83946609 9,7.875 C9,8.91053391 8.16053391,9.75 7.125,9.75 Z M14.25,0 C14.6642136,2.53632657e-17 15,0.335786438 15,0.75 L15,17.25 C15,17.6642136 14.6642136,18 14.25,18 L0.75,18 C0.335786438,18 0,17.6642136 0,17.25 L0,0.75 C0,0.335786438 0.335786438,-2.53632657e-17 0.75,0 L14.25,0 Z M11.25,13.0605 L12.3105,12 L9.975,9.6645 C10.3162196,9.12988339 10.4983068,8.50922619 10.5,7.875 C10.5,6.01103897 8.98896103,4.5 7.125,4.5 C5.26103897,4.5 3.75,6.01103897 3.75,7.875 C3.75,9.73896103 5.26103897,11.25 7.125,11.25 C7.75922619,11.2483068 8.37988339,11.0662196 8.9145,10.725 L11.25,13.0605 Z" 6 | transform="translate(1.5)" 7 | /> 8 | </svg> 9 | </template> 10 | <script> 11 | import Base from '../base.vue'; 12 | export default { 13 | extends: Base, 14 | }; 15 | </script> 16 | -------------------------------------------------------------------------------- /src/components/Icons/18/sales-invoice.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> 3 | <path 4 | fill="#36D3B1" 5 | d="M14.25,0 L0.75,0 C0.335786438,-2.53632657e-17 0,0.335786438 0,0.75 L0,17.901 L3,15.9 L5.25,17.4 L7.5,15.9 L9.75,17.4 L12,15.9 L15,17.8995 L15,0.75 C15,0.335786438 14.6642136,2.53632657e-17 14.25,0 Z M8.25,11.25 L3,11.25 L3,9.75 L8.25,9.75 L8.25,11.25 Z M7.5,7.5 C6.25735931,7.5 5.25,6.49264069 5.25,5.25 C5.25,4.00735931 6.25735931,3 7.5,3 C8.74264069,3 9.75,4.00735931 9.75,5.25 C9.75,5.8467371 9.51294711,6.41903341 9.09099026,6.84099026 C8.66903341,7.26294711 8.0967371,7.5 7.5,7.5 Z M12,11.25 L9.75,11.25 L9.75,9.75 L12,9.75 L12,11.25 Z" 6 | transform="translate(1.5)" 7 | /> 8 | </svg> 9 | </template> 10 | <script> 11 | import Base from '../base.vue'; 12 | export default { 13 | extends: Base, 14 | }; 15 | </script> 16 | -------------------------------------------------------------------------------- /src/components/Icons/18/sales.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 14"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | :fill="darkColor" 6 | d="M0,5.11230469 L16,5.11230469 L16,12 C16,13.1045695 15.1045695,14 14,14 L2,14 C0.8954305,14 1.3527075e-16,13.1045695 0,12 L0,5.11230469 Z M8.5,10 C8.22385763,10 8,10.2238576 8,10.5 C8,10.7761424 8.22385763,11 8.5,11 L12.5,11 C12.7761424,11 13,10.7761424 13,10.5 C13,10.2238576 12.7761424,10 12.5,10 L8.5,10 Z" 7 | /> 8 | <path 9 | :fill="lightColor" 10 | d="M2,0 L14,0 C15.1045695,-2.02906125e-16 16,0.8954305 16,2 L16,3.64526367 L0,3.64526367 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z" 11 | /> 12 | </g> 13 | </svg> 14 | </template> 15 | <script> 16 | import Base from '../base.vue'; 17 | export default { 18 | name: 'IconSales', 19 | extends: Base, 20 | }; 21 | </script> 22 | -------------------------------------------------------------------------------- /src/components/Icons/18/settings.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 18"> 3 | <g fill="none" fill-rule="evenodd" transform="translate(0 .5)"> 4 | <path 5 | :fill="lightColor" 6 | d="M4,8 L12,8 C14.1818182,8 16,6.18181818 16,4 C16,1.81818182 14.1818182,0 12,0 L4,0 C1.81818182,0 0,1.81818182 0,4 C0,6.18181818 1.81818182,8 4,8 Z M4,1.45454545 C5.38181818,1.45454545 6.54545455,2.61818182 6.54545455,4 C6.54545455,5.38181818 5.38181818,6.54545455 4,6.54545455 C2.61818182,6.54545455 1.45454545,5.38181818 1.45454545,4 C1.45454545,2.61818182 2.61818182,1.45454545 4,1.45454545 Z" 7 | /> 8 | <path 9 | :fill="darkColor" 10 | d="M12,9 L4,9 C1.81818182,9 0,10.8181818 0,13 C0,15.1818182 1.81818182,17 4,17 L12,17 C14.1818182,17 16,15.1818182 16,13 C16,10.8181818 14.1818182,9 12,9 Z M12,15.5454545 C10.6181818,15.5454545 9.45454545,14.3818182 9.45454545,13 C9.45454545,11.6181818 10.6181818,10.4545455 12,10.4545455 C13.3818182,10.4545455 14.5454545,11.6181818 14.5454545,13 C14.5454545,14.3818182 13.3818182,15.5454545 12,15.5454545 Z" 11 | /> 12 | </g> 13 | </svg> 14 | </template> 15 | <script> 16 | import Base from '../base.vue'; 17 | export default { 18 | name: 'IconSettings', 19 | extends: Base, 20 | }; 21 | </script> 22 | -------------------------------------------------------------------------------- /src/components/Icons/18/supplier.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> 3 | <path 4 | fill="#8F6FD8" 5 | d="M5.40000008,7.20000011 C3.74580006,7.20000011 2.40000004,5.85420009 2.40000004,4.20000006 L2.40000004,3.00000004 C2.40000004,1.34580002 3.74580006,0 5.40000008,0 C7.05420011,0 8.40000013,1.34580002 8.40000013,3.00000004 L8.40000013,4.20000006 C8.40000013,5.85420009 7.05420011,7.20000011 5.40000008,7.20000011 Z M9.25380014,9.08700014 C10.1790002,9.43740014 10.8000002,10.3398002 10.8000002,11.3316002 L10.8000002,14.4000002 L0,14.4000002 L0,11.3316002 C0,10.3398002 0.621000009,9.43740014 1.54560002,9.08760014 C2.37480004,8.77320013 3.71640006,8.40000013 5.40000008,8.40000013 C7.08360011,8.40000013 8.42520013,8.77320013 9.25380014,9.08700014 Z M9.60000014,1.80000003 L14.4000002,1.80000003 L14.4000002,3.00000004 L9.60000014,3.00000004 L9.60000014,1.80000003 Z M9.60000014,4.80000007 L14.4000002,4.80000007 L14.4000002,6.00000009 L9.60000014,6.00000009 L9.60000014,4.80000007 Z M11.4000002,7.80000012 L14.4000002,7.80000012 L14.4000002,9.00000013 L11.4000002,9.00000013 L11.4000002,7.80000012 Z" 6 | transform="translate(2 2)" 7 | /> 8 | </svg> 9 | </template> 10 | <script> 11 | import Base from '../base.vue'; 12 | export default { 13 | extends: Base, 14 | }; 15 | </script> 16 | -------------------------------------------------------------------------------- /src/components/Icons/24/general.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | :fill="lightColor" 6 | d="M9.08530087,7.67123242 L7.56272248,8.97665406 L5.29306789,6.707 L4.00006789,8 L6.78885887e-05,4 L4.00006789,-3.55351234e-12 L8.00006789,4 L6.70706789,5.293 L9.08530087,7.67123242 Z M18.9400679,13.94 L22.9650679,17.964 C24.3460679,19.345 24.3460679,21.583 22.9650679,22.964 C21.5840679,24.345 19.3460679,24.345 17.9650679,22.964 L13.4470679,18.446 L17.0880679,13.947 C17.3920679,13.982 17.6950679,14 18.0000679,14 C18.3180679,14 18.6310679,13.976 18.9400679,13.94 Z" 7 | /> 8 | <path 9 | :fill="darkColor" 10 | fill-rule="nonzero" 11 | d="M20.2710679,6.771 L17.2290679,3.729 L20.4370679,0.521 C19.6920679,0.189 18.8690679,-1.77635684e-14 18.0000679,-1.77635684e-14 C14.6860679,-1.77635684e-14 12.0000679,2.686 12.0000679,6 C12.0000679,6.594 12.0890679,7.166 12.2500679,7.708 L1.46106789,16.438 C0.570067889,17.225 0.0380678886,18.357 0.00206788859,19.544 C-0.0349321114,20.732 0.426067889,21.895 1.26606789,22.734 C2.08206789,23.551 3.16706789,24 4.32106789,24 C5.56006789,24 6.74206789,23.468 7.56206789,22.539 L16.2920679,11.75 C16.8340679,11.911 17.4060679,12 18.0000679,12 C21.3140679,12 24.0000679,9.314 24.0000679,6 C24.0000679,5.131 23.8110679,4.308 23.4790679,3.562 L20.2710679,6.771 Z" 12 | /> 13 | </g> 14 | </svg> 15 | </template> 16 | <script> 17 | import Base from '../base.vue'; 18 | export default { 19 | name: 'IconGeneral', 20 | extends: Base, 21 | }; 22 | </script> 23 | -------------------------------------------------------------------------------- /src/components/Icons/24/green-check.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12C23.98 5.38 18.62.02 12 0z" 6 | fill="#92D336" 7 | fill-rule="nonzero" 8 | /> 9 | <path 10 | stroke="#FFF" 11 | stroke-width="1.5" 12 | stroke-linecap="round" 13 | stroke-linejoin="round" 14 | d="M8 12.756l2.222 2.4L16 9.022" 15 | /> 16 | </g> 17 | </svg> 18 | </template> 19 | <script> 20 | import Base from '../base.vue'; 21 | export default { 22 | name: 'IconGreenCheck', 23 | extends: Base, 24 | }; 25 | </script> 26 | -------------------------------------------------------------------------------- /src/components/Icons/24/index.ts: -------------------------------------------------------------------------------- 1 | import General from './general.vue'; 2 | import GreenCheck from './green-check.vue'; 3 | import Invoice from './invoice.vue'; 4 | import Mail from './mail.vue'; 5 | import Privacy from './privacy.vue'; 6 | import System from './system.vue'; 7 | 8 | // prettier-ignore 9 | export default { 10 | 'general': General, 11 | 'green-check': GreenCheck, 12 | 'invoice': Invoice, 13 | 'mail': Mail, 14 | 'privacy': Privacy, 15 | 'system': System, 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Icons/24/invoice.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | :fill="lightColor" 6 | d="M3.3,8.4 L0.45,5.55 C-0.15,4.95 -0.15,4.05 0.45,3.45 L3.45,0.45 C4.05,-0.15 4.95,-0.15 5.55,0.45 L8.4,3.3 L3.3,8.4 Z M20.7,15.6 L23.55,18.45 C23.85,18.75 24,19.05 24,19.5 L24,24 L19.5,24 C19.05,24 18.75,23.85 18.45,23.55 L15.6,20.7 L20.7,15.6 Z" 7 | /> 8 | <path 9 | :fill="darkColor" 10 | fill-rule="nonzero" 11 | d="M23.55,6.45 L17.55,0.45 C16.95,-0.15 16.05,-0.15 15.45,0.45 L13.5,2.4 L16.05,4.95 L13.95,7.05 L11.4,4.5 L9,6.9 L11.55,9.45 L9.45,11.55 L6.9,9 L4.5,11.4 L7.05,13.95 L4.95,16.05 L2.4,13.5 L0.45,15.45 C-0.15,16.05 -0.15,16.95 0.45,17.55 L6.45,23.55 C7.05,24.15 7.95,24.15 8.55,23.55 L23.55,8.55 C24.15,7.95 24.15,7.05 23.55,6.45 Z" 12 | /> 13 | </g> 14 | </svg> 15 | </template> 16 | <script> 17 | import Base from '../base.vue'; 18 | export default { 19 | name: 'IconInvoice', 20 | extends: Base, 21 | }; 22 | </script> 23 | -------------------------------------------------------------------------------- /src/components/Icons/24/mail.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 20"> 3 | <g fill="none" fill-rule="evenodd"> 4 | <path 5 | :fill="lightColor" 6 | fill-rule="nonzero" 7 | d="M13.4,12.6 C12.9889642,12.8814251 12.4976701,13.0217949 12,13 C11.5023299,13.0217949 11.0110358,12.8814251 10.6,12.6 L0,6.9 L0,17 C2.02906125e-16,18.6568542 1.34314575,20 3,20 L21,20 C22.6568542,20 24,18.6568542 24,17 L24,6.9 L13.4,12.6 Z" 8 | /> 9 | <path 10 | :fill="darkColor" 11 | d="M21,-4.4408921e-16 L3,-4.4408921e-16 C1.34314575,-8.8817842e-16 4.60042228e-16,1.34314575 2.57135486e-16,3 L2.57135486e-16,4 C-0.00194326986,4.36673226 0.187596922,4.7079046 0.5,4.9 L11.5,10.9 C11.6535417,10.9808367 11.8271782,11.015564 12,11 C12.1728218,11.015564 12.3464583,10.9808367 12.5,10.9 L23.5,4.9 C23.8124031,4.7079046 24.0019433,4.36673226 24,4 L24,3 C24,1.34314575 22.6568542,2.22044605e-15 21,-4.4408921e-16 Z" 12 | /> 13 | </g> 14 | </svg> 15 | </template> 16 | <script> 17 | import Base from '../base.vue'; 18 | export default { 19 | name: 'IconMail', 20 | extends: Base, 21 | }; 22 | </script> 23 | -------------------------------------------------------------------------------- /src/components/Icons/24/privacy.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 24"> 3 | <path 4 | :fill="darkColor" 5 | d="M20.0525217,2.11095652 C20.5302337,2.21715027 20.8699706,2.64106207 20.8695652,3.13043478 L20.8695652,13.5652174 C20.8695652,19.3281887 16.1977539,24 10.4347826,24 C4.67181131,24 2.30018902e-12,19.3281887 2.29787204e-12,13.5652174 L2.29787204e-12,3.13043478 C-0.000405357328,2.64106207 0.339331532,2.21715027 0.817043478,2.11095652 L10.2083478,0.024 C10.3575704,-0.00835725383 10.5119948,-0.00835725383 10.6612174,0.024 L20.0525217,2.11095652 Z M15.6521739,16.6956522 L15.6521739,11.4782609 C15.6521739,10.9019637 15.1849928,10.4347826 14.6086957,10.4347826 L13.5652174,10.4347826 L13.5652174,8.34782609 C13.5652174,6.6189347 12.163674,5.2173913 10.4347826,5.2173913 C8.70589122,5.2173913 7.30434783,6.6189347 7.30434783,8.34782609 L7.30434783,10.4347826 L6.26086957,10.4347826 C5.68457243,10.4347826 5.2173913,10.9019637 5.2173913,11.4782609 L5.2173913,16.6956522 C5.2173913,17.2719493 5.68457243,17.7391304 6.26086957,17.7391304 L14.6086957,17.7391304 C15.1849928,17.7391304 15.6521739,17.2719493 15.6521739,16.6956522 Z M10.4347826,7.30434783 C11.0110797,7.30434783 11.4782609,7.77152896 11.4782609,8.34782609 L11.4782609,10.4347826 L9.39130435,10.4347826 L9.39130435,8.34782609 C9.39130435,7.77152896 9.85848548,7.30434783 10.4347826,7.30434783 Z" 6 | transform="translate(.565)" 7 | /> 8 | </svg> 9 | </template> 10 | <script> 11 | import Base from '../base.vue'; 12 | export default { 13 | name: 'IconPrivacy', 14 | extends: Base, 15 | }; 16 | </script> 17 | -------------------------------------------------------------------------------- /src/components/Icons/8/arrow-right.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 7"> 3 | <g fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"> 4 | <path d="M6.8 4.002L.8 4M4.6 1.335l2.667 2.667L4.6 6.668" /> 5 | </g> 6 | </svg> 7 | </template> 8 | -------------------------------------------------------------------------------- /src/components/Icons/8/chevron-left.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 10"> 3 | <path 4 | d="M4.75 8.5L1.25 5l3.5-3.5" 5 | stroke-width="1.5" 6 | fill-rule="evenodd" 7 | stroke-linecap="round" 8 | stroke-linejoin="round" 9 | /> 10 | </svg> 11 | </template> 12 | -------------------------------------------------------------------------------- /src/components/Icons/8/chevron-right.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 8"> 3 | <path 4 | d="M1.25 7.75l3.5-3.5-3.5-3.5" 5 | fill-rule="evenodd" 6 | stroke-linecap="round" 7 | stroke-linejoin="round" 8 | /> 9 | </svg> 10 | </template> 11 | -------------------------------------------------------------------------------- /src/components/Icons/8/circle.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"> 3 | <circle cx="4" cy="4" r="3.5" fill-rule="evenodd" /> 4 | </svg> 5 | </template> 6 | -------------------------------------------------------------------------------- /src/components/Icons/8/dot-horizontal.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 2"> 3 | <path 4 | d="M1.633 1.033a.833.833 0 11-1.666 0 .833.833 0 011.666 0zm3.2 0a.833.833 0 11-1.666 0 .833.833 0 011.666 0zm3.2 0a.833.833 0 11-1.666 0 .833.833 0 011.666 0z" 5 | fill-rule="nonzero" 6 | /> 7 | </svg> 8 | </template> 9 | -------------------------------------------------------------------------------- /src/components/Icons/8/dot-vertical.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 9"> 3 | <path 4 | d="M1 6.4a.833.833 0 110 1.667A.833.833 0 011 6.4zm0-3.2a.833.833 0 110 1.667A.833.833 0 011 3.2zM1 0a.833.833 0 110 1.667A.833.833 0 011 0z" 5 | fill-rule="nonzero" 6 | /> 7 | </svg> 8 | </template> 9 | -------------------------------------------------------------------------------- /src/components/Icons/8/index.ts: -------------------------------------------------------------------------------- 1 | import ArrowRight from './arrow-right.vue'; 2 | import ChevronLeft from './chevron-left.vue'; 3 | import ChevronRight from './chevron-right.vue'; 4 | import Circle from './circle.vue'; 5 | import DotHorizontal from './dot-horizontal.vue'; 6 | import DotVertical from './dot-vertical.vue'; 7 | import Pencil from './pencil.vue'; 8 | import Plus from './plus.vue'; 9 | import Up from './up.vue'; 10 | import X from './x.vue'; 11 | 12 | // prettier-ignore 13 | export default { 14 | 'arrow-right': ArrowRight, 15 | 'chevron-left': ChevronLeft, 16 | 'chevron-right': ChevronRight, 17 | 'circle': Circle, 18 | 'dot-horizontal': DotHorizontal, 19 | 'dot-vertical': DotVertical, 20 | 'pencil': Pencil, 21 | 'plus': Plus, 22 | 'up': Up, 23 | 'x': X, 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Icons/8/pencil.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"> 3 | <path 4 | d="M7 1.4L8.6 3 3.8 7.8l-2.4.8.8-2.4z" 5 | fill-rule="evenodd" 6 | stroke-linecap="round" 7 | stroke-linejoin="round" 8 | /> 9 | </svg> 10 | </template> 11 | -------------------------------------------------------------------------------- /src/components/Icons/8/plus.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"> 3 | <path 4 | d="M4 .8v6.4M7.2 4H.8" 5 | fill-rule="evenodd" 6 | stroke-linecap="round" 7 | stroke-linejoin="round" 8 | /> 9 | </svg> 10 | </template> 11 | -------------------------------------------------------------------------------- /src/components/Icons/8/up.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 8"> 3 | <g fill-rule="nonzero"> 4 | <path d="M0 3h6L3 0z" /> 5 | <path opacity=".5" d="M3 8l3-3H0z" /> 6 | </g> 7 | </svg> 8 | </template> 9 | -------------------------------------------------------------------------------- /src/components/Icons/8/x.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"> 3 | <path 4 | d="M7.2.8L.8 7.2m6.4 0L.8.8" 5 | fill-rule="evenodd" 6 | stroke-linecap="round" 7 | stroke-linejoin="round" 8 | /> 9 | </svg> 10 | </template> 11 | -------------------------------------------------------------------------------- /src/components/Icons/base.vue: -------------------------------------------------------------------------------- 1 | <script lang="ts"> 2 | import { uicolors } from 'src/utils/colors'; 3 | 4 | export default { 5 | name: 'IconBase', 6 | props: { 7 | active: Boolean, 8 | darkMode: { type: Boolean, default: false }, 9 | }, 10 | computed: { 11 | lightColor(): string { 12 | const activeGray = this.darkMode 13 | ? uicolors.gray['500'] 14 | : uicolors.gray['600']; 15 | const passiveGray = this.darkMode 16 | ? uicolors.gray['700'] 17 | : uicolors.gray['400']; 18 | return this.active ? activeGray : passiveGray; 19 | }, 20 | darkColor(): string { 21 | const activeGray = this.darkMode 22 | ? uicolors.gray['200'] 23 | : uicolors.gray['800']; 24 | const passiveGray = this.darkMode 25 | ? uicolors.gray['500'] 26 | : uicolors.gray['600']; 27 | return this.active ? activeGray : passiveGray; 28 | }, 29 | bgColor(): string { 30 | return this.darkMode ? uicolors.gray['900'] : uicolors.gray['100']; 31 | }, 32 | }, 33 | }; 34 | </script> 35 | -------------------------------------------------------------------------------- /src/components/MouseFollower.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <Tooltip ref="tooltip"><slot></slot></Tooltip> 3 | </template> 4 | 5 | <script> 6 | import { defineComponent } from 'vue'; 7 | import Tooltip from './Tooltip.vue'; 8 | 9 | export default defineComponent({ 10 | components: { Tooltip }, 11 | props: { show: { type: Boolean, default: false } }, 12 | watch: { 13 | show(val) { 14 | if (val) { 15 | this.$refs.tooltip.create(); 16 | this.setListeners(); 17 | } else { 18 | this.$refs.tooltip.destroy(); 19 | this.removeListener(); 20 | } 21 | }, 22 | }, 23 | methods: { 24 | mousemoveListener(e) { 25 | this.$refs.tooltip.update(e); 26 | }, 27 | setListeners() { 28 | window.addEventListener('mousemove', this.mousemoveListener); 29 | }, 30 | removeListener() { 31 | window.removeEventListener('mousemove', this.mousemoveListener); 32 | }, 33 | }, 34 | }); 35 | </script> 36 | -------------------------------------------------------------------------------- /src/components/POS/FloatingLabelFloatInput.vue: -------------------------------------------------------------------------------- 1 | <script lang="ts"> 2 | import { defineComponent } from 'vue'; 3 | import FloatingLabelInputBase from './FloatingLabelInputBase.vue'; 4 | 5 | export default defineComponent({ 6 | name: 'FloatingLabelFloatInput', 7 | extends: FloatingLabelInputBase, 8 | computed: { 9 | inputType() { 10 | return 'number'; 11 | }, 12 | }, 13 | }); 14 | </script> 15 | -------------------------------------------------------------------------------- /src/components/POS/types.ts: -------------------------------------------------------------------------------- 1 | import { Money } from 'pesa'; 2 | 3 | export type ItemQtyMap = { 4 | [item: string]: { availableQty: number; [batch: string]: number }; 5 | }; 6 | 7 | export type ItemSerialNumbers = { [item: string]: string }; 8 | 9 | export type DiscountType = 'percent' | 'amount'; 10 | 11 | export const modalNames = [ 12 | 'Keyboard', 13 | 'Payment', 14 | 'ShiftClose', 15 | 'LoyaltyProgram', 16 | 'SavedInvoice', 17 | 'Alert', 18 | 'CouponCode', 19 | 'PriceList', 20 | 'ReturnSalesInvoice', 21 | ] as const; 22 | 23 | export type ModalName = typeof modalNames[number]; 24 | 25 | export type PosEmits = 26 | | 'addItem' 27 | | 'toggleView' 28 | | 'toggleModal' 29 | | 'setPaidAmount' 30 | | 'setPaymentMethod' 31 | | 'setCouponsCount' 32 | | 'routeToSinvList' 33 | | 'applyPricingRule' 34 | | 'setTransferRefNo' 35 | | 'setLoyaltyPoints' 36 | | 'setTransferAmount' 37 | | 'createTransaction' 38 | | 'selectedInvoiceName' 39 | | 'selectedReturnInvoice' 40 | | 'saveAndContinue' 41 | | 'setTransferClearanceDate'; 42 | 43 | export interface POSItem { 44 | id?: number; 45 | image?: string; 46 | name: string; 47 | rate: Money; 48 | availableQty: number; 49 | unit: string; 50 | hasBatch: boolean; 51 | hasSerialNumber: boolean; 52 | } 53 | -------------------------------------------------------------------------------- /src/components/Row.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div class="inline-grid" :style="style" v-bind="$attrs"> 3 | <slot></slot> 4 | </div> 5 | </template> 6 | <script> 7 | export default { 8 | name: 'Row', 9 | props: { 10 | columnWidth: { 11 | type: String, 12 | default: '1fr', 13 | }, 14 | columnCount: { 15 | type: Number, 16 | default: 0, 17 | }, 18 | ratio: { 19 | type: Array, 20 | default: () => [], 21 | }, 22 | gridTemplateColumns: { 23 | type: String, 24 | default: null, 25 | }, 26 | gap: String, 27 | }, 28 | computed: { 29 | style() { 30 | let obj = {}; 31 | if (this.columnCount) { 32 | // prettier-ignore 33 | obj['grid-template-columns'] = `repeat(${this.columnCount}, ${this.columnWidth})`; 34 | } 35 | if (this.ratio.length) { 36 | obj['grid-template-columns'] = this.ratio 37 | .map((r) => `minmax(0, ${r}fr)`) 38 | .join(' '); 39 | } 40 | if (this.gridTemplateColumns) { 41 | obj['grid-template-columns'] = this.gridTemplateColumns; 42 | } 43 | if (this.gap) { 44 | obj['grid-gap'] = this.gap; 45 | } 46 | 47 | return obj; 48 | }, 49 | }, 50 | }; 51 | </script> 52 | -------------------------------------------------------------------------------- /src/components/ShortcutKeys.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div class="flex-shrink-0 flex items-center gap-2" style="width: fit-content"> 3 | <kbd 4 | v-for="k in keys" 5 | :key="k" 6 | class="key-common" 7 | :class="{ 'key-styling': !simple }" 8 | >{{ keyMap[k] ?? k }}</kbd 9 | > 10 | </div> 11 | </template> 12 | <script lang="ts"> 13 | import { getShortcutKeyMap } from 'src/utils/ui'; 14 | import { defineComponent, PropType } from 'vue'; 15 | export default defineComponent({ 16 | props: { 17 | keys: { type: Array as PropType<string[]>, required: true }, 18 | simple: { type: Boolean, default: false }, 19 | }, 20 | method() {}, 21 | computed: { 22 | keyMap(): Record<string, string> { 23 | return getShortcutKeyMap(this.platform); 24 | }, 25 | }, 26 | }); 27 | </script> 28 | <style scoped> 29 | .key-common { 30 | font-family: monospace; 31 | font-weight: 600; 32 | @apply rounded-md px-1.5 py-0.5 bg-gray-200 text-gray-700 33 | tracking-tighter; 34 | } 35 | 36 | .key-styling { 37 | @apply border-b-4 border-gray-400 shadow-md; 38 | } 39 | </style> 40 | -------------------------------------------------------------------------------- /src/components/WithScroll.vue: -------------------------------------------------------------------------------- 1 | <template> 2 | <div class="custom-scroll custom-scroll-thumb1"> 3 | <slot></slot> 4 | </div> 5 | </template> 6 | <script lang="ts"> 7 | import { defineComponent } from 'vue'; 8 | 9 | export default defineComponent({ 10 | name: 'WithScroll', 11 | emits: ['scroll'], 12 | data() { 13 | return { listener: undefined } as { listener?: () => void }; 14 | }, 15 | mounted() { 16 | this.listener = () => { 17 | let { scrollLeft, scrollTop } = this.$el; 18 | this.$emit('scroll', { scrollLeft, scrollTop }); 19 | }; 20 | this.$el.addEventListener('scroll', this.listener); 21 | }, 22 | beforeUnmount() { 23 | if (!this.listener) { 24 | return; 25 | } 26 | 27 | this.$el.removeEventListener('scroll', this.listener); 28 | delete this.listener; 29 | }, 30 | }); 31 | </script> 32 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="UTF-8" /> 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 | <meta http-equiv="X-UA-Compatible" content="ie=edge" /> 7 | <title>Frappe Books 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/initFyo.ts: -------------------------------------------------------------------------------- 1 | import { Fyo } from 'fyo'; 2 | 3 | /** 4 | * Global fyo: this is meant to be used only by the app. For 5 | * testing purposes a separate instance of fyo should be initialized. 6 | */ 7 | 8 | export const fyo = new Fyo({ isTest: false, isElectron: true }); 9 | -------------------------------------------------------------------------------- /src/pages/Dashboard/BaseDashboardChart.vue: -------------------------------------------------------------------------------- 1 | 43 | -------------------------------------------------------------------------------- /src/pages/Dashboard/SectionHeader.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/regional/in/in.ts: -------------------------------------------------------------------------------- 1 | import { Fyo } from 'fyo'; 2 | 3 | export type TaxType = 'GST' | 'IGST' | 'Exempt-GST' | 'Exempt-IGST'; 4 | 5 | export async function createIndianRecords(fyo: Fyo) { 6 | await createTaxes(fyo); 7 | } 8 | 9 | async function createTaxes(fyo: Fyo) { 10 | const GSTs = { 11 | GST: [28, 18, 12, 6, 5, 3, 0.25, 0], 12 | IGST: [28, 18, 12, 6, 5, 3, 0.25, 0], 13 | 'Exempt-GST': [0], 14 | 'Exempt-IGST': [0], 15 | }; 16 | 17 | for (const type of Object.keys(GSTs)) { 18 | for (const percent of GSTs[type as TaxType]) { 19 | const name = `${type}-${percent}`; 20 | const details = getTaxDetails(type as TaxType, percent); 21 | 22 | const newTax = fyo.doc.getNewDoc('Tax', { name, details }); 23 | await newTax.sync(); 24 | } 25 | } 26 | } 27 | 28 | function getTaxDetails(type: TaxType, percent: number) { 29 | if (type === 'GST') { 30 | return [ 31 | { 32 | account: 'CGST', 33 | rate: percent / 2, 34 | }, 35 | { 36 | account: 'SGST', 37 | rate: percent / 2, 38 | }, 39 | ]; 40 | } 41 | 42 | return [ 43 | { 44 | account: type.toString().split('-')[0], 45 | rate: percent, 46 | }, 47 | ]; 48 | } 49 | -------------------------------------------------------------------------------- /src/regional/index.ts: -------------------------------------------------------------------------------- 1 | import { Fyo } from 'fyo'; 2 | import { createIndianRecords } from './in/in'; 3 | 4 | export async function createRegionalRecords(country: string, fyo: Fyo) { 5 | if (country === 'India') { 6 | await createIndianRecords(fyo); 7 | } 8 | 9 | return; 10 | } 11 | -------------------------------------------------------------------------------- /src/renderer/helpers.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from 'vue'; 2 | 3 | type OutsideClickCallback = (e: Event) => void; 4 | const instanceMap: Map = new Map(); 5 | 6 | export const outsideClickDirective: Directive< 7 | HTMLElement, 8 | OutsideClickCallback 9 | > = { 10 | beforeMount(el, binding) { 11 | const clickHandler = function (e: Event) { 12 | onDocumentClick(e, el, binding.value); 13 | }; 14 | 15 | removeHandlerIfPresent(el); 16 | instanceMap.set(el, clickHandler); 17 | document.addEventListener('click', clickHandler); 18 | }, 19 | unmounted(el) { 20 | removeHandlerIfPresent(el); 21 | }, 22 | }; 23 | 24 | function onDocumentClick(e: Event, el: HTMLElement, fn: OutsideClickCallback) { 25 | const target = e.target as Node; 26 | if (el !== target && !el.contains(target)) { 27 | fn?.(e); 28 | } 29 | } 30 | 31 | function removeHandlerIfPresent(el: HTMLElement) { 32 | const clickHandler = instanceMap.get(el); 33 | if (!clickHandler) { 34 | return; 35 | } 36 | 37 | instanceMap.delete(el); 38 | document.removeEventListener('click', clickHandler); 39 | } 40 | -------------------------------------------------------------------------------- /src/setup/types.ts: -------------------------------------------------------------------------------- 1 | export interface SetupWizardOptions { 2 | logo: string | null; 3 | companyName: string; 4 | country: string; 5 | fullname: string; 6 | email: string; 7 | bankName: string; 8 | currency: string; 9 | fiscalYearStart: string; 10 | fiscalYearEnd: string; 11 | chartOfAccounts: string; 12 | } 13 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import type { IPC } from 'main/preload'; 2 | import Vue, { VNode } from 'vue'; 3 | 4 | declare global { 5 | const ipc: IPC; 6 | namespace JSX { 7 | type Element = VNode; 8 | type ElementClass = Vue; 9 | interface IntrinsicElements { 10 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 11 | [elem: string]: any; 12 | } 13 | } 14 | 15 | interface Window { 16 | ipc: IPC; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/shims-vue-custom.d.ts: -------------------------------------------------------------------------------- 1 | import { Fyo } from 'fyo'; 2 | import { TranslationLiteral } from 'fyo/utils/translation'; 3 | 4 | declare module 'vue' { 5 | interface ComponentCustomProperties { 6 | t: (...args: TranslationLiteral[]) => string; 7 | fyo: Fyo; 8 | platform: 'Windows' | 'Linux' | 'Mac'; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import type { DefineComponent } from 'vue'; 3 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 4 | const component: DefineComponent<{}, {}, any>; 5 | export default component; 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/api.ts: -------------------------------------------------------------------------------- 1 | export async function sendAPIRequest( 2 | endpoint: string, 3 | options: RequestInit | undefined 4 | ) { 5 | return await ipc.sendAPIRequest(endpoint, options); 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/colors.ts: -------------------------------------------------------------------------------- 1 | import colors from '../../colors.json'; 2 | 3 | export const uicolors = colors; 4 | export const indicators = { 5 | GRAY: 'grey', 6 | GREY: 'grey', 7 | BLUE: 'blue', 8 | RED: 'red', 9 | GREEN: 'green', 10 | ORANGE: 'orange', 11 | PURPLE: 'purple', 12 | YELLOW: 'yellow', 13 | BLACK: 'black', 14 | }; 15 | 16 | const getValidColor = (color: string) => { 17 | const isValid = [ 18 | 'gray', 19 | 'orange', 20 | 'green', 21 | 'red', 22 | 'yellow', 23 | 'blue', 24 | 'indigo', 25 | 'pink', 26 | 'purple', 27 | 'teal', 28 | ].includes(color); 29 | return isValid ? color : 'gray'; 30 | }; 31 | 32 | export function getBgColorClass(color: string) { 33 | const vcolor = getValidColor(color); 34 | return `bg-${vcolor}-200 dark:bg-${vcolor}-700`; 35 | } 36 | 37 | export function getColorClass( 38 | color: string, 39 | type: 'bg' | 'text' | 'border', 40 | value = 300, 41 | darkvalue = 600 42 | ) { 43 | return `${type}-${getValidColor(color)}-${value} dark:${type}-${getValidColor( 44 | color 45 | )}-${darkvalue}`; 46 | } 47 | 48 | export function getTextColorClass(color: string) { 49 | return `text-${getValidColor(color)}-700 dark:text-${getValidColor( 50 | color 51 | )}-200`; 52 | } 53 | 54 | export function getBgTextColorClass(color: string) { 55 | const bg = getBgColorClass(color); 56 | const text = getTextColorClass(color); 57 | return [bg, text].join(' '); 58 | } 59 | -------------------------------------------------------------------------------- /src/utils/filters.ts: -------------------------------------------------------------------------------- 1 | import { ModelNameEnum } from 'models/types'; 2 | 3 | export const routeFilters = { 4 | SalesItems: { for: ['in', ['Sales', 'Both']] }, 5 | PurchaseItems: { for: ['in', ['Purchases', 'Both']] }, 6 | Items: { for: 'Both' }, 7 | PurchasePayments: { 8 | referenceType: ModelNameEnum.PurchaseInvoice, 9 | }, 10 | SalesPayments: { 11 | referenceType: ModelNameEnum.SalesInvoice, 12 | }, 13 | Suppliers: { role: ['in', ['Supplier', 'Both']] }, 14 | Customers: { role: ['in', ['Customer', 'Both']] }, 15 | Party: { role: 'Both' }, 16 | }; 17 | 18 | export const createFilters = { 19 | SalesItems: { for: 'Sales' }, 20 | PurchaseItems: { for: 'Purchases' }, 21 | Items: { for: 'Both' }, 22 | PurchasePayments: { paymentType: 'Pay' }, 23 | SalesPayments: { paymentType: 'Receive' }, 24 | Suppliers: { role: 'Supplier' }, 25 | Customers: { role: 'Customer' }, 26 | Party: { role: 'Both' }, 27 | }; 28 | -------------------------------------------------------------------------------- /src/utils/injectionKeys.ts: -------------------------------------------------------------------------------- 1 | import type { InjectionKey, Ref } from 'vue'; 2 | import type { Search } from './search'; 3 | import type { Shortcuts } from './shortcuts'; 4 | import type { useKeys } from './vueUtils'; 5 | 6 | export const languageDirectionKey = Symbol('languageDirection') as InjectionKey< 7 | Ref<'ltr' | 'rtl'> 8 | >; 9 | 10 | export const keysKey = Symbol('keys') as InjectionKey< 11 | ReturnType 12 | >; 13 | 14 | export const searcherKey = Symbol('searcher') as InjectionKey< 15 | Ref 16 | >; 17 | 18 | export const shortcutsKey = Symbol('shortcuts') as InjectionKey; 19 | -------------------------------------------------------------------------------- /src/utils/refs.ts: -------------------------------------------------------------------------------- 1 | import { reactive, ref } from 'vue'; 2 | import type { HistoryState } from 'vue-router'; 3 | 4 | export const showSidebar = ref(true); 5 | export const docsPathRef = ref(''); 6 | export const systemLanguageRef = ref(''); 7 | export const historyState = reactive({ 8 | forward: !!(history.state as HistoryState)?.forward, 9 | back: !!(history.state as HistoryState)?.back, 10 | }); 11 | -------------------------------------------------------------------------------- /src/utils/theme.ts: -------------------------------------------------------------------------------- 1 | export function setDarkMode(darkMode: boolean): void { 2 | if (darkMode) { 3 | document.documentElement.classList.add( 4 | 'dark', 5 | 'custom-scroll', 6 | 'custom-scroll-thumb1' 7 | ); 8 | return; 9 | } 10 | document.documentElement.classList.remove('dark'); 11 | } 12 | -------------------------------------------------------------------------------- /tests/parties.csv: -------------------------------------------------------------------------------- 1 | Name,Role,"Default Account","Outstanding Amount",Currency,Email,Phone,Address,"GSTIN No.","GST Registration","Created By","Modified By",Created,Modified 2 | Party.name,Party.role,Party.defaultAccount,Party.outstandingAmount,Party.currency,Party.email,Party.phone,Party.address,Party.gstin,Party.gstType,Party.createdBy,Party.modifiedBy,Party.created,Party.modified 3 | Randoe,Both,,259.6,INR,,,,,Unregistered,lin@to.co,lin@to.co,2023-01-09T04:58:16.050Z,2023-01-09T10:46:46.128Z 4 | Saipan,Customer,Debtors,299.99999999921,USD,sai@pan.co,,,,Unregistered,lin@to.co,lin@to.co,2022-07-18T17:07:35.103Z,2023-01-30T14:36:50.058Z 5 | Lordham,Customer,Debtors,851.8,INR,lo@gamil.com,8989004444,,,Unregistered,Administrator,lin@to.co,2022-02-04T06:35:19.404Z,2022-07-18T17:05:42.976Z 6 | Lyn,Customer,Debtors,100,INR,lyn@to.co,,,,Consumer,Administrator,Administrator,2022-02-04T06:21:19.069Z,2022-02-28T05:18:32.743Z 7 | Bølèn,Customer,Debtors,0,INR,bo@len.co,,,22ABCIK123401Z5,"Registered Regular",Administrator,Administrator,2022-01-12T08:44:58.879Z,2022-01-12T08:45:26.714Z 8 | Bé,Customer,Debtors,46,INR,bey@more.tips,6969969600,,,Consumer,Administrator,Administrator,2021-12-16T11:32:11.595Z,2021-12-16T12:00:28.558Z -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "esnext", 5 | "strict": true, 6 | "allowJs": true, 7 | "jsx": "preserve", 8 | "importHelpers": true, 9 | "moduleResolution": "node", 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "resolveJsonModule": true, 14 | "sourceMap": true, 15 | "types": ["webpack-env"], 16 | "baseUrl": ".", 17 | "paths": { 18 | "src/*": ["src/*"], 19 | "schemas/*": ["schemas/*"], 20 | "main/*": ["main/*"], 21 | "backend/*": ["backend/*"], 22 | "regional/*": ["regional/*"], 23 | "fixtures/*": ["fixtures/*"], 24 | "reports/*": ["reports/*"], 25 | "models/*": ["models/*"], 26 | "utils/*": ["utils/*"], 27 | "dummy/*": ["dummy/*"] 28 | }, 29 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"] 30 | }, 31 | "ts-node": { 32 | "files": true 33 | }, 34 | "include": [ 35 | "src/**/*.ts", 36 | "src/**/*.vue", 37 | "schemas/**/*.ts", 38 | "backend/**/*.ts", 39 | "fyo/**/*.ts", 40 | "models/**/*.ts", 41 | "patches/**/*.ts", 42 | "patches/**/*.js", 43 | "reports/**/*.ts", 44 | "accounting/**/*.ts", 45 | "scripts/**/*.ts", 46 | "main/**/*.ts", 47 | "regional/**/*.ts", 48 | "reports/**/*.ts", 49 | "utils/**/*.ts", 50 | "tests/**/*.ts", 51 | "dummy/**/*.ts", 52 | "jobs/**/*.ts" 53 | ], 54 | "exclude": ["node_modules"] 55 | } 56 | -------------------------------------------------------------------------------- /utils/auth/types.ts: -------------------------------------------------------------------------------- 1 | import { Creds } from 'utils/types'; 2 | 3 | export abstract class AuthDemuxBase { 4 | abstract getCreds(): Promise; 5 | } 6 | -------------------------------------------------------------------------------- /utils/config.ts: -------------------------------------------------------------------------------- 1 | import Store from 'electron-store'; 2 | import type { ConfigMap } from 'fyo/core/types'; 3 | 4 | const config = new Store(); 5 | export default config; 6 | -------------------------------------------------------------------------------- /utils/defaults.ts: -------------------------------------------------------------------------------- 1 | import { Fyo } from 'fyo'; 2 | 3 | export function getDefaultUOMs(fyo: Fyo) { 4 | return [ 5 | { 6 | name: fyo.t`Unit`, 7 | isWhole: true, 8 | }, 9 | { 10 | name: fyo.t`Kg`, 11 | isWhole: false, 12 | }, 13 | { 14 | name: fyo.t`Gram`, 15 | isWhole: false, 16 | }, 17 | { 18 | name: fyo.t`Meter`, 19 | isWhole: false, 20 | }, 21 | { 22 | name: fyo.t`Hour`, 23 | isWhole: false, 24 | }, 25 | { 26 | name: fyo.t`Day`, 27 | isWhole: false, 28 | }, 29 | ]; 30 | } 31 | 32 | export function getDefaultLocations(fyo: Fyo) { 33 | return [{ name: fyo.t`Stores` }]; 34 | } 35 | -------------------------------------------------------------------------------- /utils/ipc/types.ts: -------------------------------------------------------------------------------- 1 | export interface BackendResponse { 2 | data?: unknown; 3 | error?: { message: string; name: string; stack?: string; code?: string }; 4 | } 5 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import vue from '@vitejs/plugin-vue'; 2 | import path from 'path'; 3 | import { defineConfig } from 'vite'; 4 | 5 | /** 6 | * This vite config file is used only for dev mode, i.e. 7 | * to create a serve build modules of the source code 8 | * which will be rendered by electron. 9 | * 10 | * For building the project, vite is used programmatically 11 | * see build/scripts/build.mjs for this. 12 | */ 13 | export default () => { 14 | let port = 6969; 15 | let host = '0.0.0.0'; 16 | if (process.env.VITE_PORT && process.env.VITE_HOST) { 17 | port = Number(process.env.VITE_PORT); 18 | host = process.env.VITE_HOST; 19 | } 20 | 21 | return defineConfig({ 22 | server: { host, port, strictPort: true }, 23 | root: path.resolve(__dirname, './src'), 24 | plugins: [vue()], 25 | resolve: { 26 | alias: { 27 | vue: 'vue/dist/vue.esm-bundler.js', 28 | fyo: path.resolve(__dirname, './fyo'), 29 | src: path.resolve(__dirname, './src'), 30 | schemas: path.resolve(__dirname, './schemas'), 31 | backend: path.resolve(__dirname, './backend'), 32 | models: path.resolve(__dirname, './models'), 33 | utils: path.resolve(__dirname, './utils'), 34 | regional: path.resolve(__dirname, './regional'), 35 | reports: path.resolve(__dirname, './reports'), 36 | dummy: path.resolve(__dirname, './dummy'), 37 | fixtures: path.resolve(__dirname, './fixtures'), 38 | }, 39 | }, 40 | }); 41 | }; 42 | --------------------------------------------------------------------------------