Füge neue Modelle für Produktion, Inventar und kaufbare Bestände hinzu; aktualisiere bestehende Modelle und Routen
This commit is contained in:
@@ -7,6 +7,8 @@ class FalukantController {
|
||||
this.randomFirstName = this.randomFirstName.bind(this);
|
||||
this.randomLastName = this.randomLastName.bind(this);
|
||||
this.getInfo = this.getInfo.bind(this);
|
||||
this.getInventory = this.getInventory.bind(this);
|
||||
this.sellProduct = this.sellProduct.bind(this);
|
||||
}
|
||||
|
||||
async getUser(req, res) {
|
||||
@@ -69,6 +71,94 @@ class FalukantController {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async getBranch(req, res) {
|
||||
try {
|
||||
const { userid: hashedUserId } = req.headers;
|
||||
const { branch: branchId } = req.params;
|
||||
console.log(branchId, req.params);
|
||||
const result = await FalukantService.getBranch(hashedUserId, branchId);
|
||||
res.status(200).json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async createProduction(req, res) {
|
||||
try {
|
||||
const { userid: hashedUserId } = req.headers;
|
||||
const { branchId, productId, quantity } = req.body;
|
||||
const result = await FalukantService.createProduction(hashedUserId, branchId, productId, quantity);
|
||||
res.status(201).json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async getProduction(req, res) {
|
||||
try {
|
||||
const { userid: hashedUserId } = req.headers;
|
||||
const { branchId } = req.params;
|
||||
const result = await FalukantService.getProduction(hashedUserId, branchId);
|
||||
res.status(200).json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async getStock(req, res) {
|
||||
try {
|
||||
const { userid: hashedUserId } = req.headers;
|
||||
const { branchId } = req.params || null;
|
||||
const result = await FalukantService.getStock(hashedUserId, branchId);
|
||||
res.status(200).json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async createStock(req, res) {
|
||||
try {
|
||||
const { userid: hashedUserId } = req.headers;
|
||||
const { branchId, stockTypeId, stockSize } = req.body;
|
||||
const result = await FalukantService.createStock(hashedUserId, branchId, stockTypeId, stockSize);
|
||||
res.status(201).json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async getProducts(req, res) {
|
||||
try {
|
||||
const { userid: hashedUserId } = req.headers;
|
||||
const result = await FalukantService.getProducts(hashedUserId);
|
||||
res.status(200).json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async getInventory(req, res) {
|
||||
try {
|
||||
const { userid: hashedUserId } = req.headers;
|
||||
const { branchId } = req.params;
|
||||
const result = await FalukantService.getInventory(hashedUserId, branchId);
|
||||
res.status(200).json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async sellProduct(req, res) {
|
||||
try {
|
||||
const { userid: hashedUserId } = req.headers;
|
||||
const { branchId, productId, quality, quantity } = req.body;
|
||||
const result = await FalukantService.sellProduct(hashedUserId, branchId, productId, quality, quantity);
|
||||
res.status(201).json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FalukantController;
|
||||
|
||||
@@ -42,6 +42,9 @@ import TitleOfNobility from './falukant/type/title_of_nobility.js';
|
||||
import TitleRequirement from './falukant/type/title_requirement.js';
|
||||
import Branch from './falukant/data/branch.js';
|
||||
import BranchType from './falukant/type/branch.js';
|
||||
import Production from './falukant/data/production.js';
|
||||
import Inventory from './falukant/data/inventory.js';
|
||||
import BuyableStock from './falukant/data/buayble_stock.js';
|
||||
|
||||
|
||||
export default function setupAssociations() {
|
||||
@@ -212,14 +215,8 @@ export default function setupAssociations() {
|
||||
FalukantStock.belongsTo(FalukantStockType, { foreignKey: 'stockTypeId', as: 'stockType' });
|
||||
FalukantStockType.hasMany(FalukantStock, { foreignKey: 'stockTypeId', as: 'stocks' });
|
||||
|
||||
FalukantStock.belongsTo(FalukantUser, { foreignKey: 'userId', as: 'user' });
|
||||
FalukantUser.hasMany(FalukantStock, { foreignKey: 'userId', as: 'stocks' });
|
||||
|
||||
FalukantStock.belongsTo(RegionData, { foreignKey: 'regionId', as: 'region' });
|
||||
RegionData.hasMany(FalukantStock, { foreignKey: 'regionId', as: 'stocksInRegion' });
|
||||
|
||||
Knowledge.belongsTo(ProductType, { foreignKey: 'productTypeId', as: 'productType' });
|
||||
ProductType.hasMany(Knowledge, { foreignKey: 'productTypeId', as: 'knowledges' });
|
||||
Knowledge.belongsTo(ProductType, { foreignKey: 'productId', as: 'productType' });
|
||||
ProductType.hasMany(Knowledge, { foreignKey: 'productId', as: 'knowledges' });
|
||||
|
||||
Knowledge.belongsTo(FalukantCharacter, { foreignKey: 'characterId', as: 'character' });
|
||||
FalukantCharacter.hasMany(Knowledge, { foreignKey: 'characterId', as: 'knowledges' });
|
||||
@@ -235,4 +232,23 @@ export default function setupAssociations() {
|
||||
|
||||
Branch.belongsTo(BranchType, { foreignKey: 'branchTypeId', as: 'branchType' });
|
||||
BranchType.hasMany(Branch, { foreignKey: 'branchTypeId', as: 'branches' });
|
||||
|
||||
Production.belongsTo(Branch, { foreignKey: 'branchId', as: 'branch' });
|
||||
Branch.hasMany(Production, { foreignKey: 'branchId', as: 'productions' });
|
||||
|
||||
Production.belongsTo(ProductType, { foreignKey: 'productId', as: 'productType' });
|
||||
ProductType.hasMany(Production, { foreignKey: 'productId', as: 'productions' });
|
||||
|
||||
Inventory.belongsTo(FalukantStock, { foreignKey: 'stockId', as: 'stock' });
|
||||
FalukantStock.hasMany(Inventory, { foreignKey: 'stockId', as: 'inventories' });
|
||||
|
||||
Inventory.belongsTo(ProductType, { foreignKey: 'productId', as: 'productType' });
|
||||
ProductType.hasMany(Inventory, { foreignKey: 'productId', as: 'inventories' });
|
||||
|
||||
BuyableStock.belongsTo(RegionData, { foreignKey: 'regionId', as: 'region' });
|
||||
RegionData.hasMany(BuyableStock, { foreignKey: 'regionId', as: 'buyableStocks' });
|
||||
|
||||
Branch.hasMany(FalukantStock, { foreignKey: 'branchId', as: 'stocks' });
|
||||
FalukantStock.belongsTo(Branch, { foreignKey: 'branchId', as: 'branch' });
|
||||
|
||||
}
|
||||
|
||||
28
backend/models/falukant/data/buayble_stock.js
Normal file
28
backend/models/falukant/data/buayble_stock.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Model, DataTypes } from 'sequelize';
|
||||
import { sequelize } from '../../../utils/sequelize.js';
|
||||
|
||||
class BuyableStock extends Model { }
|
||||
|
||||
BuyableStock.init({
|
||||
regionId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
stockTypeId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
quantity: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'BuyableStock',
|
||||
tableName: 'buyable_stock',
|
||||
schema: 'falukant_data',
|
||||
timestamps: false,
|
||||
underscored: true,
|
||||
});
|
||||
|
||||
export default BuyableStock;
|
||||
37
backend/models/falukant/data/inventory.js
Normal file
37
backend/models/falukant/data/inventory.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Model, DataTypes } from 'sequelize';
|
||||
import { sequelize } from '../../../utils/sequelize.js';
|
||||
|
||||
class Inventory extends Model { }
|
||||
|
||||
Inventory.init({
|
||||
stockId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
productId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
quantity: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
quality: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
producedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Inventory',
|
||||
tableName: 'inventory',
|
||||
schema: 'falukant_data',
|
||||
timestamps: false,
|
||||
underscored: true,
|
||||
});
|
||||
|
||||
export default Inventory;
|
||||
33
backend/models/falukant/data/production.js
Normal file
33
backend/models/falukant/data/production.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Model, DataTypes } from 'sequelize';
|
||||
import { sequelize } from '../../../utils/sequelize.js';
|
||||
|
||||
class Production extends Model { }
|
||||
|
||||
Production.init({
|
||||
branchId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
productId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
quantity: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
startTimestamp: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Production',
|
||||
tableName: 'production',
|
||||
schema: 'falukant_data',
|
||||
timestamps: false,
|
||||
underscored: true,
|
||||
});
|
||||
|
||||
export default Production;
|
||||
@@ -4,13 +4,10 @@ import { sequelize } from '../../../utils/sequelize.js';
|
||||
class FalukantStock extends Model { }
|
||||
|
||||
FalukantStock.init({
|
||||
userId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
regionId: {
|
||||
branchId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0
|
||||
},
|
||||
stockTypeId: {
|
||||
type: DataTypes.INTEGER,
|
||||
|
||||
@@ -37,6 +37,11 @@ FalukantUser.init({
|
||||
allowNull: false,
|
||||
defaultValue: 0.00,
|
||||
},
|
||||
certificate: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 1,
|
||||
},
|
||||
mainBranchRegionId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
|
||||
@@ -46,6 +46,9 @@ import TitleRequirement from './falukant/type/title_requirement.js';
|
||||
import TitleOfNobility from './falukant/type/title_of_nobility.js';
|
||||
import BranchType from './falukant/type/branch.js';
|
||||
import Branch from './falukant/data/branch.js';
|
||||
import Production from './falukant/data/production.js';
|
||||
import Inventory from './falukant/data/inventory.js';
|
||||
import BuyableStock from './falukant/data/buayble_stock.js';
|
||||
|
||||
const models = {
|
||||
SettingsType,
|
||||
@@ -96,6 +99,9 @@ const models = {
|
||||
TitleRequirement,
|
||||
BranchType,
|
||||
Branch,
|
||||
Production,
|
||||
Inventory,
|
||||
BuyableStock,
|
||||
};
|
||||
|
||||
export default models;
|
||||
|
||||
@@ -9,5 +9,13 @@ router.post('/user', falukantController.createUser);
|
||||
router.get('/name/randomfirstname/:gender', falukantController.randomFirstName);
|
||||
router.get('/name/randomlastname', falukantController.randomLastName);
|
||||
router.get('/info', falukantController.getInfo);
|
||||
router.get('/branches/:branch', falukantController.getBranch);
|
||||
router.get('/branches', falukantController.getBranches);
|
||||
router.post('/production', falukantController.createProduction);
|
||||
router.get('/production/:branchId', falukantController.getProduction);
|
||||
router.get('/stock/?:branchId', falukantController.getStock);
|
||||
router.post('/stock', falukantController.createStock);
|
||||
router.get('/products', falukantController.getProducts);
|
||||
router.get('/inventory/?:branchId', falukantController.getInventory);
|
||||
router.post('/sell', falukantController.sellProduct);
|
||||
export default router;
|
||||
|
||||
@@ -14,6 +14,12 @@ import { differenceInDays } from 'date-fns';
|
||||
import TitleOfNobility from '../models/falukant/type/title_of_nobility.js';
|
||||
import Branch from '../models/falukant/data/branch.js';
|
||||
import BranchType from '../models/falukant/type/branch.js';
|
||||
import Production from '../models/falukant/data/production.js';
|
||||
import ProductType from '../models/falukant/type/product.js';
|
||||
import { Op, fn, col } from 'sequelize';
|
||||
import Knowledge from '../models/falukant/data/product_knowledge.js';
|
||||
import Inventory from '../models/falukant/data/inventory.js';
|
||||
import Stock from '../models/falukant/data/stock.js';
|
||||
|
||||
class FalukantService extends BaseService {
|
||||
async getFalukantUserByHashedId(hashedId) {
|
||||
@@ -26,7 +32,7 @@ class FalukantService extends BaseService {
|
||||
hashedId: hashedId
|
||||
},
|
||||
}
|
||||
],
|
||||
],
|
||||
});
|
||||
return falukantUser;
|
||||
}
|
||||
@@ -279,6 +285,292 @@ class FalukantService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
async getBranch(hashedUserId, branchId) {
|
||||
try {
|
||||
const falukantUser = await this.getFalukantUserByHashedId(hashedUserId);
|
||||
if (!falukantUser) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
const branch = await Branch.findOne({
|
||||
where: { id: branchId, falukantUserId: falukantUser.id },
|
||||
include: [
|
||||
{
|
||||
model: BranchType,
|
||||
as: 'branchType',
|
||||
attributes: ['labelTr'],
|
||||
},
|
||||
{
|
||||
model: RegionData,
|
||||
as: 'region',
|
||||
attributes: ['name'],
|
||||
},
|
||||
{
|
||||
model: Production,
|
||||
as: 'productions',
|
||||
attributes: ['quantity', 'startTimestamp'],
|
||||
include: [
|
||||
{
|
||||
model: ProductType,
|
||||
as: 'productType',
|
||||
attributes: ['id', 'category', 'labelTr', 'sellCost', 'productionTime'],
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
attributes: ['id', 'regionId'],
|
||||
});
|
||||
if (!branch) {
|
||||
throw new Error('Branch not found');
|
||||
}
|
||||
return branch;
|
||||
} catch (error) {
|
||||
console.error('Error in getBranch:', error);
|
||||
throw new Error('Failed to retrieve branch');
|
||||
}
|
||||
}
|
||||
|
||||
async getStock(hashedUserId, branchId) {
|
||||
try {
|
||||
const falukantUser = await this.getFalukantUserByHashedId(hashedUserId);
|
||||
if (!falukantUser) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id } });
|
||||
if (!branch) {
|
||||
throw new Error('Branch not found');
|
||||
}
|
||||
const stock = await FalukantStock.findOne({ where: { regionId: branch.regionId, userId: falukantUser.id } });
|
||||
return stock;
|
||||
} catch (error) {
|
||||
console.error('Error in getStock:', error);
|
||||
throw new Error('Failed to retrieve stock');
|
||||
}
|
||||
}
|
||||
|
||||
async createStock(hashedUserId, branchId, stockData) {
|
||||
try {
|
||||
const falukantUser = await this.getFalukantUserByHashedId(hashedUserId);
|
||||
if (!falukantUser) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id } });
|
||||
if (!branch) {
|
||||
throw new Error('Branch not found');
|
||||
}
|
||||
const stock = await FalukantStock.create({
|
||||
userId: falukantUser.id,
|
||||
regionId: branch.regionId,
|
||||
stockTypeId: stockData.stockTypeId,
|
||||
quantity: stockData.quantity,
|
||||
});
|
||||
return stock;
|
||||
} catch (error) {
|
||||
console.error('Error in createStock:', error);
|
||||
throw new Error('Failed to create stock');
|
||||
}
|
||||
}
|
||||
|
||||
async createProduction(hashedUserId, branchId, productId, quantity) {
|
||||
const falukantUser = await this.getFalukantUserByHashedId(hashedUserId);
|
||||
if (!falukantUser) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id } });
|
||||
if (!branch) {
|
||||
throw new Error('Branch not found');
|
||||
}
|
||||
const product = await ProductType.findOne({ where: { id: productId } });
|
||||
if (falukantUser.money < quantity * product.category * 7) {
|
||||
throw new Error('notenoughmoney');
|
||||
}
|
||||
const production = await Production.create({
|
||||
branchId: branch.id,
|
||||
productId: productId,
|
||||
quantity: quantity,
|
||||
});
|
||||
falukantUser.update({ money: falukantUser.money - quantity * product.category * 7 });
|
||||
notifyUser(falukantUser.user.hashedId, 'falukantUpdateStatus', {});
|
||||
notifyUser(falukantUser.user.hashedId, 'falukantBranchUpdate', { branchId: branch.id });
|
||||
return production;
|
||||
}
|
||||
|
||||
async getProduction(hashedUserId, branchId) {
|
||||
try {
|
||||
const falukantUser = await this.getFalukantUserByHashedId(hashedUserId);
|
||||
if (!falukantUser) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id } });
|
||||
if (!branch) {
|
||||
throw new Error('Branch not found');
|
||||
}
|
||||
const production = await FalukantProduction.findOne({ where: { regionId: branch.regionId } });
|
||||
return production;
|
||||
} catch (error) {
|
||||
console.error('Error in getProduction:', error);
|
||||
throw new Error('Failed to retrieve production');
|
||||
}
|
||||
}
|
||||
|
||||
async getProducts(hashedUserId) {
|
||||
try {
|
||||
const falukantUser = await this.getFalukantUserByHashedId(hashedUserId);
|
||||
if (!falukantUser) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
const products = await ProductType.findAll({
|
||||
where: {
|
||||
category: {
|
||||
[Op.lte]: falukantUser.certificate
|
||||
}
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: Knowledge,
|
||||
as: 'knowledges',
|
||||
attributes: ['knowledge'],
|
||||
}
|
||||
],
|
||||
attributes: ['labelTr', 'id', 'sellCost', 'productionTime', 'category'],
|
||||
});
|
||||
return products;
|
||||
} catch (error) {
|
||||
console.error('Error in getProducts:', error);
|
||||
throw new Error('Failed to retrieve products');
|
||||
}
|
||||
}
|
||||
|
||||
async getInventory(hashedUserId, branchId) {
|
||||
try {
|
||||
const falukantUser = await this.getFalukantUserByHashedId(hashedUserId);
|
||||
if (!falukantUser) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
const branchFilter = branchId
|
||||
? { id: branchId, falukantUserId: falukantUser.id }
|
||||
: { falukantUserId: falukantUser.id };
|
||||
const branches = await Branch.findAll({
|
||||
where: branchFilter,
|
||||
include: [
|
||||
{
|
||||
model: FalukantStock,
|
||||
as: 'stocks',
|
||||
include: [
|
||||
{
|
||||
model: FalukantStockType,
|
||||
as: 'stockType',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
model: RegionData,
|
||||
as: 'region',
|
||||
include: [
|
||||
{
|
||||
model: RegionType,
|
||||
as: 'regionType',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
const stockIds = branches.flatMap(branch => branch.stocks.map(stock => stock.id));
|
||||
const inventoryItems = await Inventory.findAll({
|
||||
where: { stockId: stockIds },
|
||||
include: [
|
||||
{
|
||||
model: FalukantStock,
|
||||
as: 'stock',
|
||||
include: [
|
||||
{
|
||||
model: Branch,
|
||||
as: 'branch',
|
||||
include: [
|
||||
{
|
||||
model: RegionData,
|
||||
as: 'region',
|
||||
include: [
|
||||
{
|
||||
model: RegionType,
|
||||
as: 'regionType',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
model: FalukantStockType,
|
||||
as: 'stockType',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
model: ProductType,
|
||||
as: 'productType',
|
||||
},
|
||||
],
|
||||
});
|
||||
const groupedInventory = inventoryItems.reduce((acc, item) => {
|
||||
const region = item.stock.branch.region;
|
||||
const key = `${region.id}-${item.productType.id}-${item.quality}`;
|
||||
if (!acc[key]) {
|
||||
acc[key] = {
|
||||
region,
|
||||
product: item.productType,
|
||||
quality: item.quality,
|
||||
totalQuantity: 0,
|
||||
};
|
||||
}
|
||||
acc[key].totalQuantity += item.quantity;
|
||||
return acc;
|
||||
}, {});
|
||||
const sortedInventory = Object.values(groupedInventory).sort((a, b) => {
|
||||
if (a.region.id !== b.region.id) {
|
||||
return a.region.id - b.region.id;
|
||||
}
|
||||
if (a.product.id !== b.product.id) {
|
||||
return a.product.id - b.product.id;
|
||||
}
|
||||
return a.quality - b.quality;
|
||||
});
|
||||
|
||||
return sortedInventory;
|
||||
} catch (error) {
|
||||
console.error('Error in getInventory:', error);
|
||||
throw new Error('Failed to retrieve inventory');
|
||||
}
|
||||
}
|
||||
|
||||
async sellProduct(hashedUserId, branchId, productId, quality, quantity) {
|
||||
try {
|
||||
const falukantUser = await this.getFalukantUserByHashedId(hashedUserId);
|
||||
if (!falukantUser) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
const branch = await Branch.findOne({ where: { id: branchId, falukantUserId: falukantUser.id } });
|
||||
if (!branch) {
|
||||
throw new Error('Branch not found');
|
||||
}
|
||||
const product = await ProductType.findOne({ where: { id: productId } });
|
||||
if (!product) {
|
||||
throw new Error('Product not found');
|
||||
}
|
||||
const stock = await Stock.findOne({ where: { branchId: branch.id, } });
|
||||
if (!stock) {
|
||||
throw new Error('Stock not found');
|
||||
}
|
||||
if (stock.quantity < quantity) {
|
||||
throw new Error('Not enough stock');
|
||||
}
|
||||
await FalukantStock.decrement('quantity', { by: quantity, where: { regionId: branch.regionId, stockTypeId: product.id } });
|
||||
const inventory = await Inventory.create({ stockId: stock.id, quality, quantity });
|
||||
return inventory;
|
||||
} catch (error) {
|
||||
console.error('Error in sellProduct:', error);
|
||||
throw new Error('Failed to sell product');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new FalukantService();
|
||||
|
||||
@@ -235,7 +235,7 @@ async function initializeFalukantProducts() {
|
||||
{ labelTr: 'milk', category: 1, productionTime: 1, sellCost: 4 },
|
||||
{ labelTr: 'cheese', category: 1, productionTime: 1, sellCost: 4 },
|
||||
{ labelTr: 'bread', category: 1, productionTime: 1, sellCost: 4 },
|
||||
{ labelTr: 'beer', category: 1, productionTime: 1, sellCost: 4 },
|
||||
{ labelTr: 'beer', category: 2, productionTime: 3, sellCost: 4 },
|
||||
{ labelTr: 'iron', category: 2, productionTime: 4, sellCost: 15 },
|
||||
{ labelTr: 'copper', category: 2, productionTime: 4, sellCost: 15 },
|
||||
{ labelTr: 'spices', category: 2, productionTime: 8, sellCost: 30 },
|
||||
|
||||
@@ -59,7 +59,6 @@ export default {
|
||||
watch: {
|
||||
modelValue(newVal) {
|
||||
this.selected = newVal;
|
||||
console.log("FormattedDropdown modelValue changed:", newVal);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -109,12 +109,35 @@
|
||||
"info": "Informationen über den Direktor der Niederlassung."
|
||||
},
|
||||
"sale": {
|
||||
"title": "Verkauf",
|
||||
"info": "Hier können Produkte verkauft werden."
|
||||
"title": "Inventar",
|
||||
"info": "Hier finden Sie eine Übersicht über die vorhandenen Produkte in der Filiale.",
|
||||
"region": "Region",
|
||||
"product": "Produkt",
|
||||
"quality": "Qualität",
|
||||
"quantity": "Menge",
|
||||
"noInventory": "Kein Inventar verfügbar.",
|
||||
"loadError": "Fehler beim Laden des Inventars.",
|
||||
"sell": "Verkauf",
|
||||
"sellButton": "Verkaufen",
|
||||
"sellAllButton": "Alles verkaufen"
|
||||
},
|
||||
"production": {
|
||||
"title": "Produktion",
|
||||
"info": "Details zur Produktion in der Niederlassung."
|
||||
"info": "Details zur Produktion in der Niederlassung.",
|
||||
"selectProduct": "Produkt auswählen",
|
||||
"quantity": "Menge",
|
||||
"cost": "Kosten",
|
||||
"duration": "Dauer",
|
||||
"revenue": "Erlös",
|
||||
"start": "Produktion starten",
|
||||
"success": "Produktion erfolgreich gestartet!",
|
||||
"error": "Fehler beim Starten der Produktion.",
|
||||
"minutes": "Minuten",
|
||||
"ending": "Abgeschlossen:",
|
||||
"time": "Uhr",
|
||||
"current": "Laufende Produktionen",
|
||||
"product": "Produkt",
|
||||
"remainingTime": "Verbleibende Zeit (Sekunden)"
|
||||
},
|
||||
"columns": {
|
||||
"city": "Stadt",
|
||||
@@ -124,7 +147,53 @@
|
||||
"production": "Produktion",
|
||||
"store": "Verkauf",
|
||||
"fullstack": "Produktion mit Verkauf"
|
||||
},
|
||||
"revenue": {
|
||||
"title": "Produkt-Erträge",
|
||||
"product": "Produkt",
|
||||
"absolute": "Erlös (absolut)",
|
||||
"perMinute": "Erlös pro Minute",
|
||||
"expand": "Erträge anzeigen",
|
||||
"collapse": "Erträge ausblenden",
|
||||
"knowledge": "Produktwissen"
|
||||
}
|
||||
},
|
||||
"product": {
|
||||
"wheat": "Weizen",
|
||||
"grain": "Getreide",
|
||||
"carrot": "Karotte",
|
||||
"fish": "Fisch",
|
||||
"meat": "Fleisch",
|
||||
"leather": "Leder",
|
||||
"wood": "Holz",
|
||||
"stone": "Stein",
|
||||
"milk": "Milch",
|
||||
"cheese": "Käse",
|
||||
"bread": "Brot",
|
||||
"beer": "Bier",
|
||||
"iron": "Eisen",
|
||||
"copper": "Kupfer",
|
||||
"spices": "Gewürze",
|
||||
"salt": "Salz",
|
||||
"sugar": "Zucker",
|
||||
"vinegar": "Essig",
|
||||
"cotton": "Baumwolle",
|
||||
"wine": "Wein",
|
||||
"gold": "Gold",
|
||||
"diamond": "Diamant",
|
||||
"furniture": "Möbel",
|
||||
"clothing": "Kleidung",
|
||||
"jewelry": "Schmuck",
|
||||
"painting": "Gemälde",
|
||||
"book": "Buch",
|
||||
"weapon": "Waffe",
|
||||
"armor": "Rüstung",
|
||||
"shield": "Schild",
|
||||
"horse": "Pferd",
|
||||
"ox": "Ochse"
|
||||
},
|
||||
"regionType": {
|
||||
"city": "Stadt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ const store = createStore({
|
||||
language: navigator.language.startsWith('de') ? 'de' : 'en',
|
||||
menu: JSON.parse(localStorage.getItem('menu')) || [],
|
||||
socket: null,
|
||||
daemonSocket: null,
|
||||
menuNeedsUpdate: false,
|
||||
},
|
||||
mutations: {
|
||||
@@ -50,11 +51,21 @@ const store = createStore({
|
||||
}
|
||||
state.socket = null;
|
||||
},
|
||||
setDaemonSocket(state, daemonSocket) {
|
||||
state.daemonSocket = daemonSocket;
|
||||
},
|
||||
clearDaemonSocket(state) {
|
||||
if (state.daemonSocket) {
|
||||
state.daemonSocket.disconnect();
|
||||
}
|
||||
state.daemonSocket = null;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async login({ commit, dispatch }, user) {
|
||||
await commit('dologin', user);
|
||||
await dispatch('initializeSocket');
|
||||
await dispatch('initializeDaemonSocket');
|
||||
const socket = this.getters.socket;
|
||||
if (socket) {
|
||||
socket.emit('setUserId', user.id);
|
||||
@@ -63,19 +74,89 @@ const store = createStore({
|
||||
},
|
||||
logout({ commit }) {
|
||||
commit('clearSocket');
|
||||
commit('clearDaemonSocket');
|
||||
commit('dologout');
|
||||
router.push('/');
|
||||
},
|
||||
initializeSocket({ commit, state }) {
|
||||
if (state.isLoggedIn && state.user) {
|
||||
const socket = io(import.meta.env.VITE_API_BASE_URL);
|
||||
socket.on('connect', () => {
|
||||
socket.emit('setUserId', state.user.id);
|
||||
});
|
||||
socket.on('disconnect', (reason) => {
|
||||
console.warn('WebSocket disconnected:', reason);
|
||||
});
|
||||
commit('setSocket', socket);
|
||||
const connectSocket = () => {
|
||||
const socket = io(import.meta.env.VITE_API_BASE_URL);
|
||||
|
||||
socket.on('connect', () => {
|
||||
console.log('Socket.io connected');
|
||||
socket.emit('setUserId', state.user.id);
|
||||
});
|
||||
|
||||
socket.on('disconnect', (reason) => {
|
||||
console.warn('Socket.io disconnected:', reason);
|
||||
retryConnection(connectSocket);
|
||||
});
|
||||
|
||||
commit('setSocket', socket);
|
||||
};
|
||||
|
||||
const retryConnection = (reconnectFn) => {
|
||||
setTimeout(() => {
|
||||
console.log('Retrying Socket.io connection...');
|
||||
reconnectFn();
|
||||
}, 1000); // Retry every second
|
||||
};
|
||||
|
||||
connectSocket();
|
||||
}
|
||||
},
|
||||
initializeDaemonSocket({ commit, state }) {
|
||||
if (state.isLoggedIn && state.user) {
|
||||
const connectDaemonSocket = () => {
|
||||
const daemonSocket = new WebSocket(import.meta.env.VITE_DAEMON_SOCKET);
|
||||
|
||||
daemonSocket.onopen = () => {
|
||||
console.log('Daemon WebSocket connected');
|
||||
const payload = JSON.stringify({
|
||||
event: 'setUserId',
|
||||
data: { userId: state.user.id }
|
||||
});
|
||||
daemonSocket.send(payload);
|
||||
};
|
||||
|
||||
daemonSocket.onclose = (event) => {
|
||||
console.warn('Daemon WebSocket disconnected:', event.reason);
|
||||
retryConnection(connectDaemonSocket);
|
||||
};
|
||||
|
||||
daemonSocket.onerror = (error) => {
|
||||
console.error('Daemon WebSocket error:', error);
|
||||
retryConnection(connectDaemonSocket);
|
||||
};
|
||||
|
||||
daemonSocket.addEventListener('message', (event) => {
|
||||
const message = event.data;
|
||||
console.log(message);
|
||||
if (message === "ping") {
|
||||
console.log("Ping received, sending Pong...");
|
||||
daemonSocket.send("pong");
|
||||
} else {
|
||||
try {
|
||||
const data = JSON.parse(message);
|
||||
console.log("Message received:", data);
|
||||
} catch (error) {
|
||||
console.error("Error parsing message:", error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
commit('setDaemonSocket', daemonSocket);
|
||||
};
|
||||
|
||||
const retryConnection = (reconnectFn) => {
|
||||
setTimeout(() => {
|
||||
console.log('Retrying Daemon WebSocket connection...');
|
||||
reconnectFn();
|
||||
}, 1000); // Retry every second
|
||||
};
|
||||
|
||||
connectDaemonSocket();
|
||||
}
|
||||
},
|
||||
setLanguage({ commit }, language) {
|
||||
@@ -97,6 +178,7 @@ const store = createStore({
|
||||
language: state => state.language,
|
||||
menu: state => state.menu,
|
||||
socket: state => state.socket,
|
||||
daemonSocket: state => state.daemonSocket,
|
||||
menuNeedsUpdate: state => state.menuNeedsUpdate,
|
||||
},
|
||||
modules: {
|
||||
@@ -106,6 +188,7 @@ const store = createStore({
|
||||
|
||||
if (store.state.isLoggedIn && store.state.user) {
|
||||
store.dispatch('initializeSocket');
|
||||
store.dispatch('initializeDaemonSocket');
|
||||
}
|
||||
|
||||
export default store;
|
||||
|
||||
@@ -31,26 +31,150 @@
|
||||
<div class="sale-section">
|
||||
<h3>{{ $t('falukant.branch.sale.title') }}</h3>
|
||||
<p>{{ $t('falukant.branch.sale.info') }}</p>
|
||||
<div class="inventory-table" v-if="inventory.length > 0">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.branch.sale.region') }}</th>
|
||||
<th>{{ $t('falukant.branch.sale.product') }}</th>
|
||||
<th>{{ $t('falukant.branch.sale.quality') }}</th>
|
||||
<th>{{ $t('falukant.branch.sale.quantity') }}</th>
|
||||
<th>{{ $t('falukant.branch.sale.sell') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in inventory"
|
||||
:key="`${item.region.id}-${item.product.id}-${item.quality}`">
|
||||
<td>{{ item.region.name }} ({{ $t(`falukant.regionType.${item.region.regionType.labelTr}`)
|
||||
}})</td>
|
||||
<td>{{ $t(`falukant.product.${item.product.labelTr}`) }}</td>
|
||||
<td>{{ item.quality }}</td>
|
||||
<td>{{ item.totalQuantity }}</td>
|
||||
<td>
|
||||
<input type="number" v-model.number="item.sellQuantity" :min="1"
|
||||
:max="item.totalQuantity" :placeholder="item.totalQuantity" />
|
||||
<button @click="sellItem(index)">{{ $t('falukant.branch.sale.sellButton') }}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<button @click="sellAll">{{ $t('falukant.branch.sale.sellAllButton') }}</button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>{{ $t('falukant.branch.sale.noInventory') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Production Section -->
|
||||
<div class="production-section">
|
||||
<div class="production-section" v-if="['fullstack', 'production'].includes(branchData?.branchType?.labelTr)">
|
||||
<h3>{{ $t('falukant.branch.production.title') }}</h3>
|
||||
<p>{{ $t('falukant.branch.production.info') }}</p>
|
||||
<div v-if="this.productions?.length > 0">
|
||||
<h4>{{ $t('falukant.branch.production.current') }}</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.branch.production.product') }}</th>
|
||||
<th>{{ $t('falukant.branch.production.quantity') }}</th>
|
||||
<th>{{ $t('falukant.branch.production.ending') }}</th>
|
||||
<th>{{ $t('falukant.branch.production.remainingTime') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="production in productions" :key="production.id">
|
||||
<td>{{ $t(`falukant.product.${production.productType.labelTr}`) }}</td>
|
||||
<td>{{ production.quantity }}</td>
|
||||
<td>{{ calculateEndDateTime(production.startTimestamp,
|
||||
production.productType.productionTime) }}</td>
|
||||
<td>{{ calculateRemainingTime(production.startTimestamp,
|
||||
production.productType.productionTime) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<template v-if="!this.productions || this.productions.length < 2">
|
||||
<div>
|
||||
<label for="product">{{ $t('falukant.branch.production.selectProduct') }}</label>
|
||||
<select name="product" id="product" v-model="selectedProduct">
|
||||
<option v-for="product in products" :key="product.id" :value="product.id">
|
||||
{{ $t(`falukant.product.${product.labelTr}`) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="quantity">{{ $t('falukant.branch.production.quantity') }}</label>
|
||||
<input type="number" id="quantity" v-model.number="productionQuantity" min="1" />
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
{{ $t('falukant.branch.production.cost') }}:
|
||||
<strong>{{ calculateProductionCost() }}</strong>
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('falukant.branch.production.duration') }}:
|
||||
<strong>{{ calculateProductionDuration(selectedProduct?.id) }}</strong>
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('falukant.branch.production.revenue') }}:
|
||||
<strong>{{ calculateProductionRevenue() }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
<button @click="startProduction" :disabled="!selectedProduct || productionQuantity < 1">
|
||||
{{ $t('falukant.branch.production.start') }}
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="revenue-section">
|
||||
<h3>
|
||||
<button @click="toggleRevenueTable">
|
||||
{{ $t('falukant.branch.revenue.title') }}
|
||||
{{ isRevenueTableOpen ? '▲' : '▼' }}
|
||||
</button>
|
||||
</h3>
|
||||
<div v-if="isRevenueTableOpen" class="revenue-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.branch.revenue.product') }}</th>
|
||||
<th>{{ $t('falukant.branch.revenue.knowledge') }}</th>
|
||||
<th>{{ $t('falukant.branch.revenue.absolute') }}</th>
|
||||
<th>{{ $t('falukant.branch.revenue.perMinute') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="product in products" :key="product.id">
|
||||
<td>{{ $t(`falukant.product.${product.labelTr}`) }}</td>
|
||||
<td>{{ product.knowledges[0]?.knowledge }} %</td>
|
||||
<td>{{ calculateProductRevenue(product).absolute }}</td>
|
||||
<td>{{ calculateProductRevenue(product).perMinute }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MessageDialog ref="messageDialog" />
|
||||
<ErrorDialog ref="errorDialog" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StatusBar from '@/components/falukant/StatusBar.vue';
|
||||
import FormattedDropdown from '@/components/form/FormattedDropdown.vue';
|
||||
import apiClient from '@/utils/axios.js';
|
||||
import MessageDialog from '@/dialogues/standard/MessageDialog.vue';
|
||||
import ErrorDialog from '@/dialogues/standard/ErrorDialog.vue';
|
||||
import { mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "BranchView",
|
||||
components: {
|
||||
StatusBar,
|
||||
FormattedDropdown,
|
||||
MessageDialog,
|
||||
ErrorDialog,
|
||||
},
|
||||
computed: {
|
||||
...mapState(["socket", "daemonSocket"]),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -60,19 +184,64 @@ export default {
|
||||
{ field: "cityName", label: this.$t('falukant.branch.columns.city') },
|
||||
{ field: "type", label: this.$t('falukant.branch.columns.type') },
|
||||
],
|
||||
products: [],
|
||||
branchData: null,
|
||||
selectedProduct: null,
|
||||
productionQuantity: 1,
|
||||
isRevenueTableOpen: false,
|
||||
inventory: [],
|
||||
currentTime: Date.now(),
|
||||
timer: null,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadBranches();
|
||||
const branchId = this.$route.params.branchId;
|
||||
console.log('route params:', this.$route.params, branchId);
|
||||
const products = await apiClient.get('/api/falukant/products');
|
||||
this.products = products.data;
|
||||
console.log('products loaded');
|
||||
if (branchId) {
|
||||
console.log('branch selected');
|
||||
this.selectedBranch = this.branches.find(branch => branch.id === parseInt(branchId)) || null;
|
||||
} else {
|
||||
console.log('main branch selected');
|
||||
this.selectMainBranch();
|
||||
}
|
||||
if (this.socket) {
|
||||
this.socket.on("falukantBranchUpdate", this.fetchBranch);
|
||||
}
|
||||
if (this.daemonSocket) {
|
||||
this.daemonSocket.addEventListener('message', (event) => {
|
||||
try {
|
||||
if (event.data === "ping") {
|
||||
return;
|
||||
}
|
||||
const message = JSON.parse(event.data);
|
||||
console.log('Daemon WebSocket message received in BranchView:', message);
|
||||
|
||||
if (message.event === 'production_ready' && message.branch_id === this.selectedBranch?.id) {
|
||||
this.handleProductionReadyEvent(message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error processing WebSocket message in BranchView:', error);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error('Daemon socket is not initialized.');
|
||||
}
|
||||
this.startTimer();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.stopTimer();
|
||||
if (this.socket) {
|
||||
this.socket.off("falukantBranchUpdate", this.fetchBranch);
|
||||
}
|
||||
if (this.daemonSocket) {
|
||||
this.daemonSocket.onmessage = null;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
async selectedBranch(newBranch) {
|
||||
await this.loadBranchData(newBranch);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadBranches() {
|
||||
@@ -84,7 +253,6 @@ export default {
|
||||
type: this.$t(`falukant.branch.types.${branch.branchType.labelTr}`),
|
||||
isMainBranch: branch.isMainBranch,
|
||||
}));
|
||||
// Wenn keine selectedBranch gesetzt ist, versuche die Main Branch zu wählen
|
||||
if (!this.selectedBranch) {
|
||||
this.selectMainBranch();
|
||||
}
|
||||
@@ -92,11 +260,16 @@ export default {
|
||||
console.error('Error loading branches:', error);
|
||||
}
|
||||
},
|
||||
async loadBranchData(newBranch) {
|
||||
if (newBranch) {
|
||||
this.getBranchData(newBranch);
|
||||
await this.loadInventory();
|
||||
}
|
||||
},
|
||||
selectMainBranch() {
|
||||
const main = this.branches.find(b => b.isMainBranch) || null;
|
||||
if (main !== this.selectedBranch) {
|
||||
this.selectedBranch = main;
|
||||
console.log("Main branch selected:", this.selectedBranch);
|
||||
}
|
||||
},
|
||||
createBranch() {
|
||||
@@ -109,6 +282,163 @@ export default {
|
||||
);
|
||||
}
|
||||
},
|
||||
calculateProductionCost() {
|
||||
if (!this.products) {
|
||||
return 0;
|
||||
}
|
||||
const product = this.products.find(p => p.id === this.selectedProduct);
|
||||
if (product) {
|
||||
return 7 * product.category * this.productionQuantity;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
calculateProductionDuration(productId) {
|
||||
if (!this.products || !productId) {
|
||||
return 0;
|
||||
}
|
||||
const product = this.products.find(p => p.id === productId);
|
||||
if (!product) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const totalMinutes = product.productionTime * 60;
|
||||
const decimalTime = (totalMinutes / 60).toFixed(2);
|
||||
|
||||
return decimalTime;
|
||||
},
|
||||
async startProduction() {
|
||||
if (this.selectedBranch && this.selectedProduct && this.productionQuantity > 0) {
|
||||
try {
|
||||
await apiClient.post(`/api/falukant/production`, {
|
||||
branchId: this.selectedBranch.id,
|
||||
productId: this.selectedProduct,
|
||||
quantity: this.productionQuantity,
|
||||
});
|
||||
this.$root.$refs.messageDialog.open(this.$t('falukant.branch.production.success'));
|
||||
} catch (error) {
|
||||
this.$root.$refs.errorDialog.open(this.$t(`falukant.branch.production.error${error.response.data.error}`));
|
||||
}
|
||||
}
|
||||
},
|
||||
calculateProductionRevenue() {
|
||||
if (!this.selectedProduct) {
|
||||
return 0;
|
||||
}
|
||||
if (!this.products) {
|
||||
return 0;
|
||||
}
|
||||
const product = this.products.find(p => p.id === this.selectedProduct);
|
||||
if (!product) {
|
||||
return 0;
|
||||
}
|
||||
const productRevenue = this.calculateProductRevenue(product);
|
||||
return productRevenue.absolute;
|
||||
},
|
||||
toggleRevenueTable() {
|
||||
this.isRevenueTableOpen = !this.isRevenueTableOpen;
|
||||
},
|
||||
calculateProductRevenue(product) {
|
||||
if (!product.knowledges || product.knowledges.length === 0) {
|
||||
return { absolute: 0, perMinute: 0 };
|
||||
}
|
||||
const knowledgeFactor = product.knowledges[0]?.knowledge || 0;
|
||||
const maxPrice = product.category * 9;
|
||||
const minPrice = maxPrice * 0.6;
|
||||
const revenuePerUnit = minPrice + (maxPrice - minPrice) * (knowledgeFactor / 100);
|
||||
const productionTimeInMinutes = product.productionTime;
|
||||
const perMinute = (revenuePerUnit / productionTimeInMinutes).toFixed(2);
|
||||
return {
|
||||
absolute: revenuePerUnit.toFixed(2),
|
||||
perMinute: perMinute,
|
||||
};
|
||||
},
|
||||
async fetchBranch(data) {
|
||||
try {
|
||||
if (data.branchId === this.selectedBranch.id) {
|
||||
this.getBranchData();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
async getBranchData(newBranch = null) {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/falukant/branches/${newBranch ? newBranch.id : this.selectedBranch.id}`);
|
||||
this.branchData = response.data;
|
||||
this.director = response.data.director;
|
||||
this.productions = response.data.productions.sort((a, b) => {
|
||||
const endTimeA = new Date(a.startTimestamp).getTime() + a.productType.productionTime * 60 * 1000;
|
||||
const endTimeB = new Date(b.startTimestamp).getTime() + b.productType.productionTime * 60 * 1000;
|
||||
return endTimeA - endTimeB;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
calculateEndDateTime(startTimestamp, productionTime) {
|
||||
const startTime = new Date(startTimestamp);
|
||||
const endTime = new Date(startTime.getTime() + productionTime * 60 * 1000);
|
||||
return endTime.toLocaleString(); // Datum und Uhrzeit
|
||||
},
|
||||
calculateRemainingTime(startTimestamp, productionTime) {
|
||||
const startTime = new Date(startTimestamp).getTime();
|
||||
const endTime = startTime + productionTime * 60 * 1000;
|
||||
const now = Date.now();
|
||||
const remainingSeconds = Math.max(Math.floor((endTime - now) / 1000), 0);
|
||||
return remainingSeconds; // Verbleibende Zeit in Sekunden
|
||||
},
|
||||
startTimer() {
|
||||
this.timer = setInterval(() => {
|
||||
this.$forceUpdate(); // Aktualisiert die verbleibende Zeit in der Tabelle
|
||||
}, 1000);
|
||||
},
|
||||
stopTimer() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
},
|
||||
sellItem(index) {
|
||||
const item = this.inventory[index];
|
||||
console.log(`Selling ${item.sellQuantity || item.totalQuantity} of ${item.product.labelTr}`);
|
||||
const quantityToSell = item.sellQuantity || item.totalQuantity;
|
||||
item.totalQuantity -= quantityToSell;
|
||||
if (item.totalQuantity <= 0) {
|
||||
this.inventory.splice(index, 1);
|
||||
}
|
||||
item.sellQuantity = null;
|
||||
},
|
||||
sellAll() {
|
||||
console.log('Selling all items in inventory');
|
||||
this.inventory.forEach(item => {
|
||||
console.log(`Selling ${item.totalQuantity} of ${item.product.labelTr}`);
|
||||
});
|
||||
this.inventory = []; // Leert das Inventar
|
||||
},
|
||||
async loadInventory() {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/falukant/inventory/${this.selectedBranch?.id}`, {});
|
||||
this.inventory = response.data.map(item => ({
|
||||
...item,
|
||||
sellQuantity: item.totalQuantity, // Voreinstellung
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error loading inventory:', error);
|
||||
this.$refs.errorDialog.open(this.$t('falukant.branch.sale.loadError'));
|
||||
}
|
||||
},
|
||||
async handleProductionReadyEvent(message) {
|
||||
try {
|
||||
console.log('Production ready event received:', message);
|
||||
if (message.branch_id === this.selectedBranch?.id) {
|
||||
await this.loadBranchData(this.selectedBranch);
|
||||
await this.loadInventory();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error processing production_ready event:', error);
|
||||
console.error(message);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -127,4 +457,110 @@ export default {
|
||||
button {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.production-section {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.production-section label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.production-section input[type="number"] {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.revenue-section {
|
||||
border: 1px solid #ccc;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.revenue-section h3 {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.revenue-section button {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #007bff;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.revenue-section button:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.revenue-table {
|
||||
margin-top: 10px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.revenue-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.revenue-table th,
|
||||
.revenue-table td {
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.revenue-table th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.production-section table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.production-section th,
|
||||
.production-section td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.production-section th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.inventory-table {
|
||||
margin-top: 10px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.inventory-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.inventory-table th,
|
||||
.inventory-table td {
|
||||
text-align: left;
|
||||
padding: 2px 3px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.inventory-table td button {
|
||||
margin: 0 0 0 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.inventory-table th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user