Merge branch 'release/2.1.0'
This commit is contained in:
@@ -435,6 +435,7 @@ export const fetchAllLocations = withUser(async (user:AuthenticatedUser, year:nu
|
|||||||
// "bills.hub3aText": 1,
|
// "bills.hub3aText": 1,
|
||||||
// project only file name - leave out file content so that
|
// project only file name - leave out file content so that
|
||||||
// less data is transferred to the client
|
// less data is transferred to the client
|
||||||
|
"utilBillsProofOfPaymentUploadedAt": 1,
|
||||||
"utilBillsProofOfPaymentAttachment.fileName": 1,
|
"utilBillsProofOfPaymentAttachment.fileName": 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -630,6 +631,17 @@ export const uploadUtilBillsProofOfPayment = async (locationID: string, formData
|
|||||||
noStore();
|
noStore();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
// check if attachment already exists for the location
|
||||||
|
const dbClient = await getDbClient();
|
||||||
|
|
||||||
|
const existingLocation = await dbClient.collection<BillingLocation>("lokacije")
|
||||||
|
.findOne({ _id: locationID }, { projection: { utilBillsProofOfPaymentAttachment: 1 } });
|
||||||
|
|
||||||
|
if (existingLocation?.utilBillsProofOfPaymentAttachment) {
|
||||||
|
return { success: false, error: 'An attachment already exists for this location' };
|
||||||
|
}
|
||||||
|
|
||||||
const file = formData.get('utilBillsProofOfPaymentAttachment') as File;
|
const file = formData.get('utilBillsProofOfPaymentAttachment') as File;
|
||||||
|
|
||||||
// Validate file type
|
// Validate file type
|
||||||
@@ -643,13 +655,14 @@ export const uploadUtilBillsProofOfPayment = async (locationID: string, formData
|
|||||||
return { success: false, error: 'Invalid file' };
|
return { success: false, error: 'Invalid file' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbClient = await getDbClient();
|
|
||||||
|
|
||||||
// Update the location with the attachment
|
// Update the location with the attachment
|
||||||
await dbClient.collection<BillingLocation>("lokacije")
|
await dbClient.collection<BillingLocation>("lokacije")
|
||||||
.updateOne(
|
.updateOne(
|
||||||
{ _id: locationID },
|
{ _id: locationID },
|
||||||
{ $set: { utilBillsProofOfPaymentAttachment: attachment } }
|
{ $set: {
|
||||||
|
utilBillsProofOfPaymentAttachment: attachment,
|
||||||
|
utilBillsProofOfPaymentUploadedAt: new Date()
|
||||||
|
} }
|
||||||
);
|
);
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export const addMonth = withUser(async (user:AuthenticatedUser, { year, month }:
|
|||||||
...prevLocation,
|
...prevLocation,
|
||||||
// clear properties specific to the month
|
// clear properties specific to the month
|
||||||
seenByTenant: undefined,
|
seenByTenant: undefined,
|
||||||
|
utilBillsProofOfPaymentUploadedAt: undefined,
|
||||||
utilBillsProofOfPaymentAttachment: undefined,
|
utilBillsProofOfPaymentAttachment: undefined,
|
||||||
// assign a new ID
|
// assign a new ID
|
||||||
_id: (new ObjectId()).toHexString(),
|
_id: (new ObjectId()).toHexString(),
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ export interface BillingLocation {
|
|||||||
seenByTenant?: boolean | null;
|
seenByTenant?: boolean | null;
|
||||||
/** (optional) utility bills proof of payment attachment */
|
/** (optional) utility bills proof of payment attachment */
|
||||||
utilBillsProofOfPaymentAttachment?: BillAttachment|null;
|
utilBillsProofOfPaymentAttachment?: BillAttachment|null;
|
||||||
|
/** (optional) date when utility bills proof of payment was uploaded */
|
||||||
|
utilBillsProofOfPaymentUploadedAt?: Date|null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum BilledTo {
|
export enum BilledTo {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { formatYearMonth } from "../lib/format";
|
|||||||
import { decodeFromImage, DecodeResult, findDecodePdf417 } from "../lib/pdf/barcodeDecoder";
|
import { decodeFromImage, DecodeResult, findDecodePdf417 } from "../lib/pdf/barcodeDecoder";
|
||||||
import { useLocale, useTranslations } from "next-intl";
|
import { useLocale, useTranslations } from "next-intl";
|
||||||
import { Pdf417Barcode } from "./Pdf417Barcode";
|
import { Pdf417Barcode } from "./Pdf417Barcode";
|
||||||
|
import { InfoBox } from "./InfoBox";
|
||||||
|
|
||||||
// Next.js does not encode an utf-8 file name correctly when sending a form with a file attachment
|
// Next.js does not encode an utf-8 file name correctly when sending a form with a file attachment
|
||||||
// This is a workaround for that
|
// This is a workaround for that
|
||||||
@@ -67,7 +68,7 @@ export const BillEditForm: FC<BillEditFormProps> = ({ location, bill }) => {
|
|||||||
}, [bill?.barcodeImage, hub3aText]);
|
}, [bill?.barcodeImage, hub3aText]);
|
||||||
|
|
||||||
|
|
||||||
const billedTo_handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const billedTo_handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
setBilledToValue(event.target.value as BilledTo);
|
setBilledToValue(event.target.value as BilledTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,33 +228,14 @@ export const BillEditForm: FC<BillEditFormProps> = ({ location, bill }) => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-control mt-4">
|
<fieldset className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4 pb-2 mt-4">
|
||||||
<div className="flex items-center gap-4">
|
<legend className="fieldset-legend font-semibold uppercase">{t("billed-to-legend")}</legend>
|
||||||
<span className="label-text">{t("billed-to-label")}</span>
|
<select className="select select-bordered w-full" name="billedTo" defaultValue={billedToValue} onChange={billedTo_handleChange}>
|
||||||
<label className="label cursor-pointer gap-2">
|
<option value={BilledTo.Tenant}>{t("billed-to-tenant-option")}</option>
|
||||||
<input
|
<option value={BilledTo.Landlord}>{t("billed-to-landlord-option")}</option>
|
||||||
type="radio"
|
</select>
|
||||||
name="billedTo"
|
<InfoBox className="m-0 mt-3 mb-1 p-0">{t("billed-to-info")}</InfoBox>
|
||||||
value={BilledTo.Tenant}
|
</fieldset>
|
||||||
className="radio radio-primary"
|
|
||||||
checked={billedToValue === BilledTo.Tenant}
|
|
||||||
onChange={billedTo_handleChange}
|
|
||||||
/>
|
|
||||||
<span className="label-text">{t("billed-to-tenant-option")}</span>
|
|
||||||
</label>
|
|
||||||
<label className="label cursor-pointer gap-2">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="billedTo"
|
|
||||||
value={BilledTo.Landlord}
|
|
||||||
className="radio radio-primary"
|
|
||||||
checked={billedToValue === BilledTo.Landlord}
|
|
||||||
onChange={billedTo_handleChange}
|
|
||||||
/>
|
|
||||||
<span className="label-text">{t("billed-to-landlord-option")}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Show toggle only when adding a new bill (not editing) */}
|
{/* Show toggle only when adding a new bill (not editing) */}
|
||||||
{!bill && (
|
{!bill && (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { CheckCircleIcon, Cog8ToothIcon, PlusCircleIcon, ShareIcon, BanknotesIcon, DocumentIcon, EnvelopeIcon, LinkIcon, EyeIcon } from "@heroicons/react/24/outline";
|
import { CheckCircleIcon, Cog8ToothIcon, PlusCircleIcon, ShareIcon, BanknotesIcon, EyeIcon, TicketIcon } from "@heroicons/react/24/outline";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { BillBadge } from "./BillBadge";
|
import { BillBadge } from "./BillBadge";
|
||||||
import { BillingLocation } from "../lib/db-types";
|
import { BillingLocation } from "../lib/db-types";
|
||||||
@@ -23,7 +23,7 @@ export const LocationCard: FC<LocationCardProps> = ({ location, currency }) => {
|
|||||||
bills,
|
bills,
|
||||||
seenByTenant,
|
seenByTenant,
|
||||||
// NOTE: only the fileName is projected from the DB to reduce data transfer
|
// NOTE: only the fileName is projected from the DB to reduce data transfer
|
||||||
utilBillsProofOfPaymentAttachment
|
utilBillsProofOfPaymentUploadedAt
|
||||||
} = location;
|
} = location;
|
||||||
|
|
||||||
const t = useTranslations("home-page.location-card");
|
const t = useTranslations("home-page.location-card");
|
||||||
@@ -58,7 +58,7 @@ export const LocationCard: FC<LocationCardProps> = ({ location, currency }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{monthlyExpense > 0 || seenByTenant || utilBillsProofOfPaymentAttachment ?
|
{monthlyExpense > 0 || seenByTenant || utilBillsProofOfPaymentUploadedAt ?
|
||||||
|
|
||||||
<fieldset className="card card-compact card-bordered border-1 border-neutral p-3 mt-2 mr-[3.5rem]">
|
<fieldset className="card card-compact card-bordered border-1 border-neutral p-3 mt-2 mr-[3.5rem]">
|
||||||
<legend className="fieldset-legend px-2 text-sm font-semibold uppercase">{t("monthly-statement-legend")}</legend>
|
<legend className="fieldset-legend px-2 text-sm font-semibold uppercase">{t("monthly-statement-legend")}</legend>
|
||||||
@@ -78,13 +78,13 @@ export const LocationCard: FC<LocationCardProps> = ({ location, currency }) => {
|
|||||||
<CheckCircleIcon className="h-5 w-5 text-success" />
|
<CheckCircleIcon className="h-5 w-5 text-success" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{utilBillsProofOfPaymentAttachment && (
|
{utilBillsProofOfPaymentUploadedAt && (
|
||||||
<Link
|
<Link
|
||||||
href={`/share/proof-of-payment/${_id}/`}
|
href={`/share/proof-of-payment/${_id}/`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="flex items-center gap-2 mt-2 ml-4"
|
className="flex items-center gap-2 mt-2 ml-4"
|
||||||
>
|
>
|
||||||
<LinkIcon className="h-5 w-5" />
|
<TicketIcon className="h-5 w-5" />
|
||||||
<span className="text-sm">{t("download-proof-of-payment-label")}</span>
|
<span className="text-sm">{t("download-proof-of-payment-label")}</span>
|
||||||
<CheckCircleIcon className="h-5 w-5 text-success" />
|
<CheckCircleIcon className="h-5 w-5 text-success" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ export const LocationEditForm: FC<LocationEditFormProps> = ({ location, yearMont
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4 pb-2 mt-4">
|
<fieldset className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4 pb-2 mt-4">
|
||||||
<legend className="fieldset-legend font-semibold uppercase">{t("tenant-2d-code-legend")}</legend>
|
<legend className="fieldset-legend font-semibold uppercase">{t("tenant-2d-code-legend")}</legend>
|
||||||
|
|
||||||
<InfoBox className="p-1 mb-1">{t("tenant-2d-code-info")}</InfoBox>
|
<InfoBox className="p-1 mb-1">{t("tenant-2d-code-info")}</InfoBox>
|
||||||
|
|
||||||
<fieldset className="fieldset">
|
<fieldset className="fieldset">
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
import { useState, useEffect, FC } from 'react';
|
import { useState, useEffect, FC } from 'react';
|
||||||
import { generateBarcode } from '../lib/pdf/pdf417';
|
import { generateBarcode } from '../lib/pdf/pdf417';
|
||||||
import { renderBarcode } from '../lib/pdf/renderBarcode';
|
import { renderBarcode } from '../lib/pdf/renderBarcode';
|
||||||
import { EncodePayment, PaymentParams } from 'hub-3a-payment-encoder';
|
|
||||||
|
|
||||||
export const Pdf417Barcode:FC<{hub3aText:string, className?: string}> = ({hub3aText: hub3a_text, className}) => {
|
export const Pdf417Barcode:FC<{hub3aText:string, className?: string}> = ({hub3aText: hub3a_text, className}) => {
|
||||||
const [bitmapData, setBitmapData] = useState<string | undefined>(undefined);
|
const [bitmapData, setBitmapData] = useState<string | undefined>(undefined);
|
||||||
|
|||||||
@@ -37,13 +37,6 @@ export const PrintPreview: React.FC<PrintPreviewProps> = ({ data, year, month, t
|
|||||||
print-color-adjust: exact !important;
|
print-color-adjust: exact !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.print-barcode-img {
|
|
||||||
width: 69.6mm !important;
|
|
||||||
max-width: 69.6mm !important;
|
|
||||||
height: auto !important;
|
|
||||||
max-height: 85px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-table {
|
.print-table {
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
}
|
}
|
||||||
@@ -79,10 +72,6 @@ export const PrintPreview: React.FC<PrintPreviewProps> = ({ data, year, month, t
|
|||||||
.print-table thead tr {
|
.print-table thead tr {
|
||||||
background: #f5f5f5 !important;
|
background: #f5f5f5 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.print-table td img {
|
|
||||||
margin: 5em auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
|
|
||||||
@@ -144,15 +133,14 @@ export const PrintPreview: React.FC<PrintPreviewProps> = ({ data, year, month, t
|
|||||||
<div className="flex justify-center items-center">
|
<div className="flex justify-center items-center">
|
||||||
{
|
{
|
||||||
item.hub3aText ?
|
item.hub3aText ?
|
||||||
<Pdf417Barcode hub3aText={item.hub3aText} />
|
<Pdf417Barcode hub3aText={item.hub3aText} className="max-h-28 w-auto max-w-[270px] print:m-[5em_auto] print:h-[auto] print:max-h-[85px] print:w-[69.6mm] print:max-w-[69.6mm]" />
|
||||||
: (
|
: (
|
||||||
// LEGACY SUPPORT ... untill all bills have been migrated
|
// LEGACY SUPPORT ... untill all bills have been migrated
|
||||||
item.barcodeImage ?
|
item.barcodeImage ?
|
||||||
<img
|
<img
|
||||||
src={item.barcodeImage.startsWith('data:') ? item.barcodeImage : `data:image/png;base64,${item.barcodeImage}`}
|
src={item.barcodeImage.startsWith('data:') ? item.barcodeImage : `data:image/png;base64,${item.barcodeImage}`}
|
||||||
alt={`Barcode for ${item.billName}`}
|
alt={`Barcode for ${item.billName}`}
|
||||||
className="max-h-28 w-auto border border-gray-300 rounded print-barcode-img"
|
className="max-h-28 w-auto max-w-[270px] print:m-[5em_auto] print:h-[auto] print:max-h-[85px] print:w-[69.6mm] print:max-w-[69.6mm]"
|
||||||
style={{ maxWidth: '270px' }}
|
|
||||||
/> : null
|
/> : null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export const ViewBillCard:FC<ViewBillCardProps> = ({ location, bill }) => {
|
|||||||
{
|
{
|
||||||
hub3aText ?
|
hub3aText ?
|
||||||
<div className="form-control p-1">
|
<div className="form-control p-1">
|
||||||
<label className="cursor-pointer label p-2 grow bg-white justify-center">
|
<label className="label p-2 grow bg-white border border-gray-300 rounded-box justify-center">
|
||||||
<Pdf417Barcode hub3aText={hub3aText} />
|
<Pdf417Barcode hub3aText={hub3aText} />
|
||||||
</label>
|
</label>
|
||||||
<p className="text-xs my-1">{t.rich('barcode-disclaimer', { br: () => <br /> })}</p>
|
<p className="text-xs my-1">{t.rich('barcode-disclaimer', { br: () => <br /> })}</p>
|
||||||
@@ -70,7 +70,7 @@ export const ViewBillCard:FC<ViewBillCardProps> = ({ location, bill }) => {
|
|||||||
// LEGACY SUPPORT ... untill all bills have been migrated
|
// LEGACY SUPPORT ... untill all bills have been migrated
|
||||||
barcodeImage ?
|
barcodeImage ?
|
||||||
<div className="p-1">
|
<div className="p-1">
|
||||||
<label className="label p-2 grow bg-white">
|
<label className="label p-2 grow bg-white border border-gray-300 rounded-box justify-center">
|
||||||
<img src={barcodeImage} className="grow sm:max-w-[350px]" alt="2D Barcode" />
|
<img src={barcodeImage} className="grow sm:max-w-[350px]" alt="2D Barcode" />
|
||||||
</label>
|
</label>
|
||||||
<p className="text-xs my-1">{t.rich('barcode-disclaimer', { br: () => <br /> })}</p>
|
<p className="text-xs my-1">{t.rich('barcode-disclaimer', { br: () => <br /> })}</p>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { FC, useState } from "react";
|
import { FC, useEffect, useMemo, useState } from "react";
|
||||||
import { BillAttachment, BilledTo, BillingLocation, UserSettings } from "../lib/db-types";
|
import { BillAttachment, BilledTo, BillingLocation, UserSettings } from "../lib/db-types";
|
||||||
import { formatYearMonth } from "../lib/format";
|
import { formatYearMonth } from "../lib/format";
|
||||||
import { formatCurrency } from "../lib/formatStrings";
|
import { formatCurrency } from "../lib/formatStrings";
|
||||||
@@ -29,13 +29,15 @@ export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettin
|
|||||||
tenantTown,
|
tenantTown,
|
||||||
generateTenantCode,
|
generateTenantCode,
|
||||||
// NOTE: only the fileName is projected from the DB to reduce data transfer
|
// NOTE: only the fileName is projected from the DB to reduce data transfer
|
||||||
utilBillsProofOfPaymentAttachment
|
utilBillsProofOfPaymentAttachment,
|
||||||
|
utilBillsProofOfPaymentUploadedAt,
|
||||||
} = location;
|
} = location;
|
||||||
|
|
||||||
const t = useTranslations("home-page.location-card");
|
const t = useTranslations("home-page.location-card");
|
||||||
|
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
const [uploadError, setUploadError] = useState<string | null>(null);
|
const [uploadError, setUploadError] = useState<string | null>(null);
|
||||||
|
const [attachmentUploadedAt, setAttachmentUploadedAt ] = useState<Date | null>(utilBillsProofOfPaymentUploadedAt ?? null);
|
||||||
const [attachmentFilename, setAttachmentFilename] = useState(utilBillsProofOfPaymentAttachment?.fileName);
|
const [attachmentFilename, setAttachmentFilename] = useState(utilBillsProofOfPaymentAttachment?.fileName);
|
||||||
|
|
||||||
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
@@ -60,6 +62,7 @@ export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettin
|
|||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
setAttachmentFilename(file.name);
|
setAttachmentFilename(file.name);
|
||||||
|
setAttachmentUploadedAt(new Date());
|
||||||
} else {
|
} else {
|
||||||
setUploadError(result.error || 'Upload failed');
|
setUploadError(result.error || 'Upload failed');
|
||||||
}
|
}
|
||||||
@@ -74,27 +77,40 @@ export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettin
|
|||||||
// sum all the billAmounts (only for bills billed to tenant)
|
// sum all the billAmounts (only for bills billed to tenant)
|
||||||
const monthlyExpense = bills.reduce((acc, bill) => (bill.paid && (bill.billedTo ?? BilledTo.Tenant) === BilledTo.Tenant) ? acc + (bill.payedAmount ?? 0) : acc, 0);
|
const monthlyExpense = bills.reduce((acc, bill) => (bill.paid && (bill.billedTo ?? BilledTo.Tenant) === BilledTo.Tenant) ? acc + (bill.payedAmount ?? 0) : acc, 0);
|
||||||
|
|
||||||
const locationNameTrimmed_max20 = locationName.trimEnd().trimEnd().substring(0,19);
|
const { hub3aText, paymentParams } = useMemo(() => {
|
||||||
|
|
||||||
const paymentParams:PaymentParams = {
|
if(!userSettings?.show2dCodeInMonthlyStatement || !generateTenantCode) {
|
||||||
Iznos: (monthlyExpense/100).toFixed(2).replace(".",","),
|
return {
|
||||||
ImePlatitelja: tenantName ?? "",
|
hub3aText: "",
|
||||||
AdresaPlatitelja: tenantStreet ?? "",
|
paymentParams: {} as PaymentParams
|
||||||
SjedistePlatitelja: tenantTown ?? "",
|
};
|
||||||
Primatelj: userSettings?.ownerName ?? "",
|
}
|
||||||
AdresaPrimatelja: userSettings?.ownerStreet ?? "",
|
|
||||||
SjedistePrimatelja: userSettings?.ownerTown ?? "",
|
|
||||||
IBAN: userSettings?.ownerIBAN ?? "",
|
|
||||||
ModelPlacanja: "HR00",
|
|
||||||
PozivNaBroj: formatYearMonth(yearMonth),
|
|
||||||
SifraNamjene: "",
|
|
||||||
OpisPlacanja: `Režije-${locationNameTrimmed_max20}-${formatYearMonth(yearMonth)}`, // max length 35 = "Režije-" (7) + locationName (20) + "-" (1) + "YYYY-MM" (7)
|
|
||||||
};
|
|
||||||
|
|
||||||
const hub3a_text = EncodePayment(paymentParams);
|
const locationNameTrimmed_max20 = locationName.trimEnd().trimEnd().substring(0,19);
|
||||||
|
|
||||||
|
const paymentParams:PaymentParams = {
|
||||||
|
Iznos: (monthlyExpense/100).toFixed(2).replace(".",","),
|
||||||
|
ImePlatitelja: tenantName ?? "",
|
||||||
|
AdresaPlatitelja: tenantStreet ?? "",
|
||||||
|
SjedistePlatitelja: tenantTown ?? "",
|
||||||
|
Primatelj: userSettings?.ownerName ?? "",
|
||||||
|
AdresaPrimatelja: userSettings?.ownerStreet ?? "",
|
||||||
|
SjedistePrimatelja: userSettings?.ownerTown ?? "",
|
||||||
|
IBAN: userSettings?.ownerIBAN ?? "",
|
||||||
|
ModelPlacanja: "HR00",
|
||||||
|
PozivNaBroj: formatYearMonth(yearMonth),
|
||||||
|
SifraNamjene: "",
|
||||||
|
OpisPlacanja: `Režije-${locationNameTrimmed_max20}-${formatYearMonth(yearMonth)}`, // max length 35 = "Režije-" (7) + locationName (20) + "-" (1) + "YYYY-MM" (7)
|
||||||
|
};
|
||||||
|
|
||||||
|
return({
|
||||||
|
hub3aText: EncodePayment(paymentParams),
|
||||||
|
paymentParams
|
||||||
|
});
|
||||||
|
}, [userSettings?.show2dCodeInMonthlyStatement, generateTenantCode, locationName, tenantName, tenantStreet, tenantTown, userSettings, monthlyExpense, yearMonth]);
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<div data-key={_id } className="card card-compact card-bordered max-w-[30em] min-w-[350px] bg-base-100 border-1 border-neutral my-1">
|
<div data-key={_id } className="card card-compact card-bordered max-w-[30em] min-w-[330px] bg-base-100 border-1 border-neutral my-1">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<h2 className="card-title mr-[2em] text-[1.3rem]">{formatYearMonth(yearMonth)} {locationName}</h2>
|
<h2 className="card-title mr-[2em] text-[1.3rem]">{formatYearMonth(yearMonth)} {locationName}</h2>
|
||||||
<div className="card-actions mt-[1em] mb-[1em]">
|
<div className="card-actions mt-[1em] mb-[1em]">
|
||||||
@@ -118,31 +134,38 @@ export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettin
|
|||||||
<li><strong>{t("payment-recipient-label")}</strong> <pre className="inline pl-1">{paymentParams.Primatelj}</pre></li>
|
<li><strong>{t("payment-recipient-label")}</strong> <pre className="inline pl-1">{paymentParams.Primatelj}</pre></li>
|
||||||
<li><strong>{t("payment-recipient-address-label")}</strong><pre className="inline pl-1">{paymentParams.AdresaPrimatelja}</pre></li>
|
<li><strong>{t("payment-recipient-address-label")}</strong><pre className="inline pl-1">{paymentParams.AdresaPrimatelja}</pre></li>
|
||||||
<li><strong>{t("payment-recipient-city-label")}</strong><pre className="inline pl-1">{paymentParams.SjedistePrimatelja}</pre></li>
|
<li><strong>{t("payment-recipient-city-label")}</strong><pre className="inline pl-1">{paymentParams.SjedistePrimatelja}</pre></li>
|
||||||
<li><strong>{t("payment-amount-label")}</strong> <pre className="inline pl-1">{paymentParams.Iznos}</pre></li>
|
<li><strong>{t("payment-amount-label")}</strong> <pre className="inline pl-1">{paymentParams.Iznos} { userSettings?.currency }</pre></li>
|
||||||
<li><strong>{t("payment-description-label")}</strong><pre className="inline pl-1">{paymentParams.OpisPlacanja}</pre></li>
|
<li><strong>{t("payment-description-label")}</strong><pre className="inline pl-1">{paymentParams.OpisPlacanja}</pre></li>
|
||||||
<li><strong>{t("payment-model-label")}</strong><pre className="inline pl-1">{paymentParams.ModelPlacanja}</pre></li>
|
<li><strong>{t("payment-model-label")}</strong><pre className="inline pl-1">{paymentParams.ModelPlacanja}</pre></li>
|
||||||
<li><strong>{t("payment-reference-label")}</strong><pre className="inline pl-1">{paymentParams.PozivNaBroj}</pre></li>
|
<li><strong>{t("payment-reference-label")}</strong><pre className="inline pl-1">{paymentParams.PozivNaBroj}</pre></li>
|
||||||
<li><strong>{t("payment-purpose-code-label")}</strong><pre className="inline pl-1">{paymentParams.SifraNamjene}</pre></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<Pdf417Barcode hub3aText={hub3a_text} />
|
<label className="label p-2 grow bg-white border border-gray-300 rounded-box justify-center">
|
||||||
|
<Pdf417Barcode hub3aText={hub3aText} />
|
||||||
|
</label>
|
||||||
</>
|
</>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
<fieldset className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4 pb-2 pt-0 mt-2">
|
<fieldset className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4 pb-2 pt-0 mt-2">
|
||||||
<legend className="fieldset-legend font-semibold uppercase">{t("upload-proof-of-payment-legend")}</legend>
|
<legend className="fieldset-legend font-semibold uppercase">{t("upload-proof-of-payment-legend")}</legend>
|
||||||
|
{
|
||||||
{attachmentFilename ? (
|
// IF proof of payment was uploaded
|
||||||
<div className="mt-3 ml-[-.5rem]">
|
attachmentUploadedAt ? (
|
||||||
<Link
|
// IF file name is available, show link to download
|
||||||
href={`/share/proof-of-payment/${_id}/`}
|
// ELSE it's not available that means that the uploaded file was purged by housekeeping
|
||||||
target="_blank"
|
// -> don't show anything
|
||||||
className='text-center w-full max-w-[20rem] text-nowrap truncate inline-block'
|
attachmentFilename ? (
|
||||||
>
|
<div className="mt-3 ml-[-.5rem]">
|
||||||
<DocumentIcon className="h-[1em] w-[1em] text-2xl inline-block mr-1" />
|
<Link
|
||||||
{decodeURIComponent(attachmentFilename)}
|
href={`/share/proof-of-payment/${_id}/`}
|
||||||
</Link>
|
target="_blank"
|
||||||
</div>
|
className='text-center w-full max-w-[20rem] text-nowrap truncate inline-block'
|
||||||
) : (
|
>
|
||||||
|
<DocumentIcon className="h-[1em] w-[1em] text-2xl inline-block mr-1" />
|
||||||
|
{decodeURIComponent(attachmentFilename)}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
) : null
|
||||||
|
) : /* ELSE show upload input */ (
|
||||||
<div className="form-control w-full">
|
<div className="form-control w-full">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text">{t("upload-proof-of-payment-label")}</span>
|
<span className="label-text">{t("upload-proof-of-payment-label")}</span>
|
||||||
|
|||||||
@@ -124,9 +124,10 @@
|
|||||||
},
|
},
|
||||||
"attachment": "Attachment",
|
"attachment": "Attachment",
|
||||||
"back-button": "Back",
|
"back-button": "Back",
|
||||||
"billed-to-label": "Billed to",
|
"billed-to-legend": "Who bears the cost?",
|
||||||
"billed-to-tenant-option": "tenant",
|
"billed-to-tenant-option": "the tenant bears this cost",
|
||||||
"billed-to-landlord-option": "landlord"
|
"billed-to-landlord-option": "the landlord bears this cost",
|
||||||
|
"billed-to-info": "This option is intended for cases where part of the utility costs are not charged to the tenant. If 'the landlord bears this cost' is selected, this bill will not be included in the monthly statement shown to the tenant."
|
||||||
},
|
},
|
||||||
"location-delete-form": {
|
"location-delete-form": {
|
||||||
"text": "Please confirm deletion of realestate \"<strong>{name}</strong>\".",
|
"text": "Please confirm deletion of realestate \"<strong>{name}</strong>\".",
|
||||||
|
|||||||
@@ -123,9 +123,10 @@
|
|||||||
},
|
},
|
||||||
"attachment": "Privitak",
|
"attachment": "Privitak",
|
||||||
"back-button": "Nazad",
|
"back-button": "Nazad",
|
||||||
"billed-to-label": "Račun plaća",
|
"billed-to-legend": "Tko snosi trošak?",
|
||||||
"billed-to-tenant-option": "podstanar",
|
"billed-to-tenant-option": "ovaj trošak snosi podstanar",
|
||||||
"billed-to-landlord-option": "vlasnik"
|
"billed-to-landlord-option": "ovaj trošak snosi vlasnik",
|
||||||
|
"billed-to-info": "Ova opcija je predviđena za slučaj kada se dio režija ne naplaćuje od podstanara. Ako je odabrano 'trošak snosi vlasnik', ovaj račun neće biti uključen u mjesečni obračun koji se prikazuje podstanaru."
|
||||||
},
|
},
|
||||||
"location-delete-form": {
|
"location-delete-form": {
|
||||||
"text": "Molim potvrdi brisanje nekretnine \"<strong>{name}</strong>\".",
|
"text": "Molim potvrdi brisanje nekretnine \"<strong>{name}</strong>\".",
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "evidencija-rezija",
|
"name": "evidencija-rezija",
|
||||||
"version": "2.0.1",
|
"version": "2.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"version": "2.0.1",
|
"version": "2.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
|
|||||||
@@ -58,5 +58,5 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.17.0"
|
"node": ">=18.17.0"
|
||||||
},
|
},
|
||||||
"version": "2.0.1"
|
"version": "2.1.0"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user