Add functionality for managing custom user discounts

Introduced API endpoints to set and retrieve user-specific discounts. Integrated discount logic into orders, with adjustments based on category or product. Added a new object `user_price_overrides_o` for handling price overrides along with relevant updates to existing classes.
This commit is contained in:
Jepp9350
2025-01-08 15:31:02 +01:00
parent eec7a17672
commit 51c5e53c6a
8 changed files with 360 additions and 43 deletions
+7
View File
@@ -125,6 +125,13 @@ class order_items_o extends db
$row = $db->fetch_assoc($result);
$price = $row['price'];
// Check if the user has a discount on the product, or category
$customer = (new orders_o())->getOrderCustomer($order_id);
$discount = $customer->getCustomPrice($product_id, false);
if ($discount) {
$price = $price - ($price * $discount / 100);
}
// Create a new record in the database
$sql = "INSERT INTO $this->table (order_id, product_id, price, cashier_id, quantity) VALUES ($order_id, $product_id, $price, $cashier_id, $quantity)";
$db->query($sql);
+35 -28
View File
@@ -28,34 +28,6 @@ class orders_o extends db
$this->setTable('orders');
}
public function getOrderById(int $id): orders_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
public function getObjectProperties(): void
{
$this->customer_id = new object_property($this->table, $this->id, 'customer_id', 'int', true);
$this->cashier_id = new object_property($this->table, $this->id, 'cashier_id', 'int', true);
$this->reference = new object_property($this->table, $this->id, 'reference', 'string', true);
$this->notes = new object_property($this->table, $this->id, 'notes', 'string', false);
$this->department_id = new object_property($this->table, $this->id, 'department_id', 'int', true);
$this->reg_1 = new object_property($this->table, $this->id, 'reg_1', 'string', false);
$this->reg_2 = new object_property($this->table, $this->id, 'reg_2', 'string', false);
$this->reg_3 = new object_property($this->table, $this->id, 'reg_3', 'string', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', false);
$this->economic_module_orders = (new economic_module_orders())->getByOrderId($this->id);
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
}
public function add(int $customer_id, int $cashier_id, string $reference, string $notes, int $department_id, string $reg_1 = '', string $reg_2 = '', string $reg_3 = ''): orders_o
{
global $db, $response;
@@ -82,6 +54,21 @@ class orders_o extends db
}
}
public function getObjectProperties(): void
{
$this->customer_id = new object_property($this->table, $this->id, 'customer_id', 'int', true);
$this->cashier_id = new object_property($this->table, $this->id, 'cashier_id', 'int', true);
$this->reference = new object_property($this->table, $this->id, 'reference', 'string', true);
$this->notes = new object_property($this->table, $this->id, 'notes', 'string', false);
$this->department_id = new object_property($this->table, $this->id, 'department_id', 'int', true);
$this->reg_1 = new object_property($this->table, $this->id, 'reg_1', 'string', false);
$this->reg_2 = new object_property($this->table, $this->id, 'reg_2', 'string', false);
$this->reg_3 = new object_property($this->table, $this->id, 'reg_3', 'string', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', false);
$this->economic_module_orders = (new economic_module_orders())->getByOrderId($this->id);
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
}
public function edit(int $id, int $customer_id, int $cashier_id, string $reference, string $notes, int $department_id): void
{
global $db, $response;
@@ -235,4 +222,24 @@ class orders_o extends db
{
$this->economic_module_orders->unlinkAllOrdersFromDraft($draftInvoiceNumber);
}
public function getOrderCustomer(int $orderId): users_o
{
$order = new orders_o();
$order->getOrderById($orderId);
return (new users_o())->getCustomerByIdOrCustomerNumber($order->customer_id->value());
}
public function getOrderById(int $id): orders_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
}
+121
View File
@@ -0,0 +1,121 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class user_price_overrides_o extends db
{
use db_object_t;
public int $user_id;
public object_property $is_category;
public object_property $product_or_category_id;
public object_property $percentage;
public function structure(): void
{
$this->setTable('price_overrides');
}
public function getObjectProperties(): void
{
$this->is_category = new object_property($this->table, $this->id, 'is_category', 'bool', true);
$this->product_or_category_id = new object_property($this->table, $this->id, 'product_or_category_id', 'int', true);
$this->percentage = new object_property($this->table, $this->id, 'percentage', 'int', true);
}
public function setUser($user_id): user_price_overrides_o
{
$this->user_id = $user_id;
return $this;
}
/**
* Set a price override for the user
* @param bool $is_category
* @param int $product_or_category_id
* @param int $percentage
* @return $this
*/
public function setPrice(bool $is_category, int $product_or_category_id, int $percentage): user_price_overrides_o
{
global $db;
// If the user is not set, return the object
if (!isset($this->user_id)) {
return $this;
}
// Check if the record already exists
$this->removePriceIfExist($is_category, $product_or_category_id);
// Create a new record in the database
$sql = "INSERT INTO $this->table (user_id, is_category, product_or_category_id, percentage) VALUES ($this->user_id, " . (int)$is_category . ", $product_or_category_id, $percentage)";
$db->query($sql);
return $this;
}
private function removePriceIfExist(bool $is_category, int $product_or_category_id): void
{
global $db;
// Get the price override from the database
$sql = "SELECT * FROM $this->table WHERE user_id = " . $this->user_id . " AND is_category = " . (int)$is_category . " AND product_or_category_id = " . $product_or_category_id;
$result = $db->query($sql);
if ($result->num_rows > 0) {
// Remove the record from the database
$sql = "DELETE FROM $this->table WHERE user_id = " . $this->user_id . " AND is_category = " . (int)$is_category . " AND product_or_category_id = " . $product_or_category_id;
$db->query($sql);
}
}
/**
* Get the price override (PERCENTAGE) for the user
* @param bool $is_category
* @param int|string $product_or_category_id
* @return int
*/
public function getPrice(bool $is_category, int|string $product_or_category_id): int
{
global /** @var db $db */
$db;
$sql = "SELECT * FROM $this->table WHERE user_id = " . $this->user_id . " AND is_category = " . (int)$is_category . " AND product_or_category_id = ";
// If the is_category is false, the product_or_category_id is a product_id
if (!$is_category) {
$product_or_category_id = (int)$product_or_category_id;
$sql .= $product_or_category_id;
} else {
// If the is_category is true, the product_or_category_id is a category_id
$product_or_category_id = $db->escape_string($product_or_category_id);
$sql .= "'$product_or_category_id'";
}
// Get the price override from the database
$result = $db->query($sql);
if ($result->num_rows > 0) {
// Return the percentage
return $result->fetch_assoc()['percentage'];
}
// Return 0 if no price override is set
return 0;
}
/**
* Get all the price overrides for the user
* @return array
*/
public function getAllPrices(): array
{
global $db;
// Get all the price overrides for the user
$sql = "SELECT * FROM $this->table WHERE user_id = " . $this->user_id;
$result = $db->query($sql);
$prices = [];
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$prices[] = $row;
}
}
return $prices;
}
}
+69 -1
View File
@@ -22,8 +22,10 @@ class users_o extends db
public object_property $updated_at;
public array $permissions;
public array $attributes; // The attributes of the user
public array $discounts; // The discounts of the user
public customer_codes_o $customer_codes;
public user_key_value_pairs_o $keys;
public user_price_overrides_o $price_overrides;
public function structure(): void
{
@@ -74,6 +76,7 @@ class users_o extends db
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false);
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'string', false);
$this->keys = (new user_key_value_pairs_o())->setUser($this->id);
$this->price_overrides = (new user_price_overrides_o())->setUser($this->id);
}
public function getCustomerByIdOrCustomerNumber(int $idOrCustomerNumber): users_o
@@ -184,6 +187,10 @@ class users_o extends db
if (isset($this->attributes)) {
$array['attributes'] = $this->attributes;
}
// If the discounts are set, add them to the array
if (isset($this->discounts)) {
$array['discounts'] = $this->discounts;
}
return $array;
}
@@ -362,6 +369,12 @@ class users_o extends db
if ($includeEverything || $response->getRequestParameter('includeAttributes') === 'true' || in_array('attributes', $includes)) {
$this->getUserAttributes();
}
/**
* Discounts
*/
if ($includeEverything || $response->getRequestParameter('includeDiscounts') === 'true' || in_array('discounts', $includes)) {
$this->getAllDiscounts();
}
return $this;
}
@@ -403,6 +416,16 @@ class users_o extends db
return $this->attributes;
}
/**
* Get all discounts for the user (Include)
* @return void
*/
public function getAllDiscounts(): void
{
// Get all discounts (key = 'custom_price')
$this->discounts = $this->price_overrides->setUser($this->id)->getAllPrices();
}
public function getCode(): string|null
{
// Get the customer code
@@ -487,10 +510,55 @@ class users_o extends db
return (int)$this->keys->setUser($this->id)->getValue('open_invoice_draft');
}
public function unsetOpenInvoiceDraft()
public function unsetOpenInvoiceDraft(): void
{
// Unset the open invoice draft (key = 'open_invoice_draft')
$this->keys->setUser($this->id)->deleteValue('open_invoice_draft');
}
/**
* Set a custom price (DISCOUNT) for the user
* @param int $user_id
* @param int $object_id The ID of the object
* @param int $discount_percentage The discount percentage
* @param bool $is_category If the object is a category
* @return void
*/
public function setCustomPrice(int $user_id, int $object_id, int $discount_percentage, bool $is_category = false): void
{
$this->id = $user_id;
// Get the user object properties
$this->getObjectProperties();
// Set the custom price (key = 'custom_price')
$this->price_overrides->setUser($this->id)->setPrice($is_category, $object_id, $discount_percentage);
}
/**
* Get the custom price (DISCOUNT) for the user
* @param int $object_id The ID of the object
* @param bool $is_category If the object is a category
* @return int|null The discount percentage
*/
public function getCustomPrice(int $object_id, bool $is_category = false): int|null
{
// Get the custom price for the product
$discount = $this->price_overrides->setUser($this->id)->getPrice($is_category, $object_id);
// If the is_category is false, check if there is a custom price for the category that the product belongs to
if ($discount === 0 && !$is_category) {
$product = new products_o();
$product->getProductById($object_id);
$discount = $this->price_overrides->setUser($this->id)->getPrice(true, $product->category->value());
}
return $discount;
}
/**
* Get all custom prices (DISCOUNT) for the user
* @return array The custom prices
*/
public function getCustomPrices(): array
{
// Get the custom prices (key = 'custom_price')
return $this->price_overrides->setUser($this->id)->getAllPrices();
}
}
+1 -1
View File
@@ -89,7 +89,7 @@ class economicInvoiceRoute
if (!isset($result)) {
$result = $economic_invoice_draft->createInvoiceDraftExample();
}
// Check if the invoice draft was created, or if the lines were added (lines is an array, and should be 0 if it failed)
// Check if the invoice draft was created, or if the lines were added (lines is an array, and should not be empty)
if (!isset($result->draftInvoiceNumber) && !isset($result->lines[0])) {
// Log the error
(new logs_o())->add('economic_invoice_draft', 'global', 3, $user->id, 'ECONOMIC_INVOICE_DRAFT_EXPORT', 'Failed to create economic invoice draft');
+1
View File
@@ -33,6 +33,7 @@ class orderItemsRoute
if (!isset($data['quantity'])) {
$response->error('Quantity is required', 400);
}
// Add the order item to the order This is done individually, to make the notes to the individual order items possible
(new order_items_o())->addItemToOrder((int)$data['order_id'], (int)$data['product_id'], (int)$user->id, (int)$data['quantity']);
// Return the list of departments
+40 -13
View File
@@ -23,6 +23,7 @@ class productsRoute
if ($user) {
// Check if the category is set in the request
$data = $_GET ?? [];
// Check if the category is set
if (isset($data['category'])) {
// Log the incident
(new logs_o())->add('products', 'global', 1, $user->id, 'LIST_PRODUCTS', 'Successfully listed products in category ' . $data['category']);
@@ -56,12 +57,24 @@ class productsRoute
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['name'])) { $response->error('Name is required', 400); }
if (!isset($data['description'])) { $response->error('Description is required', 400); }
if (!isset($data['price'])) { $response->error('Price is required', 400); }
if (!isset($data['category'])) { $response->error('Category is required', 400); }
if (!isset($data['piktogram'])) { $response->error('Piktogram is required', 400); }
if (!isset($data['economicProductId'])) { $response->error('Economic product ID is required', 400); }
if (!isset($data['name'])) {
$response->error('Name is required', 400);
}
if (!isset($data['description'])) {
$response->error('Description is required', 400);
}
if (!isset($data['price'])) {
$response->error('Price is required', 400);
}
if (!isset($data['category'])) {
$response->error('Category is required', 400);
}
if (!isset($data['piktogram'])) {
$response->error('Piktogram is required', 400);
}
if (!isset($data['economicProductId'])) {
$response->error('Economic product ID is required', 400);
}
// Add the product
(new products_o())->add($data['name'], $data['description'], $data['price'], $data['category'], $data['piktogram'], $data['economicProductId']);
// Log the incident
@@ -87,13 +100,27 @@ class productsRoute
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) { $response->error('ID is required', 400); }
if (!isset($data['name'])) { $response->error('Name is required', 400); }
if (!isset($data['description'])) { $response->error('Description is required', 400); }
if (!isset($data['price'])) { $response->error('Price is required', 400); }
if (!isset($data['category'])) { $response->error('Category is required', 400); }
if (!isset($data['piktogram'])) { $response->error('Piktogram is required', 400); }
if (!isset($data['economicProductId'])) { $response->error('Economic product ID is required', 400); }
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
if (!isset($data['name'])) {
$response->error('Name is required', 400);
}
if (!isset($data['description'])) {
$response->error('Description is required', 400);
}
if (!isset($data['price'])) {
$response->error('Price is required', 400);
}
if (!isset($data['category'])) {
$response->error('Category is required', 400);
}
if (!isset($data['piktogram'])) {
$response->error('Piktogram is required', 400);
}
if (!isset($data['economicProductId'])) {
$response->error('Economic product ID is required', 400);
}
// Edit the product
(new products_o())->edit($data['id'], $data['name'], $data['description'], $data['price'], $data['category'], $data['piktogram'], $data['economicProductId']);
// Log the incident
+86
View File
@@ -3,6 +3,7 @@
namespace routes;
use classes\authentication;
use classes\response;
use objects\logs_o;
use objects\users_o;
use traits\route_t;
@@ -42,5 +43,90 @@ class userRoute
$response->error('Invalid session', 400);
}
});
$this->get('/superuser/user/discounts', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('get_custom_prices_other');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Log the incident
(new logs_o())->add('users', 'global', 1, $user->id, 'GET_CUSTOM_PRICE', 'Successfully fetched custom price');
$targetUser = (new users_o())->automaticGetTargetUserFromRequest();
// Check if the user was found
if (!$targetUser->exists()) {
// Log the incident
(new logs_o())->add('users', 'global', 1, $user->id, 'GET_CUSTOM_PRICE', 'No user found');
// Return an error
$response->error('User not found', 404);
}
// Return the list of users
$response->success(
$targetUser->getCustomPrices()
);
} else {
// Log the incident
(new logs_o())->add('users', 'global', 1, 0, 'GET_CUSTOM_PRICE', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
$this->post('/superuser/user/discounts', function () {
// Require the user to be logged in
global /** @var response $response */
$response;
$this->requirePermission('set_custom_price');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Set the custom price
$targetUser = (new users_o())->automaticGetTargetUserFromRequest();
if (!$targetUser->exists()) {
// Log the incident
(new logs_o())->add('users', 'global', 1, $user->id, 'SET_CUSTOM_PRICE', 'No user found');
// Return an error
$response->add_meta('user_id', (int)$this->fromRequest('user_id'));
$response->error('User not found (target)', 404);
}
// Check if the required fields are set
$data = json_decode(file_get_contents('php://input'), true);
if (!isset($data['discount'])) {
// Log the incident
(new logs_o())->add('users', 'global', 1, $user->id, 'SET_CUSTOM_PRICE', 'No discount set');
// Return an error
$response->error('No discount set', 400);
}
if (!isset($data['object_id'])) {
// Log the incident
(new logs_o())->add('users', 'global', 1, $user->id, 'SET_CUSTOM_PRICE', 'No object_id set');
// Return an error
$response->error('No object_id set', 400);
}
if (!isset($data['is_category'])) {
// Log the incident
(new logs_o())->add('users', 'global', 1, $user->id, 'SET_CUSTOM_PRICE', 'No is_category set');
// Return an error
$response->error('No is_category set', 400);
}
$discount = (int)$data['discount'];
$object_id = (int)$data['object_id'];
$is_category = (bool)$data['is_category'];
// Set the custom price
$targetUser->setCustomPrice($targetUser->id, $object_id, $discount, $is_category);
// Log the incident
(new logs_o())->add('users', 'global', 1, $user->id, 'SET_CUSTOM_PRICE', 'Successfully set custom price');
// Return a success message
$response->success('Successfully set custom price');
} else {
// Log the incident
(new logs_o())->add('users', 'global', 1, 0, 'SET_CUSTOM_PRICE', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
}
}