change invoice action impl. changing .http sample file

This commit is contained in:
Dmitriynj
2020-12-08 21:48:57 +03:00
committed by Daniel Hutzel
parent 72616ae4ce
commit aeafb1d010
13 changed files with 142 additions and 110 deletions

View File

@@ -15,6 +15,7 @@
"@testing-library/user-event": "^7.1.2", "@testing-library/user-event": "^7.1.2",
"@umijs/hooks": "^1.9.3", "@umijs/hooks": "^1.9.3",
"antd": "^4.8.2", "antd": "^4.8.2",
"@ant-design/icons": "4.3.0",
"axios": "^0.20.0", "axios": "^0.20.0",
"clean-webpack-plugin": "^3.0.0", "clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^6.3.2", "copy-webpack-plugin": "^6.3.2",

View File

@@ -43,13 +43,10 @@ const invoice = (tracks) => {
return axiosInstance.post( return axiosInstance.post(
`${INVOICES_SERVICE}/invoice`, `${INVOICES_SERVICE}/invoice`,
{ {
tracks: tracks.map(({ unitPrice, ID }) => ({ tracks,
unitPrice: `${unitPrice}`,
ID,
})),
}, },
{ {
headers: { 'content-type': 'application/json;IEEE754Compatible=true' }, headers: { 'content-type': 'application/json' },
} }
); );
}; };

View File

@@ -36,8 +36,8 @@ const Header = () => {
history.go(RELOAD_LOCATION_NUMBER); history.go(RELOAD_LOCATION_NUMBER);
}; };
const localeElements = AVAILABLE_LOCALES.filter((localeName) => localeName !== locale).map( const localeElements = AVAILABLE_LOCALES.filter((localeName) => localeName !== locale).map(
(curLocale, index) => ( (curLocale) => (
<Menu.Item key={`${index}${curLocale}`} onClick={() => onChangeLocale(curLocale)}> <Menu.Item key={curLocale} onClick={() => onChangeLocale(curLocale)}>
{curLocale} {curLocale}
</Menu.Item> </Menu.Item>
) )
@@ -83,9 +83,6 @@ const Header = () => {
Manages Manages
</Menu.Item> </Menu.Item>
)} )}
<span>
{loading && <Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} />}
</span>
</Menu> </Menu>
<Menu <Menu
@@ -94,6 +91,9 @@ const Header = () => {
mode="horizontal" mode="horizontal"
selectedKeys={currentKey} selectedKeys={currentKey}
> >
<Menu.Item>
{loading && <Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} />}
</Menu.Item>
{haveInvoicedItems && ( {haveInvoicedItems && (
<Menu.Item <Menu.Item
style={{ style={{
@@ -119,21 +119,19 @@ const Header = () => {
</div> </div>
</Menu.Item> </Menu.Item>
)} )}
<SubMenu title={locale}>{localeElements}</SubMenu> <SubMenu title={locale}>{localeElements}</SubMenu>
{user ? (
{!!user ? (
<Menu.Item <Menu.Item
onClick={onUserLogout} onClick={onUserLogout}
danger danger
icon={<LogoutOutlined style={{ fontSize: 16 }} />} icon={<LogoutOutlined style={{ fontSize: 16 }} />}
></Menu.Item> />
) : ( ) : (
<Menu.Item <Menu.Item
key="/login" key="/login"
onClick={() => history.push('/login')} onClick={() => history.push('/login')}
icon={<LoginOutlined style={{ fontSize: 16 }} />} icon={<LoginOutlined style={{ fontSize: 16 }} />}
></Menu.Item> />
)} )}
</Menu> </Menu>
</div> </div>

View File

@@ -38,9 +38,8 @@ const InvoicePage = () => {
const onBuy = () => { const onBuy = () => {
setLoading(true); setLoading(true);
invoice( invoice(
invoicedItems.map(({ ID, unitPrice }) => ({ invoicedItems.map(({ ID }) => ({
ID, ID,
unitPrice,
})) }))
) )
.then(() => { .then(() => {

View File

@@ -153,7 +153,7 @@ const TracksContainer = () => {
const isAlreadyOrdered = !isEmployee && track.alreadyOrdered; const isAlreadyOrdered = !isEmployee && track.alreadyOrdered;
const onDeleteTrack = isEmployee && ((ID) => deleteTrack(ID)); const onDeleteTrack = isEmployee && ((ID) => deleteTrack(ID));
return ( return (
<Col key={track.ID} className="gutter-row" span={8}> <Col key={`track-col${track.ID}`} className="gutter-row" span={8}>
<TrackComponent <TrackComponent
initialTrack={track} initialTrack={track}
onDeleteTrack={onDeleteTrack} onDeleteTrack={onDeleteTrack}

View File

@@ -15,11 +15,11 @@ const REQUIRED = [
}, },
]; ];
const getAlbums = function (value) { function getAlbums(value) {
return fetchAlbumsByName(value, ALBUMS_LIMIT) return fetchAlbumsByName(value, ALBUMS_LIMIT)
.then((response) => response.data.value) .then((response) => response.data.value)
.catch(this.handleError); .catch(this.handleError);
}; }
const TrackForm = ({ initialAlbumTitle }) => { const TrackForm = ({ initialAlbumTitle }) => {
const { handleError } = useErrors(); const { handleError } = useErrors();
@@ -89,5 +89,8 @@ const TrackForm = ({ initialAlbumTitle }) => {
TrackForm.propTypes = { TrackForm.propTypes = {
initialAlbumTitle: PropTypes.string, initialAlbumTitle: PropTypes.string,
}; };
TrackForm.defaultProps = {
initialAlbumTitle: undefined,
};
export { TrackForm }; export { TrackForm };

View File

@@ -18,6 +18,16 @@ const EditAction = ({ ID, name, composer, genre, unitPrice, album, afterTrackUpd
setVisible(true); setVisible(true);
}; };
const afterCloseModal = () => {
setUpdateLoading(true);
getTrack(ID)
.then((response) => {
afterTrackUpdate(response.data);
setUpdateLoading(false);
})
.catch(handleError);
};
const onFinish = (value) => { const onFinish = (value) => {
setConfirmLoading(true); setConfirmLoading(true);
updateTrack({ updateTrack({
@@ -45,16 +55,6 @@ const EditAction = ({ ID, name, composer, genre, unitPrice, album, afterTrackUpd
setVisible(false); setVisible(false);
}; };
const afterCloseModal = () => {
setUpdateLoading(true);
getTrack(ID)
.then((response) => {
afterTrackUpdate(response.data);
setUpdateLoading(false);
})
.catch(handleError);
};
return ( return (
<> <>
{updateLoading ? <LoadingOutlined /> : <EditOutlined onClick={onShowModal} />} {updateLoading ? <LoadingOutlined /> : <EditOutlined onClick={onShowModal} />}
@@ -86,11 +86,11 @@ const EditAction = ({ ID, name, composer, genre, unitPrice, album, afterTrackUpd
onFinish={onFinish} onFinish={onFinish}
onFinishFailed={() => console.log('Not valid params provided')} onFinishFailed={() => console.log('Not valid params provided')}
initialValues={{ initialValues={{
name: name, name,
composer: composer, composer,
genreID: genre.ID, genreID: genre.ID,
albumID: album.ID, albumID: album.ID,
unitPrice: unitPrice, unitPrice,
}} }}
> >
<TrackForm initialAlbumTitle={album.title} /> <TrackForm initialAlbumTitle={album.title} />

View File

@@ -1,9 +1,10 @@
import React, { useState, useRef } from "react"; import React, { useState, useRef } from 'react';
import { Card } from "antd"; import { Card } from 'antd';
import { EditAction } from "./EditAction"; import PropTypes from 'prop-types';
import { DeleteAction } from "./DeleteAction"; import { EditAction } from './EditAction';
import { TrackCardBody } from "./TrackCardBody"; import { DeleteAction } from './DeleteAction';
import "./ManagedTrack.css"; import { TrackCardBody } from './TrackCardBody';
import './ManagedTrack.css';
const ManagedTrack = ({ initialTrack, onDeleteTrack }) => { const ManagedTrack = ({ initialTrack, onDeleteTrack }) => {
const trackElement = useRef(); const trackElement = useRef();
@@ -39,4 +40,9 @@ const ManagedTrack = ({ initialTrack, onDeleteTrack }) => {
); );
}; };
ManagedTrack.propTypes = {
initialTrack: PropTypes.object.isRequired,
onDeleteTrack: PropTypes.func.isRequired,
};
export { ManagedTrack }; export { ManagedTrack };

View File

@@ -53,8 +53,11 @@ const Track = ({ initialTrack, isAlreadyOrdered }) => {
}; };
Track.propTypes = { Track.propTypes = {
initialTrack: PropTypes.object, initialTrack: PropTypes.object.isRequired,
isAlreadyOrdered: PropTypes.bool, isAlreadyOrdered: PropTypes.bool,
}; };
Track.defaultProps = {
isAlreadyOrdered: undefined,
};
export { Track }; export { Track };

View File

@@ -41,4 +41,4 @@
} }
} }
} }
} }

View File

@@ -11,8 +11,7 @@ service BrowseInvoices @(requires : 'customer') {
entity Invoices as projection on my.Invoices; entity Invoices as projection on my.Invoices;
action invoice(tracks : array of { action invoice(tracks : array of {
ID : Integer; ID : Integer;
unitPrice : Decimal(10, 2);
}); });
action cancelInvoice(ID : Integer); action cancelInvoice(ID : Integer);

View File

@@ -6,6 +6,10 @@ const CANCEL_STATUS = -1;
const SHIPPED_STATUS = 1; const SHIPPED_STATUS = 1;
const UTC_DATE_TIME_FORMAT = "YYYY-MM-DDThh:mm:ss"; const UTC_DATE_TIME_FORMAT = "YYYY-MM-DDThh:mm:ss";
function roundNumber(num) {
return Math.round((num + Number.EPSILON) * 100) / 100;
}
// the same function there is in the frontend // the same function there is in the frontend
const isLeverageTimeExpired = (utcNowTimestamp, invoiceDate) => { const isLeverageTimeExpired = (utcNowTimestamp, invoiceDate) => {
const duration = moment.duration( const duration = moment.duration(
@@ -16,7 +20,7 @@ const isLeverageTimeExpired = (utcNowTimestamp, invoiceDate) => {
module.exports = async function () { module.exports = async function () {
const db = await cds.connect.to("db"); // connect to database service const db = await cds.connect.to("db"); // connect to database service
const { Invoices, InvoiceItems } = db.entities; const { Invoices, InvoiceItems, Tracks } = db.entities;
this.on("READ", "Invoices", async (req) => { this.on("READ", "Invoices", async (req) => {
return await db.run(req.query.where({ customer_ID: req.user.attr.ID })); return await db.run(req.query.where({ customer_ID: req.user.attr.ID }));
@@ -24,13 +28,9 @@ module.exports = async function () {
this.on("invoice", async (req) => { this.on("invoice", async (req) => {
const { tracks } = req.data; const { tracks } = req.data;
const newInvoicedTracks = tracks.map(({ ID }) => ID); const newInvoicedTrackIds = tracks.map(({ ID }) => ID);
const customerId = req.user.attr.ID; const customerId = req.user.attr.ID;
const utcNowDateTime = moment().utc().format(UTC_DATE_TIME_FORMAT); const utcNowDateTime = moment().utc().format(UTC_DATE_TIME_FORMAT);
const total = tracks.reduce(
(acc, { unitPrice }) => acc + Number(unitPrice),
0
);
const transaction = await db.tx(req); const transaction = await db.tx(req);
// check if already exists // check if already exists
@@ -45,13 +45,21 @@ module.exports = async function () {
}) })
) )
); );
const isNewInvoiceHasInvoicedTracks = invoicedTracks.some( const isInValidInvoice = invoicedTracks.some(({ track_ID: curID }) => {
({ track_ID: curID }) => newInvoicedTracks.includes(curID) return newInvoicedTrackIds.includes(curID);
); });
if (isNewInvoiceHasInvoicedTracks) { if (isInValidInvoice) {
req.reject(400, "Invoice contains already owned values"); req.reject(400, "Invoice contains already owned values");
} }
const newInvoicedTracks = await transaction.run(
SELECT("ID", "unitPrice").from(Tracks).where({ ID: newInvoicedTrackIds })
);
const total = newInvoicedTracks.reduce(
(acc, { unitPrice }) => acc + roundNumber(Number(unitPrice)),
0
);
// getting last ids for new records // getting last ids for new records
let { ID: lastInvoiceId } = await transaction.run( let { ID: lastInvoiceId } = await transaction.run(
SELECT.one(Invoices).columns("ID").orderBy({ ID: "desc" }) SELECT.one(Invoices).columns("ID").orderBy({ ID: "desc" })

View File

@@ -1,74 +1,92 @@
@media-store-service = http://localhost:4004/media
@browse-tracks-service = http://localhost:4004/browse-tracks @browse-tracks-service = http://localhost:4004/browse-tracks
@browse-invoices-service = http://localhost:4004/browse-invoices
@manage-store-service = http://localhost:4004/manage-store
@user-service = http://localhost:4004/users
### ------------------------------------------------------------------------ ### ------------------------------------------------------------------------
# Get service ## Browse Tracks service
GET {{media-store-service}} ### ------------------------------------------------------------------------
### ------------------------------------------------------------------------ ### ------------------------------------------------------------------------
# Get $metadata document # Get browse-tracks-service
GET {{media-store-service}}/$metadata GET {{browse-tracks-service}}
### ------------------------------------------------------------------------ ### ------------------------------------------------------------------------
# Get Artists # Get $metadata document of browse-tracks-service
GET {{media-store-service}}/Artists GET {{browse-tracks-service}}/$metadata
### ------------------------------------------------------------------------ ### ------------------------------------------------------------------------
# Get Albums # Get Trakcs
GET {{media-store-service}}/Albums GET {{browse-tracks-service}}/Tracks
### ------------------------------------------------------------------------
# Get Albums by artist ID axpanding tracks and artist
GET {{browse-tracks-service}}/Albums
?$filter=artist_ID eq 1 ?$filter=artist_ID eq 1
&$expand=tracks,artist &$expand=tracks,artist
### ------------------------------------------------------------------------ ### ------------------------------------------------------------------------
# Get Customers ## Users service
GET {{media-store-service}}/Customers(1) ### ------------------------------------------------------------------------
?$expand=supportRep
### ------------------------------------------------------------------------ ### ------------------------------------------------------------------------
# Get Employees # Login user (customer/employee)
GET {{media-store-service}}/Employees(1) POST {{user-service}}/login
?$expand=subordinates,reportsTo
### ------------------------------------------------------------------------
# Get Genres
GET {{media-store-service}}/Genres
?$top=10
### ------------------------------------------------------------------------
# Get InvoiceItems
GET {{media-store-service}}/InvoiceItems
?$expand=track,invoice&$top=10
### ------------------------------------------------------------------------
# Get Invoices
GET {{media-store-service}}/Invoices
?$top=10&$expand=customer
### ------------------------------------------------------------------------
# Get MediaTypes
GET {{media-store-service}}/MediaTypes
?$top=10
### ------------------------------------------------------------------------
# Get PlaylistTrack
GET {{media-store-service}}/PlaylistTrack/$count
?$filter=playlist_ID eq 8
# ?$count=true
# &$expand=track&$select=track
### ------------------------------------------------------------------------
# Get Playlists
GET {{media-store-service}}/Playlists
?$top=10
### ------------------------------------------------------------------------
# Get Playlists
GET {{media-store-service}}/Tracks
# ?$top=10&$expand=genre,mediaType,album
### ------------------------------------------------------------------------
# Get My tracks
POST {{browse-tracks-service}}/getInvoicedTracks
Content-Type: application/json Content-Type: application/json
{
"email": "leonekohler@surfeu.de",
"password": "some"
}
# employee data
# {
# "email": "andrew@chinookcorp.com",
# "password": "some"
# }
### ------------------------------------------------------------------------
# Refresh tokens
POST {{user-service}}/refreshTokens
Content-Type: application/json
{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imxlb25la29obGVyQHN1cmZldS5kZSIsIklEIjoyLCJyb2xlcyI6WyJjdXN0b21lciJdLCJpYXQiOjE2MDc0MzE2MzYsImV4cCI6MTYwNzQzMjgzNn0.5MPlOr05Qr1fYbE0dutnUu3n8JMOiuLLUnsnM0RSeA8"
}
### ------------------------------------------------------------------------
# Get current customer data
GET {{user-service}}/Customers(2)
Authorization: Basic eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imxlb25la29obGVyQHN1cmZldS5kZSIsIklEIjoyLCJyb2xlcyI6WyJjdXN0b21lciJdLCJpYXQiOjE2MDc0MzE5NTgsImV4cCI6MTYwNzQzMjU1OH0.2sP3epaEo8tpBJZ-PuJInuC36TOyMgL2W6QjNFGyhSs
### ------------------------------------------------------------------------
# Get current employee data
GET {{user-service}}/Employees(1)
Authorization: Basic eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFuZHJld0BjaGlub29rY29ycC5jb20iLCJJRCI6MSwicm9sZXMiOlsiZW1wbG95ZWUiXSwiaWF0IjoxNjA3NDMyMTY0LCJleHAiOjE2MDc0MzI3NjR9.HVwadUbUq3K0_5NIo9pYX9rK9awmzZ3hIqauF3yusdI
### ------------------------------------------------------------------------
## Invocies service
### ------------------------------------------------------------------------
### ------------------------------------------------------------------------
# Get all current customer invoices
GET {{browse-invoices-service}}/Invoices
Authorization: Basic eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imxlb25la29obGVyQHN1cmZldS5kZSIsIklEIjoyLCJyb2xlcyI6WyJjdXN0b21lciJdLCJpYXQiOjE2MDc0MzE5NTgsImV4cCI6MTYwNzQzMjU1OH0.2sP3epaEo8tpBJZ-PuJInuC36TOyMgL2W6QjNFGyhSs
### ------------------------------------------------------------------------
## Manage store service
### ------------------------------------------------------------------------
### ------------------------------------------------------------------------
# Crete new Album
POST {{manage-store-service}}/Artists
Content-Type: application/json
Authorization: Basic eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFuZHJld0BjaGlub29rY29ycC5jb20iLCJJRCI6MSwicm9sZXMiOlsiZW1wbG95ZWUiXSwiaWF0IjoxNjA3NDQxMzQwLCJleHAiOjE2MDc0NDE5NDB9._JQzhqUwbutccoSWWeCZ2R16gLzzMD7b21bZ5wxN1gU
{
"name": "some"
}