diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 67246dc1953..25e3855bf84 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -25017,10 +25017,9 @@
/turf/open/floor/plating,
/area/maintenance/starboard/aft)
"fcB" = (
-/obj/structure/chair/office{
- dir = 1
+/obj/machinery/computer/accounting{
+ dir = 4
},
-/obj/machinery/newscaster/directional/west,
/turf/open/floor/wood,
/area/command/heads_quarters/hop)
"fcG" = (
diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
index c89f063321c..6bdbedd16fe 100644
--- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm
+++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
@@ -25602,6 +25602,9 @@
/obj/effect/turf_decal/tile/blue{
dir = 8
},
+/obj/machinery/computer/accounting{
+ dir = 4
+ },
/turf/open/floor/iron,
/area/command/heads_quarters/hop)
"lwk" = (
diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm
index 73ad0388c5e..8d15bb24c47 100644
--- a/_maps/map_files/KiloStation/KiloStation.dmm
+++ b/_maps/map_files/KiloStation/KiloStation.dmm
@@ -31744,16 +31744,11 @@
/turf/open/floor/iron/showroomfloor,
/area/medical/medbay/central)
"goc" = (
-/obj/structure/table/wood,
-/obj/machinery/recharger,
-/obj/item/toy/figure/hop{
- pixel_x = -8
- },
-/obj/item/toy/figure/ian{
- pixel_x = 8
- },
/obj/machinery/newscaster/directional/west,
/obj/machinery/airalarm/directional/north,
+/obj/machinery/computer/accounting{
+ dir = 4
+ },
/turf/open/floor/carpet/blue,
/area/command/heads_quarters/hop)
"goy" = (
@@ -59648,11 +59643,15 @@
/turf/open/floor/cult,
/area/service/chapel/office)
"puG" = (
-/obj/structure/table/wood,
-/obj/item/stack/package_wrap,
-/obj/item/storage/lockbox/loyalty,
-/obj/item/hand_labeler,
/obj/machinery/keycard_auth/directional/north,
+/obj/item/toy/figure/ian{
+ pixel_x = 8
+ },
+/obj/item/toy/figure/hop{
+ pixel_x = -8
+ },
+/obj/machinery/recharger,
+/obj/structure/table/wood,
/turf/open/floor/carpet/blue,
/area/command/heads_quarters/hop)
"puH" = (
@@ -78817,6 +78816,7 @@
},
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/bot,
+/obj/item/storage/lockbox/loyalty,
/turf/open/floor/iron/dark,
/area/command/heads_quarters/hop)
"vME" = (
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index d52d2372cdb..d572b7f728e 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -456,16 +456,12 @@
/turf/closed/wall/r_wall,
/area/security/prison/safe)
"aeN" = (
-/obj/item/flashlight/lamp/green{
- pixel_x = 1;
- pixel_y = 5
- },
-/obj/structure/table/wood,
/obj/machinery/button/door/directional/north{
id = "hop";
name = "Privacy Shutters Control";
req_access_txt = "28"
},
+/obj/machinery/computer/accounting,
/turf/open/floor/wood,
/area/command/heads_quarters/hop)
"aeO" = (
@@ -17503,9 +17499,16 @@
/turf/open/floor/iron,
/area/hallway/primary/central)
"fdu" = (
-/obj/machinery/recharger,
+/obj/machinery/recharger{
+ pixel_x = 2;
+ pixel_y = 3
+ },
/obj/item/storage/secure/safe/directional/east,
/obj/structure/table/wood,
+/obj/item/flashlight/lamp/green{
+ pixel_x = -12;
+ pixel_y = 5
+ },
/turf/open/floor/wood,
/area/command/heads_quarters/hop)
"fdB" = (
diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm
index 015f40474be..b462b0aa5a2 100644
--- a/_maps/map_files/tramstation/tramstation.dmm
+++ b/_maps/map_files/tramstation/tramstation.dmm
@@ -33190,8 +33190,13 @@
/turf/open/floor/iron,
/area/cargo/drone_bay)
"kBI" = (
-/obj/item/kirbyplants/random,
/obj/machinery/status_display/evac/directional/north,
+/obj/structure/table/wood,
+/obj/item/paper_bin{
+ pixel_x = -2;
+ pixel_y = 4
+ },
+/obj/item/pen,
/turf/open/floor/wood,
/area/command/heads_quarters/hop)
"kBN" = (
@@ -33679,12 +33684,9 @@
/obj/structure/window/reinforced{
dir = 4
},
-/obj/structure/table/wood,
-/obj/item/paper_bin{
- pixel_x = -2;
- pixel_y = 4
+/obj/machinery/computer/accounting{
+ dir = 8
},
-/obj/item/pen,
/turf/open/floor/carpet,
/area/command/heads_quarters/hop)
"kJx" = (
@@ -37095,8 +37097,8 @@
dir = 4
},
/obj/structure/table/wood,
-/obj/machinery/recharger,
/obj/machinery/newscaster/directional/north,
+/obj/machinery/recharger,
/turf/open/floor/carpet,
/area/command/heads_quarters/hop)
"lQz" = (
@@ -53906,6 +53908,7 @@
},
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
+/obj/item/kirbyplants/random,
/turf/open/floor/iron,
/area/hallway/secondary/command)
"sco" = (
diff --git a/code/controllers/subsystem/economy.dm b/code/controllers/subsystem/economy.dm
index 8ecfc723c4f..03ec542506e 100644
--- a/code/controllers/subsystem/economy.dm
+++ b/code/controllers/subsystem/economy.dm
@@ -49,6 +49,11 @@ SUBSYSTEM_DEF(economy)
///The modifier multiplied to the value of cargo pack prices.
var/pack_price_modifier = 1
var/fire_counter_for_paycheck = 0 //SKYRAT EDIT ADDITION
+ /**
+ * A list of strings containing a basic transaction history of purchases on the station.
+ * Added to any time when player accounts purchase something.
+ */
+ var/list/audit_log = list()
/// Total value of exported materials.
var/export_total = 0
@@ -148,3 +153,20 @@ SUBSYSTEM_DEF(economy)
return 1
inflation_value = max(round(((station_total / bank_accounts_by_id.len) / station_target), 0.1), 1.0)
return inflation_value
+
+/**
+ * Proc that adds a set of strings and ints to the audit log, tracked by the economy SS.
+ *
+ * * account: The bank account of the person purchasing the item.
+ * * price_to_use: The cost of the purchase made for this transaction.
+ * * vendor: The object or structure medium that is charging the user. For Vending machines that's the machine, for payment component that's the parent, cargo that's the crate, etc.
+ */
+/datum/controller/subsystem/economy/proc/track_purchase(datum/bank_account/account, price_to_use, vendor)
+ if(!account || !price_to_use || !vendor)
+ CRASH("Track purchases was missing an argument! (Account, Price, or Vendor.)")
+
+ audit_log += list(list(
+ "account" = account.account_holder,
+ "cost" = price_to_use,
+ "vendor" = vendor,
+ ))
diff --git a/code/datums/components/payment.dm b/code/datums/components/payment.dm
index 49375c63079..c5c9c5b8542 100644
--- a/code/datums/components/payment.dm
+++ b/code/datums/components/payment.dm
@@ -69,10 +69,10 @@
target_acc.transfer_money(card.registered_account, cost + extra_fees)
card.registered_account.bank_card_talk("[cost + extra_fees] credits deducted from your account.")
playsound(src, 'sound/effects/cashregister.ogg', 20, TRUE)
+ SSeconomy.track_purchase(card.registered_account, cost + extra_fees, parent)
/datum/component/payment/proc/change_cost(datum/source, new_cost)
SIGNAL_HANDLER
-
if(!isnum(new_cost))
CRASH("change_cost called with variable new_cost as not a number.")
cost = new_cost
diff --git a/code/game/machinery/computer/accounting.dm b/code/game/machinery/computer/accounting.dm
new file mode 100644
index 00000000000..64a26655540
--- /dev/null
+++ b/code/game/machinery/computer/accounting.dm
@@ -0,0 +1,34 @@
+/obj/machinery/computer/accounting
+ name = "account lookup console"
+ desc = "Used to view crewmember accounts and purchases."
+ icon_screen = "accounts"
+ icon_keyboard = "id_key"
+ circuit = /obj/item/circuitboard/computer/accounting
+ light_color = LIGHT_COLOR_GREEN
+
+/obj/machinery/computer/accounting/ui_interact(mob/user, datum/tgui/ui)
+ . = ..()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if (!ui)
+ ui = new(user, src, "AccountingConsole", name)
+ ui.open()
+
+/obj/machinery/computer/accounting/ui_data(mob/user)
+ . = ..()
+ var/list/data = list()
+ var/list/player_accounts = list()
+ var/list/audit_list = SSeconomy.audit_log
+
+ for(var/current_account as anything in SSeconomy.bank_accounts_by_id)
+ var/datum/bank_account/current_bank_account = SSeconomy.bank_accounts_by_id[current_account]
+ player_accounts += list(list(
+ "name" = current_bank_account.account_holder,
+ "job" = current_bank_account.account_job.title,
+ "balance" = current_bank_account.account_balance,
+ "modifier" = current_bank_account.payday_modifier,
+ ))
+ data["PlayerAccounts"] = player_accounts
+ data["AuditLog"] = audit_list
+ data["Crashing"] = HAS_TRAIT(SSeconomy, TRAIT_MARKET_CRASHING)
+ return data
+
diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm
index dd22f011c53..cc3b1fb8e27 100644
--- a/code/game/objects/items/circuitboards/computer_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm
@@ -14,6 +14,11 @@
name = "Bluespace Artillery Controls (Computer Board)"
build_path = /obj/machinery/computer/bsa_control
+/obj/item/circuitboard/computer/accounting
+ name = "Account Lookup Console (Computer Board)"
+ greyscale_colors = CIRCUIT_COLOR_COMMAND
+ build_path = /obj/machinery/computer/accounting
+
//obj/item/circuitboard/computer/shield
// name = "Shield Control (Computer Board)"
// greyscale_colors = CIRCUIT_COLOR_COMMAND
diff --git a/code/modules/research/designs/comp_board_designs.dm b/code/modules/research/designs/comp_board_designs.dm
index 259655ae867..f48584a8ff7 100644
--- a/code/modules/research/designs/comp_board_designs.dm
+++ b/code/modules/research/designs/comp_board_designs.dm
@@ -306,3 +306,12 @@
build_type = IMPRINTER
build_path = /obj/item/circuitboard/computer/exodrone_console
category = list("Computer Boards")
+
+/datum/design/board/accounting_console
+ name = "Computer Design (Account Lookup Console)"
+ desc = "Allows for the construction of circuit boards used to assess the wealth of crewmates on station."
+ id = "account_console"
+ build_type = IMPRINTER
+ build_path = /obj/item/circuitboard/computer/accounting
+ category = list("Computer Boards")
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index de82d739807..956f57ea211 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -1116,6 +1116,7 @@
description = "Organized record databases and how they're used."
prereq_ids = list("comptech")
design_ids = list(
+ "account_console",
"automated_announcement",
"med_data",
"prisonmanage",
diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm
index edf535d8d28..c9bf792865d 100644
--- a/code/modules/shuttle/supply.dm
+++ b/code/modules/shuttle/supply.dm
@@ -158,6 +158,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
if(istype(spawning_order, /datum/supply_order/armament))
LAZYADD(forced_briefcases[spawning_order.paying_account], spawning_order)
paying_for_this.bank_card_talk("Cargo order #[spawning_order.id] has shipped. [price] credits have been charged to your bank account.")
+ SSeconomy.track_purchase(paying_for_this, price, spawning_order.pack.name)
var/datum/bank_account/department/cargo = SSeconomy.get_dep_account(ACCOUNT_CAR)
cargo.adjust_money(price - spawning_order.pack.get_cost()) //Cargo gets the handling fee
value += spawning_order.pack.get_cost()
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index b1ccc4634e5..88e625c2725 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -962,7 +962,8 @@ GLOBAL_LIST_EMPTY(vending_products)
if(D)
D.adjust_money(price_to_use)
SSblackbox.record_feedback("amount", "vending_spent", price_to_use)
- log_econ("[price_to_use] credits were inserted into [src] by [D.account_holder] to buy [R].")
+ SSeconomy.track_purchase(account, price_to_use, name)
+ log_econ("[price_to_use] credits were inserted into [src] by [account.account_holder] to buy [R].")
if(last_shopper != REF(usr) || purchase_message_cooldown < world.time)
say("Thank you for shopping with [src]!")
purchase_message_cooldown = world.time + 5 SECONDS
diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi
index dfa3057b2e2..fb281724f0b 100644
Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index b9a67d01703..35e873fc0f8 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1319,6 +1319,7 @@
#include "code\game\machinery\camera\presets.dm"
#include "code\game\machinery\camera\tracking.dm"
#include "code\game\machinery\computer\_computer.dm"
+#include "code\game\machinery\computer\accounting.dm"
#include "code\game\machinery\computer\aifixer.dm"
#include "code\game\machinery\computer\apc_control.dm"
#include "code\game\machinery\computer\arena.dm"
diff --git a/tgui/global.d.ts b/tgui/global.d.ts
index 348cb9c5165..73660d5b76e 100644
--- a/tgui/global.d.ts
+++ b/tgui/global.d.ts
@@ -21,6 +21,13 @@ declare module '*.svg' {
export default content;
}
+namespace JSX {
+ interface IntrinsicElements {
+ marquee: any;
+ blink: any;
+ }
+}
+
type TguiMessage = {
type: string;
payload?: any;
diff --git a/tgui/packages/tgui/interfaces/AccountingConsole.js b/tgui/packages/tgui/interfaces/AccountingConsole.js
new file mode 100644
index 00000000000..41c2fa1c35b
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AccountingConsole.js
@@ -0,0 +1,93 @@
+import { BlockQuote, Collapsible, LabeledList, Modal, Section, Tabs } from '../components';
+import { useBackend } from '../backend';
+import { useLocalState } from '../backend';
+import { Window } from '../layouts';
+
+export const AccountingConsole = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ PlayerAccounts,
+ AuditLog,
+ } = data;
+ const USER_SCREEN = 1;
+ const AUDIT_SCREEN = 2;
+ const [screenmode, setScreenmode] = useLocalState(context, 'tab_main', USER_SCREEN);
+ return (
+
+
+
+
+ setScreenmode(USER_SCREEN)}>
+ Users
+
+ setScreenmode(AUDIT_SCREEN)}>
+ Audit
+
+
+ {screenmode === USER_SCREEN && (
+
+ {PlayerAccounts.map(account => (
+
+
+
+ {account.job}
+
+
+ {account.balance}
+
+
+ {account.modifier*100}%
+
+
+
+ ))}
+
+ )}
+ {screenmode === AUDIT_SCREEN && (
+
+ {AuditLog.map(purchase => (
+
+ {purchase.account} spent {purchase.cost}
+ {' '}cr at {purchase.vendor}.
+
+ ))}
+
+ )}
+
+
+ );
+};
+
+/** The modal menu that contains the prompts to making new channels. */
+const MarketCrashing = (props, context) => {
+ const { act, data } = useBackend(context);
+ const [lockedmode, setLockedmode] = useLocalState(context, 'lockedmode', 1);
+ const {
+ Crashing,
+ } = data;
+ if (!Crashing) {
+ return null;
+ }
+ return (
+
+
+
+ );
+};
+
+
diff --git a/tgui/public/tgui.html b/tgui/public/tgui.html
index 9a53f328ab6..ecb3e11b9cc 100644
--- a/tgui/public/tgui.html
+++ b/tgui/public/tgui.html
@@ -475,6 +475,17 @@ Byond.sendMessage('ready');