mirror of
https://github.com/frappe/books.git
synced 2025-01-22 22:58:28 +00:00
Merge branch 'master' of https://github.com/frappe/books
This commit is contained in:
commit
a2d44a686b
@ -157,6 +157,22 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
return false;
|
||||
}
|
||||
|
||||
get canEdit() {
|
||||
if (!this.schema.isSubmittable) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.submitted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.cancelled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
get canSave() {
|
||||
if (!!this.submitted) {
|
||||
return false;
|
||||
|
@ -6,13 +6,11 @@ import {
|
||||
DefaultMap,
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
HiddenMap
|
||||
HiddenMap,
|
||||
} from 'fyo/model/types';
|
||||
import { DEFAULT_CURRENCY } from 'fyo/utils/consts';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import {
|
||||
getExchangeRate, getNumberSeries
|
||||
} from 'models/helpers';
|
||||
import { addItem, getExchangeRate, getNumberSeries } from 'models/helpers';
|
||||
import { InventorySettings } from 'models/inventory/InventorySettings';
|
||||
import { StockTransfer } from 'models/inventory/StockTransfer';
|
||||
import { Transactional } from 'models/Transactional/Transactional';
|
||||
@ -695,4 +693,8 @@ export abstract class Invoice extends Transactional {
|
||||
}))
|
||||
.sort((a, b) => a.date.valueOf() - b.date.valueOf());
|
||||
}
|
||||
|
||||
async addItem(name: string) {
|
||||
return await addItem(name, this);
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ export class Item extends Doc {
|
||||
!this.fyo.singles.AccountingSettings?.enableInventory ||
|
||||
this.itemType !== 'Product' ||
|
||||
(this.inserted && !this.trackItem),
|
||||
barcode: () => !this.fyo.singles.InventorySettings?.enableBarcodes,
|
||||
};
|
||||
|
||||
readOnly: ReadOnlyMap = {
|
||||
|
@ -14,6 +14,8 @@ import {
|
||||
numberSeriesDefaultsMap,
|
||||
} from './baseModels/Defaults/Defaults';
|
||||
import { Invoice } from './baseModels/Invoice/Invoice';
|
||||
import { StockMovement } from './inventory/StockMovement';
|
||||
import { StockTransfer } from './inventory/StockTransfer';
|
||||
import { InvoiceStatus, ModelNameEnum } from './types';
|
||||
|
||||
export function getInvoiceActions(
|
||||
@ -321,3 +323,27 @@ export function getDocStatusListColumn(): ColumnConfig {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
type ModelsWithItems = Invoice | StockTransfer | StockMovement;
|
||||
export async function addItem<M extends ModelsWithItems>(name: string, doc: M) {
|
||||
if (!doc.canEdit) {
|
||||
return;
|
||||
}
|
||||
|
||||
const items = (doc.items ?? []) as NonNullable<M['items']>[number][];
|
||||
|
||||
let item = items.find((i) => i.item === name);
|
||||
if (item) {
|
||||
const q = item.quantity ?? 0;
|
||||
await item.set('quantity', q + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
await doc.append('items');
|
||||
item = doc.items?.at(-1);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
await item.set('item', name);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ export class InventorySettings extends Doc {
|
||||
valuationMethod?: ValuationMethod;
|
||||
stockReceivedButNotBilled?: string;
|
||||
costOfGoodsSold?: string;
|
||||
enableBarcodes?: boolean;
|
||||
|
||||
static filters: FiltersMap = {
|
||||
stockInHand: () => ({
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
FormulaMap,
|
||||
ListViewSettings,
|
||||
} from 'fyo/model/types';
|
||||
import { getDocStatusListColumn, getLedgerLinkAction } from 'models/helpers';
|
||||
import { addItem, getDocStatusListColumn, getLedgerLinkAction } from 'models/helpers';
|
||||
import { LedgerPosting } from 'models/Transactional/LedgerPosting';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { Money } from 'pesa';
|
||||
@ -92,4 +92,8 @@ export class StockMovement extends Transfer {
|
||||
static getActions(fyo: Fyo): Action[] {
|
||||
return [getLedgerLinkAction(fyo, true)];
|
||||
}
|
||||
|
||||
async addItem(name: string) {
|
||||
return await addItem(name, this);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { Action, DefaultMap, FiltersMap, FormulaMap } from 'fyo/model/types';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import { Defaults } from 'models/baseModels/Defaults/Defaults';
|
||||
import { Invoice } from 'models/baseModels/Invoice/Invoice';
|
||||
import { getLedgerLinkAction, getNumberSeries } from 'models/helpers';
|
||||
import { addItem, getLedgerLinkAction, getNumberSeries } from 'models/helpers';
|
||||
import { LedgerPosting } from 'models/Transactional/LedgerPosting';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { Money } from 'pesa';
|
||||
@ -232,4 +232,8 @@ export abstract class StockTransfer extends Transfer {
|
||||
role: doc.isSales ? 'Customer' : 'Supplier',
|
||||
}),
|
||||
};
|
||||
|
||||
async addItem(name: string) {
|
||||
return await addItem(name, this);
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +106,12 @@
|
||||
"fieldtype": "Int",
|
||||
"placeholder": "HSN/SAC Code"
|
||||
},
|
||||
{
|
||||
"fieldname": "barcode",
|
||||
"label": "Barcode",
|
||||
"fieldtype": "Data",
|
||||
"placeholder": "Barcode"
|
||||
},
|
||||
{
|
||||
"fieldname": "trackItem",
|
||||
"label": "Track Item",
|
||||
@ -122,6 +128,7 @@
|
||||
"description",
|
||||
"incomeAccount",
|
||||
"expenseAccount",
|
||||
"barcode",
|
||||
"hsnCode",
|
||||
"trackItem"
|
||||
],
|
||||
|
@ -45,6 +45,11 @@
|
||||
"label": "Cost Of Goods Sold Acc.",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account"
|
||||
},
|
||||
{
|
||||
"fieldname": "enableBarcodes",
|
||||
"label": "Enable Barcodes",
|
||||
"fieldtype": "Check"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
130
src/components/Controls/Barcode.vue
Normal file
130
src/components/Controls/Barcode.vue
Normal file
@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div
|
||||
class="
|
||||
flex
|
||||
items-center
|
||||
border
|
||||
w-36
|
||||
rounded
|
||||
px-2
|
||||
bg-gray-50
|
||||
focus-within:bg-gray-100
|
||||
"
|
||||
>
|
||||
<input
|
||||
ref="scanner"
|
||||
type="text"
|
||||
class="text-base placeholder-gray-600 w-full bg-transparent outline-none"
|
||||
@change="handleChange"
|
||||
:placeholder="t`Enter barcode`"
|
||||
/>
|
||||
<feather-icon
|
||||
name="maximize"
|
||||
class="w-3 h-3 text-gray-600 cursor-text"
|
||||
@click="() => ($refs.scanner as HTMLInputElement).focus()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { showToast } from 'src/utils/ui';
|
||||
import { defineComponent } from 'vue';
|
||||
export default defineComponent({
|
||||
emits: ['item-selected'],
|
||||
data() {
|
||||
return {
|
||||
timerId: null,
|
||||
barcode: '',
|
||||
} as {
|
||||
timerId: null | ReturnType<typeof setInterval>;
|
||||
barcode: string;
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('keydown', this.scanListener);
|
||||
},
|
||||
unmounted() {
|
||||
document.removeEventListener('keydown', this.scanListener);
|
||||
},
|
||||
activated() {
|
||||
document.addEventListener('keydown', this.scanListener);
|
||||
},
|
||||
deactivated() {
|
||||
document.removeEventListener('keydown', this.scanListener);
|
||||
},
|
||||
methods: {
|
||||
handleChange(e: Event) {
|
||||
const elem = e.target as HTMLInputElement;
|
||||
this.selectItem(elem.value);
|
||||
elem.value = '';
|
||||
},
|
||||
async selectItem(code: string) {
|
||||
const barcode = code.trim();
|
||||
if (!/\d{12,}/.test(barcode)) {
|
||||
return this.error(this.t`Invalid barcode value ${barcode}.`);
|
||||
}
|
||||
|
||||
const items = (await this.fyo.db.getAll('Item', {
|
||||
filters: { barcode },
|
||||
fields: ['name'],
|
||||
})) as { name: string }[];
|
||||
|
||||
const name = items?.[0]?.name;
|
||||
if (!name) {
|
||||
return this.error(this.t`Item with barcode ${barcode} not found.`);
|
||||
}
|
||||
|
||||
this.success(this.t`${name} quantity 1 added.`);
|
||||
this.$emit('item-selected', name);
|
||||
},
|
||||
async scanListener({ key, code }: KeyboardEvent) {
|
||||
/**
|
||||
* Based under the assumption that
|
||||
* - Barcode scanners trigger keydown events
|
||||
* - Keydown events are triggered quicker than human can
|
||||
* i.e. at max 20ms between events
|
||||
* - Keydown events are triggered for barcode digits
|
||||
* - The sequence of digits might be punctuated by a return
|
||||
*/
|
||||
|
||||
const keyCode = Number(key);
|
||||
const isEnter = code === 'Enter';
|
||||
if (Number.isNaN(keyCode) && !isEnter) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEnter) {
|
||||
return await this.setItemFromBarcode();
|
||||
}
|
||||
|
||||
if (this.timerId !== null) {
|
||||
clearInterval(this.timerId);
|
||||
}
|
||||
|
||||
this.barcode += key;
|
||||
this.timerId = setInterval(async () => {
|
||||
await this.setItemFromBarcode();
|
||||
this.barcode = '';
|
||||
}, 20);
|
||||
},
|
||||
async setItemFromBarcode() {
|
||||
if (this.barcode.length < 12) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.selectItem(this.barcode);
|
||||
|
||||
this.barcode = '';
|
||||
if (this.timerId !== null) {
|
||||
clearInterval(this.timerId);
|
||||
}
|
||||
},
|
||||
error(message: string) {
|
||||
showToast({ type: 'error', message });
|
||||
},
|
||||
success(message: string) {
|
||||
showToast({ type: 'success', message });
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex items-center bg-gray-100 rounded-md textsm px-1">
|
||||
<div class="flex items-center bg-gray-50 rounded-md textsm px-1 border">
|
||||
<div
|
||||
class="rate-container"
|
||||
:class="disabled ? 'bg-gray-100' : 'bg-gray-25'"
|
||||
@ -89,12 +89,11 @@ export default defineComponent({
|
||||
</script>
|
||||
<style scoped>
|
||||
input[type='number'] {
|
||||
@apply w-12 outline-none bg-transparent p-0.5;
|
||||
@apply w-12 bg-transparent p-0.5;
|
||||
}
|
||||
|
||||
.rate-container {
|
||||
@apply flex items-center rounded-md border border-gray-100 text-gray-900
|
||||
text-sm outline-none focus-within:bg-gray-50 px-1 focus-within:border-gray-200;
|
||||
@apply flex items-center rounded-md border-gray-100 text-gray-900 text-sm px-1 focus-within:border-gray-200 bg-transparent;
|
||||
}
|
||||
|
||||
.rate-container > p {
|
||||
|
@ -4,7 +4,7 @@
|
||||
class="
|
||||
fixed
|
||||
top-0
|
||||
left-0
|
||||
start-0
|
||||
w-screen
|
||||
h-screen
|
||||
z-20
|
||||
@ -21,7 +21,6 @@
|
||||
bg-white
|
||||
rounded-lg
|
||||
shadow-2xl
|
||||
w-form
|
||||
border
|
||||
overflow-hidden
|
||||
inner
|
||||
@ -44,10 +43,6 @@ export default defineComponent({
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
setCloseListener: {
|
||||
default: true,
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
emits: ['closemodal'],
|
||||
watch: {
|
||||
|
@ -17,7 +17,7 @@
|
||||
:set-close-listener="false"
|
||||
>
|
||||
<!-- Search Input -->
|
||||
<div class="p-1">
|
||||
<div class="p-1 w-form">
|
||||
<input
|
||||
ref="input"
|
||||
type="search"
|
||||
|
@ -167,7 +167,7 @@
|
||||
|
||||
<!-- Base Count Selection when Dev -->
|
||||
<Modal :open-modal="openModal" @closemodal="openModal = false">
|
||||
<div class="p-4 text-gray-900">
|
||||
<div class="p-4 text-gray-900 w-form">
|
||||
<h2 class="text-xl font-semibold select-none">Set Base Count</h2>
|
||||
<p class="text-base mt-2">
|
||||
Base Count is a lower bound on the number of entries made when
|
||||
|
@ -36,15 +36,16 @@
|
||||
class="
|
||||
absolute
|
||||
bottom-0
|
||||
left-0
|
||||
start-0
|
||||
text-gray-600
|
||||
bg-gray-100
|
||||
rounded
|
||||
rtl-rotate-180
|
||||
p-1
|
||||
m-4
|
||||
opacity-0
|
||||
hover:opacity-100 hover:shadow-md
|
||||
"
|
||||
|
||||
@click="sidebar = !sidebar"
|
||||
>
|
||||
<feather-icon name="chevrons-right" class="w-4 h-4" />
|
||||
@ -76,6 +77,11 @@ export default {
|
||||
transform: translateX(calc(-1 * var(--w-sidebar)));
|
||||
width: 0px;
|
||||
}
|
||||
[dir='rtl'] .sidebar-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(calc(1 * var(--w-sidebar)));
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.sidebar-enter-to,
|
||||
.sidebar-leave-from {
|
||||
|
@ -3,6 +3,10 @@
|
||||
<!-- Page Header (Title, Buttons, etc) -->
|
||||
<template #header v-if="doc">
|
||||
<StatusBadge :status="status" />
|
||||
<Barcode
|
||||
v-if="showBarcode"
|
||||
@item-selected="(name) => doc.addItem(name)"
|
||||
/>
|
||||
<DropdownWithActions
|
||||
v-for="group of groupedActions"
|
||||
:key="group.label"
|
||||
@ -145,7 +149,9 @@
|
||||
import { computed } from '@vue/reactivity';
|
||||
import { t } from 'fyo';
|
||||
import { getDocStatus } from 'models/helpers';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import Barcode from 'src/components/Controls/Barcode.vue';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import Table from 'src/components/Controls/Table.vue';
|
||||
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||
@ -176,6 +182,7 @@ export default {
|
||||
FormContainer,
|
||||
QuickEditForm,
|
||||
FormHeader,
|
||||
Barcode,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
@ -204,6 +211,25 @@ export default {
|
||||
groupedActions() {
|
||||
return getGroupedActionsForDoc(this.doc);
|
||||
},
|
||||
showBarcode() {
|
||||
if (!this.doc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.doc.canEdit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fyo.singles.InventorySettings?.enableBarcodes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return [
|
||||
ModelNameEnum.Shipment,
|
||||
ModelNameEnum.PurchaseReceipt,
|
||||
ModelNameEnum.StockMovement,
|
||||
].includes(this.schemaName);
|
||||
},
|
||||
},
|
||||
activated() {
|
||||
docsPath.value = docsPathMap[this.schemaName];
|
||||
|
@ -13,6 +13,10 @@
|
||||
async (exchangeRate) => await doc.set('exchangeRate', exchangeRate)
|
||||
"
|
||||
/>
|
||||
<Barcode
|
||||
v-if="doc.canEdit && fyo.singles.InventorySettings?.enableBarcodes"
|
||||
@item-selected="(name) => doc.addItem(name)"
|
||||
/>
|
||||
<Button
|
||||
v-if="!doc.isCancelled && !doc.dirty"
|
||||
:icon="true"
|
||||
@ -298,6 +302,7 @@ import { computed } from '@vue/reactivity';
|
||||
import { getDocStatus } from 'models/helpers';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import Barcode from 'src/components/Controls/Barcode.vue';
|
||||
import ExchangeRate from 'src/components/Controls/ExchangeRate.vue';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import Table from 'src/components/Controls/Table.vue';
|
||||
@ -332,6 +337,7 @@ export default {
|
||||
ExchangeRate,
|
||||
FormHeader,
|
||||
LinkedEntryWidget,
|
||||
Barcode,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
|
@ -31,6 +31,7 @@
|
||||
/>
|
||||
<Modal :open-modal="openExportModal" @closemodal="openExportModal = false">
|
||||
<ExportWizard
|
||||
class="w-form"
|
||||
:schema-name="schemaName"
|
||||
:title="pageTitle"
|
||||
:list-filters="listFilters"
|
||||
|
@ -126,7 +126,8 @@ export default {
|
||||
fieldnames.includes('hideGetStarted') ||
|
||||
fieldnames.includes('displayPrecision') ||
|
||||
fieldnames.includes('enableDiscounting') ||
|
||||
fieldnames.includes('enableInventory')
|
||||
fieldnames.includes('enableInventory') ||
|
||||
fieldnames.includes('enableBarcodes')
|
||||
) {
|
||||
this.showReloadToast();
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
}
|
||||
|
||||
* {
|
||||
outline-color: theme('colors.pink.500');
|
||||
font-variation-settings: 'slnt' 0deg;
|
||||
}
|
||||
.italic {
|
||||
|
@ -38,7 +38,7 @@ Accounts,Comptes,
|
||||
"Add a remark","Ajouter une remarque",
|
||||
"Add invoice terms","Ajouter votre politique de vente",
|
||||
"Add products or services that you buy from your suppliers","Ajoutez des produits ou services que vous achetez à vos fournisseurs",
|
||||
"Add products or services that you sell to your customers","Ajouter les produits ou services que vous vendez à vos clients.",
|
||||
"Add products or services that you sell to your customers","Ajouter les produits ou services que vous vendez à vos clients",
|
||||
Address,Adresse,
|
||||
"Address Display","Affichage de l'adresse",
|
||||
"Address Line 1","Ligne d'adresse 1",
|
||||
@ -76,7 +76,7 @@ Buildings,Bâtiments,
|
||||
Business,Entreprise,
|
||||
Cancel,Annuler,
|
||||
"Cancel ${0} ${1}?","Annuler ${0} ${1} ?",
|
||||
Cancelled,Anulé,
|
||||
Cancelled,Annulé,
|
||||
"Cannot Delete","Impossible à supprimer",
|
||||
"Cannot delete ${0} ${1} because of linked entries.","Impossible de supprimer ${0} ${1} à cause des entrées liées.",
|
||||
"Capital Equipments","Biens d'équipement",
|
||||
@ -143,8 +143,8 @@ Credit,Crédit,
|
||||
"Credit Card Entry","Entrée Carte de Crédit",
|
||||
"Credit Note",Avoir,
|
||||
Creditors,Créanciers,
|
||||
Currency,Monnaie,
|
||||
"Currency Name","Nom de la monnaie",
|
||||
Currency,Devise,
|
||||
"Currency Name","Nom de la devise",
|
||||
Current,Actuel,
|
||||
"Current Assets","Actifs courants",
|
||||
"Current Liabilities","Passifs courants",
|
||||
@ -153,11 +153,11 @@ Customer,Client,
|
||||
"Customer Created","Client créé",
|
||||
"Customer Currency","Monnaie du client",
|
||||
Customers,Clients,
|
||||
Customise,Personnalisez,
|
||||
"Customize your invoices by adding a logo and address details","Customisez vos factures en y ajoutant votre logo et adresse",
|
||||
Customise,Personnaliser,
|
||||
"Customize your invoices by adding a logo and address details",Personnalisez vos factures en y ajoutant votre logo et adresse,
|
||||
Dashboard,"Tableau de bord",
|
||||
"Data Import","Importation de données",
|
||||
"Database file: ${0}","Fichier de base de donnée : ${0}",
|
||||
"Database file: ${0}","Fichier de base de données : ${0}",
|
||||
Date,,
|
||||
"Date Format","Format de la date",
|
||||
Debit,Débit,
|
||||
@ -176,7 +176,7 @@ Details,Détails,
|
||||
"Direct Income","Revenu direct",
|
||||
Discount,Réduction,
|
||||
"Discount Account","Compte des réductions",
|
||||
"Discount Account is not set.","Le compte des réductions n'est pas défini",
|
||||
"Discount Account is not set.","Le compte des réductions n'est pas défini.",
|
||||
"Discount After Tax","Réduction après taxes",
|
||||
"Discount Amount","Montant de la réduction",
|
||||
"Discount Amount (${0}) cannot be greated than Amount (${1}).","Le montant de la réduction (${0}) ne peut pas être plus grand que le montant (${1}).",
|
||||
@ -189,7 +189,7 @@ Discounts,"Réductions",
|
||||
"Display Logo in Invoice","Afficher le logo sur la facture",
|
||||
"Display Precision","Précision de l'affichage",
|
||||
"Display Precision should have a value between 0 and 9.","La précision de l'affichage doit avoir une valeur comprise entre 0 et 9.",
|
||||
"Dividends Paid","Dividendes versés",
|
||||
"Dividends Paid","Dividendes versées",
|
||||
Docs,Documents,
|
||||
Documentation,Documentation,
|
||||
"Does Not Contain","Ne contient pas",
|
||||
@ -222,7 +222,7 @@ Expenses,Dépenses,
|
||||
"Expenses Included In Valuation","Dépenses incluses dans la valorisation ",
|
||||
Export,Exporter,
|
||||
"Export Failed","Export Échoué",
|
||||
"Export Successful","Export réussie",
|
||||
"Export Successful","Export réussi",
|
||||
Fax,Fax,
|
||||
Field,Champ,
|
||||
Fieldname,"Nom du champ",
|
||||
@ -377,13 +377,13 @@ Orange,,
|
||||
Organisation,Organisation,
|
||||
Outflow,Dépenses,
|
||||
"Outstanding Amount","Montant impayé",
|
||||
"Pad Zeros","Remplir de zéros",
|
||||
"Pad Zeros","Remplir de zéros",
|
||||
Page,,
|
||||
Paid,Payé,
|
||||
Parent,,
|
||||
"Parent Account","Compte parent",
|
||||
Party,Partie,
|
||||
"Patch Run","Éxecuter les correctifs",
|
||||
"Patch Run","Exécuter les correctifs",
|
||||
Pay,Payer,
|
||||
Payable,Payable,
|
||||
Payment,Paiement,
|
||||
@ -434,7 +434,7 @@ Purchases,Achats,
|
||||
Purple,Violet,
|
||||
Quantity,Quantité,
|
||||
Quarterly,Trimestriel,
|
||||
Quarters,Trimestre,
|
||||
Quarters,Trimestres,
|
||||
Rate,Tarif,
|
||||
"Rate (${0}) cannot be less zero.","Le Tarif (${0}) ne peut pas être inférieur à zéro.",
|
||||
"Rate (Company Currency)","Tarif (devise utilisée par la société)",
|
||||
@ -584,7 +584,7 @@ Terms,Conditions,
|
||||
"This action is permanent","Cette action est permanente",
|
||||
"This action is permanent and will cancel the following payment: ${0}","Cette action est permanente et annulera le paiement suivant : ${0}",
|
||||
"This action is permanent and will cancel the following payments: ${0}","Cette action est permanente et annulera les paiements suivants : ${0}",
|
||||
"This action is permanent and will delete associated ledger entries.","Cette action est permanente et supprimera les écritures dans le registre associées.",
|
||||
"This action is permanent and will delete associated ledger entries.","Cette action est permanente et supprimera les écritures dans le registre associés.",
|
||||
"This action is permanent.","Cette action est permanente",
|
||||
"Times New Roman",,
|
||||
"To Account","Au compte",
|
||||
|
|
Loading…
x
Reference in New Issue
Block a user