diff --git a/installer/data.sql b/installer/data.sql index ed3fa723..0d59dbba 100644 --- a/installer/data.sql +++ b/installer/data.sql @@ -1,8 +1,9 @@ --- MariaDB dump 10.19 Distrib 10.5.19-MariaDB, for debian-linux-gnu (x86_64) +/*M!999999\- enable the sandbox mode */ +-- MariaDB dump 10.19 Distrib 10.5.29-MariaDB, for debian-linux-gnu (x86_64) -- -- Host: libki-mariadb Database: libki -- ------------------------------------------------------ --- Server version 10.3.5-MariaDB-10.3.5+maria~jessie +-- Server version 12.1.2-MariaDB-ubu2404 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -167,8 +168,8 @@ INSERT INTO `settings` VALUES ('','DefaultGuestSessionTimeAllowance','45'); INSERT INTO `settings` VALUES ('','DefaultGuestTimeAllowance','45'); INSERT INTO `settings` VALUES ('','DefaultSessionTimeAllowance','45'); INSERT INTO `settings` VALUES ('','DefaultTimeAllowance','45'); -INSERT INTO `settings` VALUES ('','DisplayReservationStatusWithin','60'); INSERT INTO `settings` VALUES ('','DisplayPublicReservationsPage','enabled'); +INSERT INTO `settings` VALUES ('','DisplayReservationStatusWithin','60'); INSERT INTO `settings` VALUES ('','GuestBatchCount','40'); INSERT INTO `settings` VALUES ('','MinimumReservationMinutes','5'); INSERT INTO `settings` VALUES ('','PostCrashTimeout','5'); @@ -177,9 +178,8 @@ INSERT INTO `settings` VALUES ('','ReservationShowUsername','RSD'); INSERT INTO `settings` VALUES ('','ReservationTimeout','15'); INSERT INTO `settings` VALUES ('','ShowFirstLastNames','1'); INSERT INTO `settings` VALUES ('','ThirdPartyURL',''); -INSERT INTO `settings` VALUES ('','TimeDisplayFormat','24'); INSERT INTO `settings` VALUES ('','UserCategories',''); -INSERT INTO `settings` VALUES ('','Version','5.2.1'); +INSERT INTO `settings` VALUES ('','Version','5.2.x'); /*!40000 ALTER TABLE `settings` ENABLE KEYS */; UNLOCK TABLES; @@ -192,6 +192,15 @@ LOCK TABLES `statistics` WRITE; /*!40000 ALTER TABLE `statistics` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Dumping data for table `transactions` +-- + +LOCK TABLES `transactions` WRITE; +/*!40000 ALTER TABLE `transactions` DISABLE KEYS */; +/*!40000 ALTER TABLE `transactions` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Dumping data for table `user_roles` -- @@ -218,4 +227,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2023-07-17 7:52:06 +-- Dump completed on 2026-02-11 10:59:54 diff --git a/installer/schema.sql b/installer/schema.sql index 01c0ce3a..bdbd15c8 100644 --- a/installer/schema.sql +++ b/installer/schema.sql @@ -1,8 +1,9 @@ --- MariaDB dump 10.19 Distrib 10.11.13-MariaDB, for debian-linux-gnu (x86_64) +/*M!999999\- enable the sandbox mode */ +-- MariaDB dump 10.19 Distrib 10.5.29-MariaDB, for debian-linux-gnu (x86_64) -- --- Host: localhost Database: libki +-- Host: libki-mariadb Database: libki -- ------------------------------------------------------ --- Server version 11.8.5-MariaDB-ubu2404 +-- Server version 12.1.2-MariaDB-ubu2404 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -374,7 +375,7 @@ CREATE TABLE `transactions` ( KEY `idx_instance_status` (`instance`,`status`), KEY `idx_created_on` (`created_on`), CONSTRAINT `user_transactions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -418,6 +419,7 @@ CREATE TABLE `users` ( `lastname` varchar(191) DEFAULT '', `category` varchar(191) DEFAULT '', `creation_source` varchar(191) DEFAULT NULL, + `gratis_print_balance` int(11) NOT NULL DEFAULT 0 COMMENT 'Number of free prints remaining; represents pages or cents depending on system settings', PRIMARY KEY (`id`), UNIQUE KEY `unique_username` (`instance`,`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; @@ -432,4 +434,4 @@ CREATE TABLE `users` ( /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2025-12-19 13:32:36 +-- Dump completed on 2026-02-11 10:59:54 diff --git a/installer/versions/5/2.x_gratis_print_balance.sql b/installer/versions/5/2.x_gratis_print_balance.sql new file mode 100644 index 00000000..223a09e0 --- /dev/null +++ b/installer/versions/5/2.x_gratis_print_balance.sql @@ -0,0 +1,3 @@ +ALTER TABLE `users` + ADD COLUMN `gratis_print_balance` INT(11) NOT NULL DEFAULT 0 + COMMENT 'Number of free prints remaining; represents pages or cents depending on system settings'; diff --git a/lib/Libki/Controller/API/PrintStation/v1_0.pm b/lib/Libki/Controller/API/PrintStation/v1_0.pm index e0eabc39..dcaa7df8 100755 --- a/lib/Libki/Controller/API/PrintStation/v1_0.pm +++ b/lib/Libki/Controller/API/PrintStation/v1_0.pm @@ -134,20 +134,22 @@ sub print_jobs : Path('print_jobs') : Args(0) { my $print_jobs = []; while ( my $j = $jobs->next ) { - my $cost = Libki::Utils::Printing::calculate_job_cost( $c, { print_job => $j } ); + my ( $cost, $gratis_discount ) = Libki::Utils::Printing::calculate_job_cost( $c, { print_job => $j } ); push( @$print_jobs, { - print_job_id => $j->id, - printer => $j->printer, - copies => $j->copies, - created_on => $c->format_dt( { dt => $j->created_on, include_time => 1 } ), - print_file_id => $j->print_file_id, - pages => $j->print_file->pages, - cost => $cost, + print_job_id => $j->id, + printer => $j->printer, + copies => $j->copies, + created_on => $c->format_dt( { dt => $j->created_on, include_time => 1 } ), + print_file_id => $j->print_file_id, + pages => $j->print_file->pages, + cost => $cost, + gratis_discount => $gratis_discount, } ); + } my $data = { diff --git a/lib/Libki/Controller/Administration.pm b/lib/Libki/Controller/Administration.pm index bff52b1c..198142f5 100644 --- a/lib/Libki/Controller/Administration.pm +++ b/lib/Libki/Controller/Administration.pm @@ -51,6 +51,7 @@ sub index : Path : Args(0) { PrinterConfiguration => $c->setting('PrinterConfiguration'), ShowFirstLastNames => $c->setting('ShowFirstLastNames'), UserCategories => $c->setting('UserCategories'), + GratisPrintingValue => $c->setting('GratisPrintingValue'), locations => \@locations, types => \@types, ); diff --git a/lib/Libki/Controller/Administration/API/DataTables.pm b/lib/Libki/Controller/Administration/API/DataTables.pm index df086c85..4b9f2383 100644 --- a/lib/Libki/Controller/Administration/API/DataTables.pm +++ b/lib/Libki/Controller/Administration/API/DataTables.pm @@ -34,7 +34,7 @@ sub users : Local Args(0) { my $params = $c->req->params; # We need to map the table columns to field names for ordering - my @columns = qw/me.username me.lastname me.firstname me.category allotments.minutes session.minutes me.status me.notes me.is_troublemaker client.name session.status me.creation_source/; + my @columns = qw/me.username me.lastname me.firstname me.category allotments.minutes session.minutes me.funds me.gratis_print_balance me.status me.notes me.is_troublemaker client.name session.status me.creation_source/; my $search_term = $params->{"search[value]"}; my $filter; @@ -127,8 +127,9 @@ sub users : Local Args(0) { category => $u->category, client_name => defined( $u->session ) ? $u->session->client->name : undef, creation_source => $u->creation_source, - firstname => $u->firstname, - funds => $u->funds, + firstname => $u->firstname, + funds => $u->funds, + gratis_print_balance => $u->gratis_print_balance, is_troublemaker => $u->is_troublemaker, lastname => $u->lastname, minutes => $u->session ? $u->session->minutes : undef, diff --git a/lib/Libki/Controller/Administration/API/User.pm b/lib/Libki/Controller/Administration/API/User.pm index 85a79a83..64230b7b 100644 --- a/lib/Libki/Controller/Administration/API/User.pm +++ b/lib/Libki/Controller/Administration/API/User.pm @@ -42,17 +42,18 @@ sub get : Local : Args(1) { $c->stash( { - id => $user->id, - username => $user->username, - firstname => $user->firstname, - lastname => $user->lastname, - category => $user->category, - minutes => $user->minutes($c), - status => $user->status, - notes => $user->notes, - funds => $user->funds, - is_troublemaker => $user->is_troublemaker, - troublemaker_until => defined( $user->troublemaker_until ) + id => $user->id, + username => $user->username, + firstname => $user->firstname, + lastname => $user->lastname, + category => $user->category, + minutes => $user->minutes($c), + status => $user->status, + notes => $user->notes, + funds => $user->funds, + gratis_print_balance => $user->gratis_print_balance, + is_troublemaker => $user->is_troublemaker, + troublemaker_until => defined( $user->troublemaker_until ) ? $user->troublemaker_until->strftime('%Y-%m-%d 23:59') : undef, roles => \@roles, @@ -220,15 +221,16 @@ sub update : Local : Args(0) { my $success = 0; - my $id = $c->request->params->{'id'}; - my $firstname = $c->request->params->{firstname}; - my $lastname = $c->request->params->{lastname}; - my $category = $c->request->params->{category}; - my $minutes = $c->request->params->{'minutes'}; - my $funds = $c->request->params->{'funds'}; - my $notes = $c->request->params->{'notes'}; - my $status = $c->request->params->{'status'}; - my @roles = $c->request->params->{'roles'} || []; + my $id = $c->request->params->{'id'}; + my $firstname = $c->request->params->{firstname}; + my $lastname = $c->request->params->{lastname}; + my $category = $c->request->params->{category}; + my $minutes = $c->request->params->{'minutes'}; + my $funds = $c->request->params->{'funds'}; + my $notes = $c->request->params->{'notes'}; + my $gratis_print_balance = $c->request->params->{'gratis_print_balance'} || 0; + my $status = $c->request->params->{'status'}; + my @roles = $c->request->params->{'roles'} || []; # For some reason the list of checkboxes are created # as a list within a list if multiple are checked @@ -247,12 +249,13 @@ sub update : Local : Args(0) { $success = 1 if $user->update( { - firstname => $firstname, - lastname => $lastname, - category => $category, - notes => $notes, - status => $status, - updated_on => $now, + firstname => $firstname, + lastname => $lastname, + category => $category, + gratis_print_balance => $gratis_print_balance, + notes => $notes, + status => $status, + updated_on => $now, } ); diff --git a/lib/Libki/Controller/Public/API/DataTables.pm b/lib/Libki/Controller/Public/API/DataTables.pm index 64d45c84..83e685e0 100644 --- a/lib/Libki/Controller/Public/API/DataTables.pm +++ b/lib/Libki/Controller/Public/API/DataTables.pm @@ -112,12 +112,13 @@ sub prints : Local Args(0) { foreach my $print_job (@prints) { my $print_file = $print_job->print_file; - my $total_cost = Libki::Utils::Printing::calculate_job_cost( + my ($total_cost, $gratis_discount) = Libki::Utils::Printing::calculate_job_cost( $c, { print_job => $print_job, print_file => $print_file, printer => $printers->{printers}->{ $print_job->printer }, + user => $user, } ); @@ -130,7 +131,7 @@ sub prints : Local Args(0) { } keys %{$printers->{printers}} ) { my $printer = $printers->{printers}->{ $key }; - my $total_cost = Libki::Utils::Printing::calculate_job_cost( + my ($total_cost, $gratis_discount) = Libki::Utils::Printing::calculate_job_cost( $c, { print_job => $print_job, @@ -143,6 +144,7 @@ sub prints : Local Args(0) { selected => $key eq $print_job->printer ? 1 : 0, name => $printer->{public_printer_name}, cost => $total_cost, + gratis_discount => $gratis_discount, }; push( @printer_costs, $data ); diff --git a/lib/Libki/Controller/Public/API/User.pm b/lib/Libki/Controller/Public/API/User.pm index c33a78f2..6cee6d2a 100644 --- a/lib/Libki/Controller/Public/API/User.pm +++ b/lib/Libki/Controller/Public/API/User.pm @@ -41,7 +41,8 @@ sub funds : Local Args(0) { delete $c->stash->{$_} for keys %{$c->stash}; $c->stash( { - funds => $user->funds + funds => $user->funds, + gratis_print_balance => $user->gratis_print_balance, } ); delete $c->stash->{Settings}; diff --git a/lib/Libki/Schema/DB/Result/Job.pm b/lib/Libki/Schema/DB/Result/Job.pm index 3d6829b0..1d2670f0 100644 --- a/lib/Libki/Schema/DB/Result/Job.pm +++ b/lib/Libki/Schema/DB/Result/Job.pm @@ -70,7 +70,7 @@ __PACKAGE__->table("jobs"); =head2 data - data_type: 'longtext' + data_type: 'mediumtext' is_nullable: 1 =head2 taken @@ -110,7 +110,7 @@ __PACKAGE__->add_columns( "type", { data_type => "varchar", is_nullable => 0, size => 191 }, "data", - { data_type => "longtext", is_nullable => 1 }, + { data_type => "mediumtext", is_nullable => 1 }, "taken", { data_type => "varchar", is_nullable => 1, size => 191 }, "status", @@ -149,8 +149,8 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key("id"); -# Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-04-27 09:23:45 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:D2SKTWMAT1jwHSux1UE7qQ +# Created by DBIx::Class::Schema::Loader v0.07053 @ 2026-02-11 10:59:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:slA0TWq4fcyY7kqYTEuk9g __PACKAGE__->load_components('InflateColumn::Serializer'); __PACKAGE__->add_columns( diff --git a/lib/Libki/Schema/DB/Result/LoginSession.pm b/lib/Libki/Schema/DB/Result/LoginSession.pm index 238c7bce..ac5af6c9 100644 --- a/lib/Libki/Schema/DB/Result/LoginSession.pm +++ b/lib/Libki/Schema/DB/Result/LoginSession.pm @@ -57,7 +57,7 @@ __PACKAGE__->table("login_sessions"); =head2 session_data - data_type: 'longtext' + data_type: 'mediumtext' is_nullable: 1 =head2 expires @@ -71,7 +71,7 @@ __PACKAGE__->add_columns( "id", { data_type => "char", is_nullable => 0, size => 72 }, "session_data", - { data_type => "longtext", is_nullable => 1 }, + { data_type => "mediumtext", is_nullable => 1 }, "expires", { data_type => "integer", is_nullable => 1 }, ); @@ -89,8 +89,8 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key("id"); -# Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-04-27 09:23:45 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:m+OJsvQRg7FrVcbnVJwBxg +# Created by DBIx::Class::Schema::Loader v0.07053 @ 2026-02-11 10:59:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:6VF4JGAZI2UkY4knbkR/XQ # You can replace this text with custom code or comments, and it will be preserved on regeneration diff --git a/lib/Libki/Schema/DB/Result/Message.pm b/lib/Libki/Schema/DB/Result/Message.pm index f68f0169..d8f906c7 100644 --- a/lib/Libki/Schema/DB/Result/Message.pm +++ b/lib/Libki/Schema/DB/Result/Message.pm @@ -70,7 +70,7 @@ __PACKAGE__->table("messages"); =head2 content - data_type: 'longtext' + data_type: 'mediumtext' is_nullable: 0 =cut @@ -83,7 +83,7 @@ __PACKAGE__->add_columns( "user_id", { data_type => "integer", is_foreign_key => 1, is_nullable => 0 }, "content", - { data_type => "longtext", is_nullable => 0 }, + { data_type => "mediumtext", is_nullable => 0 }, ); =head1 PRIMARY KEY @@ -116,8 +116,8 @@ __PACKAGE__->belongs_to( ); -# Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-04-27 09:23:45 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hn49kjZD6Hzpl6FiOXBHww +# Created by DBIx::Class::Schema::Loader v0.07053 @ 2026-02-11 10:59:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CuV5c2ONcc5nZazLSr+QhQ # You can replace this text with custom code or comments, and it will be preserved on regeneration diff --git a/lib/Libki/Schema/DB/Result/PrintFile.pm b/lib/Libki/Schema/DB/Result/PrintFile.pm index 1e17122c..812f7e69 100644 --- a/lib/Libki/Schema/DB/Result/PrintFile.pm +++ b/lib/Libki/Schema/DB/Result/PrintFile.pm @@ -64,7 +64,7 @@ __PACKAGE__->table("print_files"); =head2 filename - data_type: 'longtext' + data_type: 'mediumtext' is_nullable: 0 =head2 content_type @@ -141,7 +141,7 @@ __PACKAGE__->add_columns( "id", { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, "filename", - { data_type => "longtext", is_nullable => 0 }, + { data_type => "mediumtext", is_nullable => 0 }, "content_type", { data_type => "varchar", is_nullable => 1, size => 191 }, "data", @@ -246,8 +246,8 @@ __PACKAGE__->belongs_to( ); -# Created by DBIx::Class::Schema::Loader v0.07051 @ 2023-07-11 07:35:11 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:9IRAjpODFDD7mIKVnM/Jgg +# Created by DBIx::Class::Schema::Loader v0.07053 @ 2026-02-11 10:59:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:kc5pJ/hX1R5YWDHG53PgHQ # You can replace this text with custom code or comments, and it will be preserved on regeneration diff --git a/lib/Libki/Schema/DB/Result/PrintJob.pm b/lib/Libki/Schema/DB/Result/PrintJob.pm index bfbb10d8..6d8a32a4 100644 --- a/lib/Libki/Schema/DB/Result/PrintJob.pm +++ b/lib/Libki/Schema/DB/Result/PrintJob.pm @@ -81,7 +81,7 @@ __PACKAGE__->table("print_jobs"); =head2 data - data_type: 'longtext' + data_type: 'mediumtext' is_nullable: 1 =head2 printer @@ -148,7 +148,7 @@ __PACKAGE__->add_columns( "copies", { data_type => "integer", is_nullable => 1 }, "data", - { data_type => "longtext", is_nullable => 1 }, + { data_type => "mediumtext", is_nullable => 1 }, "printer", { data_type => "varchar", is_nullable => 0, size => 191 }, "user_id", @@ -240,8 +240,8 @@ __PACKAGE__->belongs_to( ); -# Created by DBIx::Class::Schema::Loader v0.07051 @ 2023-07-11 07:35:11 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:09Hi0qsW0zShp8vcR24gVg +# Created by DBIx::Class::Schema::Loader v0.07053 @ 2026-02-11 10:59:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:jlX7n/s5f08LDVSFfpbM/g __PACKAGE__->load_components('InflateColumn::Serializer'); __PACKAGE__->add_columns( diff --git a/lib/Libki/Schema/DB/Result/Role.pm b/lib/Libki/Schema/DB/Result/Role.pm index 77e63b42..6febaa74 100644 --- a/lib/Libki/Schema/DB/Result/Role.pm +++ b/lib/Libki/Schema/DB/Result/Role.pm @@ -57,7 +57,7 @@ __PACKAGE__->table("roles"); =head2 role - data_type: 'longtext' + data_type: 'mediumtext' is_nullable: 1 =cut @@ -66,7 +66,7 @@ __PACKAGE__->add_columns( "id", { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, "role", - { data_type => "longtext", is_nullable => 1 }, + { data_type => "mediumtext", is_nullable => 1 }, ); =head1 PRIMARY KEY @@ -109,8 +109,8 @@ Composing rels: L -> user __PACKAGE__->many_to_many("users", "user_roles", "user"); -# Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-04-27 09:23:45 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:/EScGDz6Ax+fO+asu+G6sw +# Created by DBIx::Class::Schema::Loader v0.07053 @ 2026-02-11 10:59:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:I0G2JZpQIrURRKmeedBoJA # You can replace this text with custom code or comments, and it will be preserved on regeneration diff --git a/lib/Libki/Schema/DB/Result/Setting.pm b/lib/Libki/Schema/DB/Result/Setting.pm index 2595918c..05811d8c 100644 --- a/lib/Libki/Schema/DB/Result/Setting.pm +++ b/lib/Libki/Schema/DB/Result/Setting.pm @@ -64,7 +64,7 @@ __PACKAGE__->table("settings"); =head2 value - data_type: 'longtext' + data_type: 'mediumtext' is_nullable: 0 =cut @@ -75,7 +75,7 @@ __PACKAGE__->add_columns( "name", { data_type => "varchar", is_nullable => 0, size => 191 }, "value", - { data_type => "longtext", is_nullable => 0 }, + { data_type => "mediumtext", is_nullable => 0 }, ); =head1 PRIMARY KEY @@ -93,8 +93,8 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key("instance", "name"); -# Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-04-27 09:23:45 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EqZtUoS/kZvDJlZBoafaPQ +# Created by DBIx::Class::Schema::Loader v0.07053 @ 2026-02-11 10:59:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:L3jv3QXJPdbcPUJ5AR3QnA # You can replace this text with custom code or comments, and it will be preserved on regeneration diff --git a/lib/Libki/Schema/DB/Result/Transaction.pm b/lib/Libki/Schema/DB/Result/Transaction.pm index f543fa85..cae346de 100644 --- a/lib/Libki/Schema/DB/Result/Transaction.pm +++ b/lib/Libki/Schema/DB/Result/Transaction.pm @@ -83,7 +83,6 @@ __PACKAGE__->table("transactions"); =head2 amount_cents data_type: 'integer' - extra: {unsigned => 1} is_nullable: 0 =head2 currency @@ -132,7 +131,7 @@ __PACKAGE__->add_columns( "provider_payment_id", { data_type => "varchar", is_nullable => 1, size => 128 }, "amount_cents", - { data_type => "integer", extra => { unsigned => 1 }, is_nullable => 0 }, + { data_type => "integer", is_nullable => 0 }, "currency", { data_type => "char", default_value => "USD", is_nullable => 0, size => 3 }, "status", @@ -206,8 +205,8 @@ __PACKAGE__->belongs_to( ); -# Created by DBIx::Class::Schema::Loader v0.07053 @ 2025-12-19 15:32:17 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:C5vxDal47UNOSdaRn9nouQ +# Created by DBIx::Class::Schema::Loader v0.07053 @ 2026-02-11 10:59:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wTt5kG0X0tOz/Mq55tR78w # You can replace this text with custom code or comments, and it will be preserved on regeneration diff --git a/lib/Libki/Schema/DB/Result/User.pm b/lib/Libki/Schema/DB/Result/User.pm index 3aa669ae..d6972231 100644 --- a/lib/Libki/Schema/DB/Result/User.pm +++ b/lib/Libki/Schema/DB/Result/User.pm @@ -152,6 +152,14 @@ __PACKAGE__->table("users"); is_nullable: 1 size: 191 +=head2 gratis_print_balance + + data_type: 'integer' + default_value: 0 + is_nullable: 0 + +Number of free prints remaining; represents pages or cents depending on system settings + =cut __PACKAGE__->add_columns( @@ -211,6 +219,8 @@ __PACKAGE__->add_columns( { data_type => "varchar", default_value => "", is_nullable => 1, size => 191 }, "creation_source", { data_type => "varchar", is_nullable => 1, size => 191 }, + "gratis_print_balance", + { data_type => "integer", default_value => 0, is_nullable => 0 }, ); =head1 PRIMARY KEY @@ -374,8 +384,8 @@ Composing rels: L -> role __PACKAGE__->many_to_many("roles", "user_roles", "role"); -# Created by DBIx::Class::Schema::Loader v0.07053 @ 2025-12-19 15:32:17 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ieWa+l+vWEQ/vWPaNZ37DQ +# Created by DBIx::Class::Schema::Loader v0.07053 @ 2026-02-11 10:59:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tV3lA7kB36cKoCgfsJq4wg __PACKAGE__->add_columns( 'password' => { diff --git a/lib/Libki/Utils/Printing.pm b/lib/Libki/Utils/Printing.pm index 32564e7f..6e48a750 100644 --- a/lib/Libki/Utils/Printing.pm +++ b/lib/Libki/Utils/Printing.pm @@ -4,6 +4,7 @@ use Modern::Perl; use File::Slurp qw( write_file ); use File::Temp qw( tempfile ); +use JSON qw( to_json ); use PDF::API2; use Try::Tiny; @@ -148,13 +149,18 @@ sub create_print_job_and_file { =head2 calculate_job_cost -Helper function to calculate the cost of a print job for a given printer +Helper function to calculate the cost of a print job for a given printer. +Returns a list containing the calculated cost and the gratis discount applied. + + my ( $cost, $gratis_discount ) = calculate_job_cost( $c, { print_job => $job } ); =cut sub calculate_job_cost { my ( $c, $params ) = @_; + my $GratisPrintingMethod = $c->setting("GratisPrintingMethod"); + my $print_job = $params->{print_job}; my $print_file = $params->{print_file}; my $printer = $params->{printer}; @@ -175,9 +181,33 @@ sub calculate_job_cost { my $copies = $print_job->copies || 1; my $cpp = $printer->{cost_per_page} || 0; - my $cost = $copies * $pages * $cpp; + my $total_pages = $pages * $copies; + + my $gratis_discount = 0; + + if ( $GratisPrintingMethod eq 'pages' ) { + if ( $total_pages >= $c->user->gratis_print_balance ) { + $gratis_discount = $c->user->gratis_print_balance; + $total_pages -= $c->user->gratis_print_balance; + } else { + $gratis_discount = $total_pages; + $total_pages = 0; + } + } + + my $cost = $total_pages * $cpp; + + if ( $GratisPrintingMethod eq 'balance' ) { + if ( $cost >= $c->user->gratis_print_balance ) { + $gratis_discount = $c->user->gratis_print_balance; + $cost -= $c->user->gratis_print_balance; + } else { + $gratis_discount = $cost; + $cost = 0; + } + } - return $cost; + return ($cost, $gratis_discount); } =head2 cancel @@ -298,7 +328,7 @@ sub release { printer => $printer, }; - my $total_cost = calculate_job_cost( $c, + my ($total_cost, $gratis_discount) = calculate_job_cost( $c, { print_job => $print_job, printer => $printer, @@ -310,12 +340,33 @@ sub release { if ( $total_cost <= $user->funds ) { $user->debit_funds( $c, $total_cost ); + $user->gratis_print_balance( $user->gratis_print_balance - $gratis_discount ); + $print_job->status(PRINT_STATUS_PENDING); $c->model('DB')->txn_do( sub { $user->update(); $print_job->update(); + + # Add statistic indicating gratis discount was used + if ( $gratis_discount > 0 ) { + $c->model('DB::Statistic')->create( + { + instance => $c->instance, + username => $c->user->username, + action => 'GRATIS_DISCOUNT', + created_on => $c->now, + session_id => $c->sessionid, + info => to_json( + { + print_job_id => $print_job->id, + gratis_discount => $gratis_discount, + } + ), + } + ); + } } ); } diff --git a/lib/Libki/Utils/User.pm b/lib/Libki/Utils/User.pm index 426d6150..66737759 100644 --- a/lib/Libki/Utils/User.pm +++ b/lib/Libki/Utils/User.pm @@ -70,6 +70,7 @@ sub create_or_update_user { is_troublemaker => 'No', created_on => $now, updated_on => $now, + gratis_print_balance => $c->setting('GratisPrintingValue') || 0, } ); @@ -158,6 +159,7 @@ sub create_guest { updated_on => $now, category => $category, creation_source => 'local', + gratis_print_balance => $c->setting('GratisPrintingValueGuest') || 0, } ); diff --git a/root/dynamic/includes/index/printing.tt b/root/dynamic/includes/index/printing.tt index 84f1c264..42d1bb81 100644 --- a/root/dynamic/includes/index/printing.tt +++ b/root/dynamic/includes/index/printing.tt @@ -68,7 +68,32 @@