From bf4cc3e41544174f43b4fffd24e5d63e36c4ecd8 Mon Sep 17 00:00:00 2001
From: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Date: Thu, 9 Oct 2025 15:16:15 -0400
Subject: [PATCH] Cargo requests for other departments may now request they
come out of the department budget. (#93152)
## About The Pull Request
When placing a cargo request as a non-cargo staff, you may now specify
which department you are requesting this for, which if approved will
bill that department from their budget. Requests placed this way will
need to be approved by cargo staff by default.
Originally I was going to tie this to my proposed rolling permission
system (Think an email chain where if things don't get approved, it goes
up a PDA Message chain until someone approves the thing), however I've
quite stalled on that and I wanted to at least get the baseline
functionality into people's hands.
Cargo, as per usual, has a right to refusal to reject the request as
well.
_WIP Photos, names/departments only show here as undefined due to being
admin spawned and not naturally spawned players_
## Why It's Good For The Game
Heads of staff can already order crates using their departmental
ordering apps and utilizing their funds, without needing QM approval.
This will allow for crewmembers within a department to request to
requisition supplies using their budget's funds as well.
As it stands, ordering from budget is already quite clunky, and being
able to go to cargo to place a request for your department should at
least elicit a conversation between heads of staff to see if it makes
sense. Keeping in mind, spending too much of a budget's funds can result
in failure to complete paychecks, so there is some conflict that can
arise from interacting with this mechanic too aggressively.
**I'll be real, there's a pretty good chance this may be too easy to use
and too powerful in it's current state.** If it needs something like the
additional rolling permissions system in place in the future, we may
want to re-assess at a later point.
## Changelog
:cl:
qol: When placing cargo requests, you can now request that a crate be
obtained using your department's funds as opposed to the cargo budget.
/:cl:
---------
Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
---
code/modules/cargo/orderconsole.dm | 41 +++++++++-----
.../file_system/programs/budgetordering.dm | 56 +++++++++++++------
.../programs/messenger/messenger_program.dm | 1 +
.../tgui/interfaces/Cargo/CargoRequests.tsx | 4 ++
tgui/packages/tgui/interfaces/Cargo/types.ts | 1 +
5 files changed, 73 insertions(+), 30 deletions(-)
diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm
index d5984da9ec9..06465c48d8e 100644
--- a/code/modules/cargo/orderconsole.dm
+++ b/code/modules/cargo/orderconsole.dm
@@ -7,7 +7,7 @@
///Can the supply console send the shuttle back and forth? Used in the UI backend.
var/can_send = TRUE
- ///Can this console only send requests?
+ ///Can this console only send requests? Typically used at the cargo front desk for pedestrians.
var/requestonly = FALSE
///Can you approve requests placed for cargo? Works differently between the app and the computer.
var/can_approve_requests = TRUE
@@ -143,6 +143,7 @@
"orderer" = order.orderer,
"reason" = order.reason,
"id" = order.id,
+ "account" = order.paying_account ? order.paying_account.account_holder : "Cargo Department"
))
data["amount_by_name"] = amount_by_name
@@ -238,33 +239,46 @@
rank = "Silicon"
var/datum/bank_account/account
- if(self_paid && isliving(user))
+ if(isliving(user))
var/mob/living/living_user = user
var/obj/item/card/id/id_card = living_user.get_idcard(TRUE)
- if(!istype(id_card))
+ if(!istype(id_card) && self_paid)
say("No ID card detected.")
return
- if(IS_DEPARTMENTAL_CARD(id_card))
+ if(IS_DEPARTMENTAL_CARD(id_card) && self_paid)
say("The [src] rejects [id_card].")
return
- account = id_card.registered_account
- if(!istype(account))
- say("Invalid bank account.")
- return
- var/list/access = id_card.GetAccess()
- if(pack.access_view && !(pack.access_view in access))
- say("[id_card] lacks the requisite access for this purchase.")
- return
+ account = id_card?.registered_account // We can still assign an account for request department purposes.
+ if(self_paid)
+ if(!istype(account))
+ say("Invalid bank account.")
+ return
+ var/list/access = id_card.GetAccess()
+ if(pack.access_view && !(pack.access_view in access))
+ say("[id_card] lacks the requisite access for this purchase.")
+ return
// The list we are operating on right now
var/list/working_list = SSshuttle.shopping_list
var/reason = ""
- if(requestonly && !self_paid)
+ if(requestonly && !self_paid && !pack.goody)
working_list = SSshuttle.request_list
reason = tgui_input_text(user, "Reason", name, max_length = MAX_MESSAGE_LEN)
if(isnull(reason))
return
+ name = account?.account_holder
+ if(account?.account_job)
+ var/datum/bank_account/personal_department = SSeconomy.get_dep_account(account.account_job.paycheck_department)
+ if(!(personal_department.account_holder == "Cargo Budget"))
+ var/dept_choice = tgui_alert(usr, "Which department are you requesting this for?", "Choose department to request from", list("Cargo Budget", "[personal_department.account_holder]"))
+ if(!dept_choice)
+ return
+ if(dept_choice != "Cargo Budget")
+ account = personal_department
+ else
+ account = SSeconomy.get_dep_account(cargo_account)
+
if(pack.goody && !self_paid)
playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
say("ERROR: Small crates may only be purchased by private accounts.")
@@ -294,6 +308,7 @@
reason = reason,
paying_account = account,
coupon = applied_coupon,
+ department_destination = reason ? TRUE : FALSE, // Hijacking reason as a way to determine if an order's requested from at least one budget
)
working_list += order
diff --git a/code/modules/modular_computers/file_system/programs/budgetordering.dm b/code/modules/modular_computers/file_system/programs/budgetordering.dm
index 38bfb364744..27fd838af30 100644
--- a/code/modules/modular_computers/file_system/programs/budgetordering.dm
+++ b/code/modules/modular_computers/file_system/programs/budgetordering.dm
@@ -1,9 +1,9 @@
/datum/computer_file/program/budgetorders
filename = "orderapp"
- filedesc = "NT IRN"
+ filedesc = "NT Shopping Network"
downloader_category = PROGRAM_CATEGORY_SUPPLY
program_open_overlay = "request"
- extended_desc = "Nanotrasen Internal Requisition Network interface for supply purchasing using a department budget account."
+ extended_desc = "Nanotrasen Shopping Network interface for purchasing supplies from the cargo catalogue using a department budget account."
program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
can_run_on_flags = PROGRAM_LAPTOP | PROGRAM_PDA
size = 10
@@ -62,23 +62,27 @@
var/list/data = list()
data["location"] = SSshuttle.supply.getStatusText()
data["department"] = "Cargo"
+
var/datum/bank_account/buyer = SSeconomy.get_dep_account(cargo_account)
var/obj/item/card/id/id_card = computer.stored_id?.GetID()
if(id_card?.registered_account)
+ buyer = SSeconomy.get_dep_account(id_card?.registered_account.account_job.paycheck_department)
if((ACCESS_COMMAND in id_card.access))
requestonly = FALSE
- buyer = SSeconomy.get_dep_account(id_card.registered_account.account_job.paycheck_department)
can_approve_requests = TRUE
+ // If buyer is a departmental budget, replaces "Cargo" with that budget - we're not using the cargo budget here
+ data["department"] = "[buyer.account_holder] Requisitions"
else
requestonly = TRUE
can_approve_requests = FALSE
- if(ACCESS_COMMAND in id_card.access)
- // If buyer is a departmental budget, replaces "Cargo" with that budget - we're not using the cargo budget here
- data["department"] = addtext(buyer.account_holder, " Requisitions")
else
requestonly = TRUE
if(buyer)
data["points"] = buyer.account_balance
+ // To recap above because it's kind of a mess, here's all the options:
+ //Head of staff ID card: Can approve, buy, and make purchases using their own departmental budgets.
+ //ID card, not a head of staff: can request items from cargo using departmental budget.
+ //No ID card, can request items from cargo using the cargo budget.
//Otherwise static data, that is being applied in ui_data as the crates visible and buyable are not static, and are determined by inserted ID.
data["requestonly"] = requestonly
@@ -160,7 +164,8 @@
"cost" = pack.get_cost(),
"orderer" = order.orderer,
"reason" = order.reason,
- "id" = order.id
+ "id" = order.id,
+ "account" = order.paying_account?.account_holder || "Cargo Department"
))
data["amount_by_name"] = amount_by_name
@@ -219,25 +224,31 @@
var/name = "*None Provided*"
var/rank = "*None Provided*"
var/ckey = usr.ckey
+ var/mob/living/carbon/human/hwoman
if(ishuman(usr))
- var/mob/living/carbon/human/H = usr
- name = H.get_authentification_name()
- rank = H.get_assignment(hand_first = TRUE)
+ hwoman = usr
+ rank = hwoman.get_assignment(hand_first = TRUE)
else if(issilicon(usr))
name = usr.real_name
rank = "Silicon"
- var/datum/bank_account/account
+ // Our account that we want to end up paying with. Defaults to the cargo budget!
+ var/datum/bank_account/account = SSeconomy.get_dep_account(ACCOUNT_CAR)
+ // Our ID card that we want to pull from for identification. Modifies either name, account, or neither depending on function.
+ var/obj/item/card/id/id_card_customer = computer.stored_id?.GetID()
+ if(!id_card_customer)
+ id_card_customer = hwoman?.get_idcard(TRUE) //Grab from hands/mob if there's no id_card slot to prioritize.
+ name = id_card_customer?.registered_account.account_holder
+
if(self_paid)
- var/mob/living/carbon/human/H = usr
- var/obj/item/card/id/id_card = H.get_idcard(TRUE)
- if(!istype(id_card))
+ if(!istype(id_card_customer))
computer.say("No ID card detected.")
return
- if(IS_DEPARTMENTAL_CARD(id_card))
- computer.say("[id_card] cannot be used to make purchases.")
+ if(IS_DEPARTMENTAL_CARD(id_card_customer))
+ computer.say("[id_card_customer] cannot be used to make purchases.")
return
- account = id_card.registered_account
+ account = id_card_customer.registered_account
+ name = id_card_customer.registered_account.account_holder
if(!istype(account))
computer.say("Invalid bank account.")
return
@@ -248,6 +259,16 @@
if(isnull(reason) || ..())
return
+ if(id_card_customer?.registered_account?.account_job) //Find a budget to pull from
+ var/datum/bank_account/personal_department = SSeconomy.get_dep_account(id_card_customer.registered_account.account_job.paycheck_department)
+ if(!(personal_department.account_holder == "Cargo Budget"))
+ var/choice = tgui_alert(usr, "Which department are you requesting this for?", "Choose request department", list("Cargo Budget", "[personal_department.account_holder]"))
+ if(!choice)
+ return
+ if(choice != "Cargo Budget")
+ account = personal_department
+ name = id_card_customer.registered_account?.account_holder
+
if(pack.goody && !self_paid)
playsound(computer, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
computer.say("ERROR: Small crates may only be purchased by private accounts.")
@@ -271,6 +292,7 @@
SSshuttle.shopping_list += SO
if(self_paid)
computer.say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.")
+ playsound(computer, 'sound/effects/coin2.ogg', 40, TRUE)
. = TRUE
if("remove")
var/id = text2num(params["id"])
diff --git a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
index 20326141922..d4dbe9b2ff6 100644
--- a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
+++ b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
@@ -723,6 +723,7 @@
SStgui.update_uis(computer)
update_pictures_for_all()
+
/// topic call that answers to people pressing "(Reply)" in chat
/datum/computer_file/program/messenger/Topic(href, href_list)
..()
diff --git a/tgui/packages/tgui/interfaces/Cargo/CargoRequests.tsx b/tgui/packages/tgui/interfaces/Cargo/CargoRequests.tsx
index 42af4c46494..ddfc78a9205 100644
--- a/tgui/packages/tgui/interfaces/Cargo/CargoRequests.tsx
+++ b/tgui/packages/tgui/interfaces/Cargo/CargoRequests.tsx
@@ -19,6 +19,7 @@ export function CargoRequests(props) {