Skip to content

Commit

Permalink
Merge pull request #276 from itenium-be/peppol-xml
Browse files Browse the repository at this point in the history
Peppol xml front end changes
  • Loading branch information
Laoujin authored Dec 18, 2023
2 parents a01af54 + bba1105 commit 9f850d7
Show file tree
Hide file tree
Showing 15 changed files with 96 additions and 31 deletions.
2 changes: 1 addition & 1 deletion backend/src/controllers/attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export const getAttachmentController = async (req: Request, res: Response) => {
.collection(attachmentModelConfig.attachmentCollectionName)
.findOne({_id: new ObjectID(id)});

if (!attachment) {
if (!attachment || !attachment[type]) {
return res.status(500).send('Could not get the requested file.');
}

Expand Down
10 changes: 10 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 7 additions & 11 deletions frontend/src/actions/downloadActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import moment from 'moment';
import {catchHandler} from './utils/fetch';
import {buildUrl} from './utils/buildUrl';
import InvoiceModel from '../components/invoice/models/InvoiceModel';
import {Attachment} from '../models';
import {Attachment, CoreInvoiceAttachments} from '../models';
import {ClientModel} from '../components/client/models/ClientModels';
import {getInvoiceFileName, getDownloadUrl, previewPdf, downloadAttachment} from './utils/download-helpers';
import {ProjectMonthOverviewModel} from '../components/project/models/ProjectMonthModel';
Expand All @@ -14,19 +14,16 @@ import {authService} from '../components/users/authService';
export function getInvoiceDownloadUrl(
fileNameTemplate: string,
invoice: InvoiceModel,
attachment: 'pdf' | Attachment = 'pdf',
attachment: CoreInvoiceAttachments | Attachment = 'pdf',
downloadType?: 'preview' | 'download',
fullProjectMonth?: FullProjectMonthModel,
): string {

const fileType = invoice.isQuotation ? 'quotation' : 'invoice';
const fileName = attachment === 'pdf' || attachment.type === 'pdf'
? getInvoiceFileName(fileNameTemplate, invoice, fullProjectMonth)
: attachment.fileName;
const attachmentType = attachment === 'pdf' ? 'pdf' : attachment.type;
// return buildUrl(`/attachments/${fileType}/${invoice._id}/${attachmentType}/${encodeURIComponent(fileName)}${query}`);
const attachmentType = typeof attachment === 'string' ? attachment : attachment.type;
const filename = typeof attachment === 'string' || attachment.type === 'pdf' || attachment.type === 'xml' ?
getInvoiceFileName(fileNameTemplate, invoice, attachmentType, fullProjectMonth) : attachment.fileName;

return getDownloadUrl(fileType, invoice._id, attachmentType, fileName, downloadType);
return getDownloadUrl(fileType, invoice._id, attachmentType, filename, downloadType);
}


Expand Down Expand Up @@ -56,8 +53,7 @@ export function previewInvoice(fileName: string, data: InvoiceModel, fullProject
.responseType('blob')
.send(data)
.then(res => {
// console.log('previewInvoice response', res.body);
previewPdf(getInvoiceFileName(fileName, data, fullProjectMonth), res.body);
previewPdf(getInvoiceFileName(fileName, data, 'pdf', fullProjectMonth), res.body);
return res.text;
})
.catch(catchHandler);
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/actions/emailActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export function sendEmail(
fileName: `${invoiceReplacements(invoiceFileName, invoice, fullProjectMonth)}.pdf`,
fileType: 'application/pdf',
};
} else if (attachmentType === 'xml'){
return {
type: 'xml',
fileName: `${invoiceReplacements(invoiceFileName, invoice, fullProjectMonth)}.xml`,
fileType: 'application/xml',
};
}

const details = invoice.attachments.find(a => a.type === attachmentType);
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/actions/utils/download-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ export function downloadAttachment(fileName: string, content: Blob): void {
}



export function getInvoiceFileName(fileName: string, invoice: InvoiceModel, fullProjectMonth?: FullProjectMonthModel): string {
return `${invoiceReplacements(fileName, invoice, fullProjectMonth)}.pdf`;
export function getInvoiceFileName(fileName: string, invoice: InvoiceModel, extension: string, fullProjectMonth?: FullProjectMonthModel): string {
return `${invoiceReplacements(fileName, invoice, fullProjectMonth)}.${extension}`;
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,33 @@ import {Icon, IconProps} from '../Icon';
import {getInvoiceDownloadUrl} from '../../../actions/index';
import t from '../../../trans';
import {InvoiceModelProps} from '../../invoice/models/InvoiceModel';
import {Attachment} from '../../../models';
import {Attachment, CoreInvoiceAttachments} from '../../../models';
import {getAwesomeFileType} from '../../invoice/models/getAwesomeFileType';
import {ConfacState} from '../../../reducers/app-state';

type InvoiceDownloadIconProps = InvoiceModelProps & {
fileType: CoreInvoiceAttachments,
style?: {}
}


export const InvoiceDownloadIcon = ({invoice, ...props}: InvoiceModelProps) => {
export const InvoiceDownloadIcon = ({invoice, fileType, style, ...props}: InvoiceDownloadIconProps) => {
const configInvoiceFileName = useSelector((state: ConfacState) => state.config.invoiceFileName);

const defaultInvoiceFileName = invoice.client.invoiceFileName || configInvoiceFileName;
const url = getInvoiceDownloadUrl(defaultInvoiceFileName, invoice, 'pdf', 'download')
const url = getInvoiceDownloadUrl(defaultInvoiceFileName, invoice, fileType, 'download');
return (
<AttachmentDownloadIcon
downloadUrl={url}
attachment={invoice.attachments.find(a => a.type === 'pdf')}
attachment={invoice.attachments.find(a => a.type === fileType)}
style={style}
{...props}
/>
);
};


export const InvoicePreviewIcon = ({invoice, ...props}: InvoiceModelProps & IconProps) => {
export const InvoicePreviewIcon = ({ invoice, ...props }: InvoiceModelProps & IconProps) => {
const configInvoiceFileName = useSelector((state: ConfacState) => state.config.invoiceFileName);
const defaultInvoiceFileName = invoice.client.invoiceFileName || configInvoiceFileName;
const fileType = invoice.isQuotation ? 'quotation' : 'invoice';
Expand Down Expand Up @@ -60,7 +66,7 @@ type AttachmentDownloadIconProps = IconProps & {
export const AttachmentDownloadIcon = ({downloadUrl, attachment, ...props}: AttachmentDownloadIconProps) => (
<Icon
fa={`${getAwesomeFileType(attachment)} fa-2x`}
title={t('invoice.downloadAttachment', attachment && {type: attachment.fileName || attachment.type})}
title={t('invoice.downloadAttachment', attachment && {type: attachment.desc || attachment.fileName || attachment.type})}
{...props}
href={downloadUrl}
labelStyle={{fontSize: 16}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const _AttachmentsForm = (props: AttachmentsFormProps) => {
onAdd={(att: { file: File; type: string; }) => props.updateAttachment(props.model, modelType, att)}
/>
<Row>
{model.attachments.filter(att => att.type !== 'pdf').map(att => (
{model.attachments.filter(att => (att.type !== 'pdf' && att.type !== 'xml')).map(att => (
<Col
lg={4}
md={6}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const AttachmentsTypeSelect = ({value, onChange, ...props}: AttachmentsTy
<StringsSelect
value={value}
onChange={onChange}
options={[...options, 'pdf']}
options={[...options, 'pdf', 'xml']}
{...props}
/>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Creatable from 'react-select/creatable';
import React from 'react';
import {SelectItem} from '../../../../models';
import {t} from '../../../utils';

export type Option = {
label: string,
value: string
}

export type SimpleCreatableSelectWithOptionsProps = {
options: Option[];
value: string;
onChange: Function;
isClearable?: boolean;
};


export const SimpleCreatableSelectWithOptions = ({options, value, onChange, isClearable = false, ...props}: SimpleCreatableSelectWithOptionsProps) => {
const labelFromOptions = options.find(entry => entry.value === value);

return (
<Creatable
value={{label: labelFromOptions ? labelFromOptions.label : value, value}}
options={options}
onChange={itm => onChange(itm && (itm as SelectItem).value)}
isClearable={isClearable}
isMulti={false}
noOptionsMessage={() => t('controls.noResultsText')}
formatCreateLabel={itm => t('controls.addLabelText', {value: itm})}
placeholder={t('controls.selectPlaceholder')}
classNamePrefix="react-select"
/>
);
};
15 changes: 10 additions & 5 deletions frontend/src/components/controls/other/CountrySelect.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import React from 'react';
import { EnhanceInputWithLabel } from '../../enhancers/EnhanceInputWithLabel';
import { BaseInputProps } from '../form-controls/inputs/BaseInput';
import { SimpleCreatableSelect } from '../form-controls/select/SimpleCreatableSelect';
import {EnhanceInputWithLabel} from '../../enhancers/EnhanceInputWithLabel';
import {BaseInputProps} from '../form-controls/inputs/BaseInput';
import {Option, SimpleCreatableSelectWithOptions} from '../form-controls/select/SimpleCreatableSelectWithOptions';

export type CountrySelectProps = BaseInputProps<string>

const countries: string[] = ['België', 'Nederland', 'UK', 'Duitsland', 'Frankrijk'];
const countries: Option[] = [
{label: 'België', value: 'BE'},
{label: 'Nederland', value: 'NL'},
{label: 'UK', value: 'GB'},
{label: 'Duitsland', value: 'DE'},
{label: 'Frankrijk', value: 'FR'}];

const CountrySelectComponent = ({value, onChange, ...props}: CountrySelectProps) => {
return (
<SimpleCreatableSelect
<SimpleCreatableSelectWithOptions
value={value}
onChange={onChange}
options={countries}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/invoice/invoice-edit/EditInvoice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {Claim} from '../../users/models/UserModel';
import {useProjectsMonth} from '../../hooks/useProjects';
import {useParams} from 'react-router-dom';
import {ProjectMonthOrManualSelect} from '../../project/controls/ProjectMonthOrManualSelect';
import {InvoiceDownloadIcon} from '../../controls/attachments/AttachmentDownloadIcon';


import './EditInvoice.scss';
Expand All @@ -48,7 +49,7 @@ const EditInvoice = () => {
const dispatch = useDispatch();
// useEffect(() => window.scrollTo(0, 0)); // TODO: each keystroke made it scroll to top :(
const [showEmailModal, setEmailModal] = useState<EmailTemplate>(EmailTemplate.None);

let docTitle: string;
if (storeInvoice?._id) {
const name = t(isQuotation ? 'quotation.pdfName' : 'invoice.invoice');
Expand Down Expand Up @@ -102,6 +103,7 @@ const EditInvoice = () => {
variant="link"
/>
{initInvoice._id && <DownloadInvoiceButton invoice={initInvoice} />}
{storeInvoice?._id && !storeInvoice?.isQuotation && <InvoiceDownloadIcon invoice={invoice} fileType='xml' style={{color: '#0062cc', marginLeft: 20}} />}
</div>
</div>
</Col>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const InvoiceListRowActions = ({invoice, toggleValid, small = false}: Inv
/>
)}
<InvoiceVerifyIconToggle claim={Claim.ValidateInvoices} invoice={invoice} toggleValid={toggleValid} />
{!small && <InvoiceDownloadIcon invoice={invoice} />}
{!small && <InvoiceDownloadIcon invoice={invoice} fileType='pdf'/>}
<InvoicePreviewIcon invoice={invoice} />
{!small && (
<ConfirmedDeleteIcon
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/invoice/models/InvoiceModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default class InvoiceModel implements IAttachment {
this.orderNr = obj.orderNr || '';
this.verified = obj.verified || false;
this.discount = obj.discount;
this.attachments = obj.attachments || [{type: 'pdf'}];
this.attachments = obj.attachments || [{type: 'pdf', desc:'Factuur pdf'}, {type:'xml', desc:'PEPPOL xml'}];
this.isQuotation = obj.isQuotation || false;
this.lastEmail = obj.lastEmail;
this.note = obj.note || '';
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/components/invoice/models/getAwesomeFileType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export function getAwesomeFileType(att: Attachment | undefined): string {
// --> Looks too busy in the InvoiceList
// return 'fa fa-file-invoice-dollar';
}
if (att.type === 'xml') {
return 'far fa-file-code';
}
if (att.fileType === 'application/pdf') {
return 'far fa-file-pdf';
}
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ export type Attachment = {
originalFileName?: string,
fileType: string,
lastModifiedDate?: string,
desc?: string
}

export type CoreInvoiceAttachments = 'pdf' | 'xml';


/** Duplicated on backend */
export const TimesheetCheckAttachmentType = 'Timesheet check';
Expand Down

0 comments on commit 9f850d7

Please sign in to comment.