Skip to content

Commit

Permalink
implement an initial quantity cap mechanism, per #4
Browse files Browse the repository at this point in the history
  • Loading branch information
bcholmes committed Dec 5, 2021
1 parent ef3d890 commit 8b34761
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 52 deletions.
14 changes: 13 additions & 1 deletion client/src/component/offeringList.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ class OfferingList extends Component {
let price = o.suggestedPrice === undefined || o.suggestedPrice === null ? 'Any' : (o.suggestedPrice === 0 ? 'Free' : ('$' + o.suggestedPrice.toFixed(0)));
let priceSuffix = this.isVariableAmount(o) ? (<small className="text-muted">+/-</small>) : undefined;

if (o.emphasis) {
if (o.remaining != null && o.remaining <= 0) {
return undefined;
} else if (o.emphasis) {
return (
<div className="col" key={'offering-' + o.id}>
<div className="card mb-4 rounded-3 shadow-sm border-primary">
Expand All @@ -75,6 +77,7 @@ class OfferingList extends Component {
<div className="card-body">
<h1 className="card-title #-card-title"><small className="text-muted fw-light"><small>{o.currency}</small></small> {price } {priceSuffix}</h1>
<ul className="list-unstyled mt-3 mb-4">
{this.supplyNotes(o)}
{highlights}
</ul>
<Button size="lg" className="w-100" onClick={() => this.showModal(o) }>Add to cart</Button>
Expand All @@ -92,6 +95,7 @@ class OfferingList extends Component {
<div className="card-body">
<h1 className="card-title #-card-title"><small className="text-muted fw-light"><small>{o.currency}</small></small> {price } {priceSuffix}</h1>
<ul className="list-unstyled mt-3 mb-4">
{this.supplyNotes(o)}
{highlights}
</ul>
<Button size="lg" className="w-100" onClick={() => this.showModal(o) } variant="outline-primary">Add to cart</Button>
Expand Down Expand Up @@ -499,6 +503,14 @@ class OfferingList extends Component {
}
}

supplyNotes(offering) {
if (offering.remaining && offering.remaining < 10) {
return (<li className="text-warning">Not many left</li>)
} else {
return undefined;
}
}

getErrorClass(name) {
return this.isFieldInError(name) ? "is-invalid" : "";
}
Expand Down
40 changes: 39 additions & 1 deletion etc/db_schema.puml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,40 @@ title Online Registration Entity Relationship Diagram
'hide the spot
hide circle

!global $DARK_BLUE = "#094785"
!global $MEDIUM_GREY = "#5E5F60"
!global $DARK_GREY = "#44546A"
!global $AMBER = "#f7dd90"
'avoid problems with angled crows feet
skinparam linetype ortho


<style>
class {
RoundCorner 15
LineColor $DARK_BLUE
FontColor $DARK_BLUE
BackgroundColor #fcf6e4
FontSize 12
AttributeFontColor $DARK_GREY
}

arrow {
LineColor $MEDIUM_GREY
FontColor $MEDIUM_GREY
FontSize 12
}

note {
LineColor $DARK_GREY
FontColor #111111
BackgroundColor $AMBER
FontSize 12
AttributeFontColor $DARK_GREY
}
</style>


entity reg_con_info {
* id : INT <<auto_increment>>
--
Expand Down Expand Up @@ -81,17 +112,24 @@ entity reg_order_item {
age: varchar(255)
}

entity reg_quantity_pool {
* id : INT <<auto_increment>>
--
* quantity : INT
}

entity reg_admin_user {
* id : INT <<auto_increment>>
--
* name : varchar(255)
* password : varchar(255)
}
note bottom: reg_admin_user is really a view on\nZambia's Participant and CongoDump tables

reg_con_info }|-right-|| reg_perennial_con_info : perennial_con_id
reg_order ||-left-o{ reg_order_item : order_id
reg_order_item }o--|| reg_offering : offering_id
reg_offering }|--|| reg_con_info : con_id
reg_offering ||-right-o{ reg_offering_highlight : offering_id

reg_offering }o-left-|| reg_quantity_pool : quantity_pool_id
@enduml
13 changes: 12 additions & 1 deletion etc/db_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -330,4 +330,15 @@ create table reg_program_link (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

alter table reg_offering add column is_donation char(1) not null default 'N';
alter table reg_offering add column short_name varchar(255);
alter table reg_offering add column short_name varchar(255);
alter table reg_offering add column quantity_pool_id int;

create table reg_quantity_pool (
`id` int(11) NOT NULL AUTO_INCREMENT,
`quantity` int(11) NOT NULL,
`notes` varchar(255),
PRIMARY KEY (`id`)
);

insert into reg_quantity_pool (quantity, notes) values (200, 'Dessert tickets');
insert into reg_quantity_pool (quantity, notes) values (1000, 'In-person memberships');
67 changes: 48 additions & 19 deletions server/api/db_common_functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ function mark_order_as_finalized($db, $order_id, $payment_method, $email_address
SET payment_method = ?,
confirmation_email = ?,
status = 'CHECKED_OUT',
finalized_date = now()
finalized_date = now(),
last_modified_date = now()
WHERE id = ?;
EOD;

Expand All @@ -191,7 +192,8 @@ function mark_order_as_paid($db, $order_id) {
$query = <<<EOD
UPDATE reg_order
SET payment_date = now(),
status = 'PAID'
status = 'PAID',
last_modified_date = now()
WHERE id = ?;
EOD;

Expand All @@ -210,7 +212,8 @@ function mark_order_as_paid($db, $order_id) {
function mark_order_as_cancelled($db, $order_id) {
$query = <<<EOD
UPDATE reg_order
SET status = 'CANCELLED'
SET status = 'CANCELLED',
last_modified_date = now()
WHERE id = ?;
EOD;

Expand All @@ -229,7 +232,8 @@ function mark_order_as_cancelled($db, $order_id) {
function mark_order_as_refunded($db, $order_id) {
$query = <<<EOD
UPDATE reg_order
SET status = 'REFUNDED'
SET status = 'REFUNDED',
last_modified_date = now()
WHERE id = ?;
EOD;

Expand All @@ -256,7 +260,9 @@ function boolean_value_from($value) {
}

function create_order_item_with_uuid($db, $conData, $orderId, $values, $offering, $item_uuid) {
$query = <<<EOD
mysqli_begin_transaction($db);
try {
$query = <<<EOD
INSERT
INTO reg_order_item (order_id, for_name, email_address, item_uuid, amount, email_ok, volunteer_ok, snail_mail_ok,
street_line_1, street_line_2, city, state_or_province, country, zip_or_postal_code, age, offering_id)
Expand All @@ -266,26 +272,49 @@ function create_order_item_with_uuid($db, $conData, $orderId, $values, $offering
and o.con_id = ?;
EOD;

$stmt = mysqli_prepare($db, $query);
mysqli_stmt_bind_param($stmt, "isssdssssssssssii", $orderId, $values->for, $values->email, $item_uuid, $values->amount,
boolean_value_from($values->newsletter != null ? $values->newsletter : false),
boolean_value_from($values->volunteer != null ? $values->volunteer : false),
boolean_value_from($values->snailMail != null ? $values->snailMail : false),
$values->streetLine1,
$values->streetLine2,
$values->city,
$values->stateOrProvince,
$values->country,
$values->zipOrPostalCode,
$values->age,
$offering->id, $conData->id);

if ($stmt->execute()) {
mysqli_stmt_close($stmt);
} else {
throw new DatabaseSqlException("Insert could not be processed: $query");
}

update_last_modified_date_on_order($db, $orderId);
mysqli_commit($db);
return true;
} catch (Exception $e) {
mysqli_rollback($db);
throw $e;
}
}

function update_last_modified_date_on_order($db, $orderId) {
$query = <<<EOD
UPDATE reg_order
SET last_modified_date = now()
where id = ?;
EOD;

$stmt = mysqli_prepare($db, $query);
mysqli_stmt_bind_param($stmt, "isssdssssssssssii", $orderId, $values->for, $values->email, $item_uuid, $values->amount,
boolean_value_from($values->newsletter != null ? $values->newsletter : false),
boolean_value_from($values->volunteer != null ? $values->volunteer : false),
boolean_value_from($values->snailMail != null ? $values->snailMail : false),
$values->streetLine1,
$values->streetLine2,
$values->city,
$values->stateOrProvince,
$values->country,
$values->zipOrPostalCode,
$values->age,
$offering->id, $conData->id);
mysqli_stmt_bind_param($stmt, "i", $orderId);

if ($stmt->execute()) {
mysqli_stmt_close($stmt);
return true;
} else {
throw new DatabaseSqlException("Insert could not be processed: $query");
}
}

?>
43 changes: 40 additions & 3 deletions server/api/offering_list.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function find_offerings($conData, $db) {
SELECT
o.id, o.title, o.minimum_price, o.currency, o.suggested_price, o.maximum_price,
o.description, o.is_membership, o.add_prompts, o.emphasis, o.email_required, h.highlight_text, o.address_required,
o.age_required, o.is_donation
o.age_required, o.is_donation, o.quantity_pool_id
FROM
reg_offering o
LEFT OUTER JOIN reg_offering_highlight h
Expand All @@ -41,13 +41,13 @@ function find_offerings($conData, $db) {
o.sort_order, o.id, h.sort_order;
EOD;

$items = array();
$stmt = mysqli_prepare($db, $query);
mysqli_stmt_bind_param($stmt, "i", $conData->id);
mysqli_set_charset($db, "utf8");
if (mysqli_stmt_execute($stmt)) {
$result = mysqli_stmt_get_result($stmt);

$items = array();
$item = null;
$highlight_list = null;
while ($row = mysqli_fetch_object($result)) {
Expand All @@ -70,6 +70,7 @@ function find_offerings($conData, $db) {
"addressRequired" => ("Y" == $row->address_required ? true : false),
"ageRequired" => ("Y" == $row->age_required ? true : false),
"isDonation" => ("Y" == $row->is_donation ? true : false),
"quantityPoolId" => $row->quantity_pool_id,
);
}
if ($row->highlight_text) {
Expand All @@ -82,8 +83,44 @@ function find_offerings($conData, $db) {
array_push($items, $item);
}
mysqli_stmt_close($stmt);
return $items;
} else {
throw new DatabaseSqlException("Could not execute query: $query");
}

$query = <<<EOD
SELECT count(i.id) as current_count, p.quantity, p.id as pool_id
FROM reg_order_item i, reg_order ord, reg_offering of, reg_quantity_pool p
WHERE i.offering_id = of.id
AND i.order_id = ord.id
AND (ord.status in ('PAID', 'CHECKED_OUT') or (ord.status = 'IN_PROGRESS' and timestampdiff(SECOND, ord.last_modified_date, now()) < 600))
AND of.quantity_pool_id = p.id
AND ord.con_id = ?
GROUP BY p.id, p.quantity;
EOD;

$stmt = mysqli_prepare($db, $query);
mysqli_stmt_bind_param($stmt, "i", $conData->id);
mysqli_set_charset($db, "utf8");
$quantities = array();
if (mysqli_stmt_execute($stmt)) {
$result = mysqli_stmt_get_result($stmt);

while ($row = mysqli_fetch_object($result)) {
$quantities[$row->pool_id] = $row->quantity - $row->current_count;
}
mysqli_stmt_close($stmt);
} else {
throw new DatabaseSqlException("Could not execute query: $query");
}

foreach ($items as &$item) {
if ($item['quantityPoolId'] && array_key_exists($item['quantityPoolId'], $quantities)) {
$item['remaining'] = $quantities[$item['quantityPoolId']];
}
}
unset($item);

return $items;
}

$db = connect_to_db($ini);
Expand Down
31 changes: 19 additions & 12 deletions server/api/remove_item.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,27 @@
require_once("db_common_functions.php");

function delete_order_item($db, $order, $order_item_uuid) {
$query = <<<EOD
DELETE from reg_order_item
WHERE item_uuid = ?
AND order_id = ?;
EOD;
mysqli_begin_transaction($db);
try {
$query = <<<EOD
DELETE from reg_order_item
WHERE item_uuid = ?
AND order_id = ?;
EOD;

$stmt = mysqli_prepare($db, $query);
mysqli_stmt_bind_param($stmt, "si", $order_item_uuid, $order->id);
$stmt = mysqli_prepare($db, $query);
mysqli_stmt_bind_param($stmt, "si", $order_item_uuid, $order->id);

if ($stmt->execute()) {
mysqli_stmt_close($stmt);
return true;
} else {
return false;
if ($stmt->execute()) {
mysqli_stmt_close($stmt);
} else {
throw new DatabaseSqlException("Delete could not be processed: $query");
}

update_last_modified_date_on_order($db, $order->id);
mysqli_commit($db);
} catch (Exception $e) {
mysqli_rollback($db);
}
}

Expand Down
Loading

0 comments on commit 8b34761

Please # to comment.