diff --git a/media-store/app-src/package.json b/media-store/app-src/package.json
index a3bd4554..457556aa 100644
--- a/media-store/app-src/package.json
+++ b/media-store/app-src/package.json
@@ -15,6 +15,7 @@
"@testing-library/user-event": "^7.1.2",
"@umijs/hooks": "^1.9.3",
"antd": "^4.8.2",
+ "@ant-design/icons": "4.3.0",
"axios": "^0.20.0",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^6.3.2",
diff --git a/media-store/app-src/src/api/calls.js b/media-store/app-src/src/api/calls.js
index f22c18bb..10b6abb5 100644
--- a/media-store/app-src/src/api/calls.js
+++ b/media-store/app-src/src/api/calls.js
@@ -43,13 +43,10 @@ const invoice = (tracks) => {
return axiosInstance.post(
`${INVOICES_SERVICE}/invoice`,
{
- tracks: tracks.map(({ unitPrice, ID }) => ({
- unitPrice: `${unitPrice}`,
- ID,
- })),
+ tracks,
},
{
- headers: { 'content-type': 'application/json;IEEE754Compatible=true' },
+ headers: { 'content-type': 'application/json' },
}
);
};
diff --git a/media-store/app-src/src/components/Header.jsx b/media-store/app-src/src/components/Header.jsx
index 7dd719d9..a965ff8c 100644
--- a/media-store/app-src/src/components/Header.jsx
+++ b/media-store/app-src/src/components/Header.jsx
@@ -36,8 +36,8 @@ const Header = () => {
history.go(RELOAD_LOCATION_NUMBER);
};
const localeElements = AVAILABLE_LOCALES.filter((localeName) => localeName !== locale).map(
- (curLocale, index) => (
-
onChangeLocale(curLocale)}>
+ (curLocale) => (
+ onChangeLocale(curLocale)}>
{curLocale}
)
@@ -83,9 +83,6 @@ const Header = () => {
Manages
)}
-
- {loading && } />}
-
diff --git a/media-store/app-src/src/components/InvoicePage.jsx b/media-store/app-src/src/components/InvoicePage.jsx
index 645a24a0..505ccd9a 100644
--- a/media-store/app-src/src/components/InvoicePage.jsx
+++ b/media-store/app-src/src/components/InvoicePage.jsx
@@ -38,9 +38,8 @@ const InvoicePage = () => {
const onBuy = () => {
setLoading(true);
invoice(
- invoicedItems.map(({ ID, unitPrice }) => ({
+ invoicedItems.map(({ ID }) => ({
ID,
- unitPrice,
}))
)
.then(() => {
diff --git a/media-store/app-src/src/components/TracksPage.jsx b/media-store/app-src/src/components/TracksPage.jsx
index a59cf6ea..ed6c9835 100644
--- a/media-store/app-src/src/components/TracksPage.jsx
+++ b/media-store/app-src/src/components/TracksPage.jsx
@@ -153,7 +153,7 @@ const TracksContainer = () => {
const isAlreadyOrdered = !isEmployee && track.alreadyOrdered;
const onDeleteTrack = isEmployee && ((ID) => deleteTrack(ID));
return (
-
+
response.data.value)
.catch(this.handleError);
-};
+}
const TrackForm = ({ initialAlbumTitle }) => {
const { handleError } = useErrors();
@@ -89,5 +89,8 @@ const TrackForm = ({ initialAlbumTitle }) => {
TrackForm.propTypes = {
initialAlbumTitle: PropTypes.string,
};
+TrackForm.defaultProps = {
+ initialAlbumTitle: undefined,
+};
export { TrackForm };
diff --git a/media-store/app-src/src/components/tracks/EditAction.jsx b/media-store/app-src/src/components/tracks/EditAction.jsx
index 6b156076..1c8c42e6 100644
--- a/media-store/app-src/src/components/tracks/EditAction.jsx
+++ b/media-store/app-src/src/components/tracks/EditAction.jsx
@@ -18,6 +18,16 @@ const EditAction = ({ ID, name, composer, genre, unitPrice, album, afterTrackUpd
setVisible(true);
};
+ const afterCloseModal = () => {
+ setUpdateLoading(true);
+ getTrack(ID)
+ .then((response) => {
+ afterTrackUpdate(response.data);
+ setUpdateLoading(false);
+ })
+ .catch(handleError);
+ };
+
const onFinish = (value) => {
setConfirmLoading(true);
updateTrack({
@@ -45,16 +55,6 @@ const EditAction = ({ ID, name, composer, genre, unitPrice, album, afterTrackUpd
setVisible(false);
};
- const afterCloseModal = () => {
- setUpdateLoading(true);
- getTrack(ID)
- .then((response) => {
- afterTrackUpdate(response.data);
- setUpdateLoading(false);
- })
- .catch(handleError);
- };
-
return (
<>
{updateLoading ? : }
@@ -86,11 +86,11 @@ const EditAction = ({ ID, name, composer, genre, unitPrice, album, afterTrackUpd
onFinish={onFinish}
onFinishFailed={() => console.log('Not valid params provided')}
initialValues={{
- name: name,
- composer: composer,
+ name,
+ composer,
genreID: genre.ID,
albumID: album.ID,
- unitPrice: unitPrice,
+ unitPrice,
}}
>
diff --git a/media-store/app-src/src/components/tracks/ManagedTrack.jsx b/media-store/app-src/src/components/tracks/ManagedTrack.jsx
index 8beaa37c..42bea8c8 100644
--- a/media-store/app-src/src/components/tracks/ManagedTrack.jsx
+++ b/media-store/app-src/src/components/tracks/ManagedTrack.jsx
@@ -1,9 +1,10 @@
-import React, { useState, useRef } from "react";
-import { Card } from "antd";
-import { EditAction } from "./EditAction";
-import { DeleteAction } from "./DeleteAction";
-import { TrackCardBody } from "./TrackCardBody";
-import "./ManagedTrack.css";
+import React, { useState, useRef } from 'react';
+import { Card } from 'antd';
+import PropTypes from 'prop-types';
+import { EditAction } from './EditAction';
+import { DeleteAction } from './DeleteAction';
+import { TrackCardBody } from './TrackCardBody';
+import './ManagedTrack.css';
const ManagedTrack = ({ initialTrack, onDeleteTrack }) => {
const trackElement = useRef();
@@ -39,4 +40,9 @@ const ManagedTrack = ({ initialTrack, onDeleteTrack }) => {
);
};
+ManagedTrack.propTypes = {
+ initialTrack: PropTypes.object.isRequired,
+ onDeleteTrack: PropTypes.func.isRequired,
+};
+
export { ManagedTrack };
diff --git a/media-store/app-src/src/components/tracks/Track.jsx b/media-store/app-src/src/components/tracks/Track.jsx
index 0ab57308..3fb90950 100644
--- a/media-store/app-src/src/components/tracks/Track.jsx
+++ b/media-store/app-src/src/components/tracks/Track.jsx
@@ -53,8 +53,11 @@ const Track = ({ initialTrack, isAlreadyOrdered }) => {
};
Track.propTypes = {
- initialTrack: PropTypes.object,
+ initialTrack: PropTypes.object.isRequired,
isAlreadyOrdered: PropTypes.bool,
};
+Track.defaultProps = {
+ isAlreadyOrdered: undefined,
+};
export { Track };
diff --git a/media-store/package.json b/media-store/package.json
index 334f1a55..8015f55f 100644
--- a/media-store/package.json
+++ b/media-store/package.json
@@ -41,4 +41,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/media-store/srv/browse-invoices-service.cds b/media-store/srv/browse-invoices-service.cds
index 8305d806..42e45545 100644
--- a/media-store/srv/browse-invoices-service.cds
+++ b/media-store/srv/browse-invoices-service.cds
@@ -11,8 +11,7 @@ service BrowseInvoices @(requires : 'customer') {
entity Invoices as projection on my.Invoices;
action invoice(tracks : array of {
- ID : Integer;
- unitPrice : Decimal(10, 2);
+ ID : Integer;
});
action cancelInvoice(ID : Integer);
diff --git a/media-store/srv/browse-invoices-service.js b/media-store/srv/browse-invoices-service.js
index a7e490cf..c49eb057 100644
--- a/media-store/srv/browse-invoices-service.js
+++ b/media-store/srv/browse-invoices-service.js
@@ -6,6 +6,10 @@ const CANCEL_STATUS = -1;
const SHIPPED_STATUS = 1;
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
const isLeverageTimeExpired = (utcNowTimestamp, invoiceDate) => {
const duration = moment.duration(
@@ -16,7 +20,7 @@ const isLeverageTimeExpired = (utcNowTimestamp, invoiceDate) => {
module.exports = async function () {
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) => {
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) => {
const { tracks } = req.data;
- const newInvoicedTracks = tracks.map(({ ID }) => ID);
+ const newInvoicedTrackIds = tracks.map(({ ID }) => ID);
const customerId = req.user.attr.ID;
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);
// check if already exists
@@ -45,13 +45,21 @@ module.exports = async function () {
})
)
);
- const isNewInvoiceHasInvoicedTracks = invoicedTracks.some(
- ({ track_ID: curID }) => newInvoicedTracks.includes(curID)
- );
- if (isNewInvoiceHasInvoicedTracks) {
+ const isInValidInvoice = invoicedTracks.some(({ track_ID: curID }) => {
+ return newInvoicedTrackIds.includes(curID);
+ });
+ if (isInValidInvoice) {
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
let { ID: lastInvoiceId } = await transaction.run(
SELECT.one(Invoices).columns("ID").orderBy({ ID: "desc" })
diff --git a/media-store/test/test.http b/media-store/test/test.http
index 677b493b..aab092f4 100644
--- a/media-store/test/test.http
+++ b/media-store/test/test.http
@@ -1,74 +1,92 @@
-@media-store-service = http://localhost:4004/media
@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
-GET {{media-store-service}}
+## Browse Tracks service
+### ------------------------------------------------------------------------
### ------------------------------------------------------------------------
-# Get $metadata document
-GET {{media-store-service}}/$metadata
+# Get browse-tracks-service
+GET {{browse-tracks-service}}
### ------------------------------------------------------------------------
-# Get Artists
-GET {{media-store-service}}/Artists
+# Get $metadata document of browse-tracks-service
+GET {{browse-tracks-service}}/$metadata
### ------------------------------------------------------------------------
-# Get Albums
-GET {{media-store-service}}/Albums
+# Get Trakcs
+GET {{browse-tracks-service}}/Tracks
+
+### ------------------------------------------------------------------------
+# Get Albums by artist ID axpanding tracks and artist
+GET {{browse-tracks-service}}/Albums
?$filter=artist_ID eq 1
&$expand=tracks,artist
### ------------------------------------------------------------------------
-# Get Customers
-GET {{media-store-service}}/Customers(1)
-?$expand=supportRep
+## Users service
+### ------------------------------------------------------------------------
### ------------------------------------------------------------------------
-# Get Employees
-GET {{media-store-service}}/Employees(1)
-?$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
+# Login user (customer/employee)
+POST {{user-service}}/login
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"
+}
\ No newline at end of file