package Business::AU::Ledger::View::Payment; use Business::AU::Ledger::Util::Validate; use Date::Simple; use JSON::XS; use Moose; extends 'Business::AU::Ledger::View::Base'; has field_width => (is => 'rw', isa => 'HashRef'); has row_count => (is => 'rw', isa => 'Int'); use namespace::autoclean; our $VERSION = '0.88'; # ----------------------------------------------- sub BUILD { my($self) = @_; $self -> field_width ( { amount => 12, day => 5, # For 'Total'. gst_amount => 12, petty_cash_in => 7, petty_cash_out => 7, private_use_amount => 10, private_use_percent => 6, reference => 10, } ); $self -> row_count(0); } # End of BUILD; # ----------------------------------------------- sub format { my($self, $output) = @_; my($field); my($row, @row); for $row (@$output) { $field = $self -> format_fields($row); push @row, $field; } $self -> log(__PACKAGE__ . '. Leaving format'); return [@row]; } # End of format. # ----------------------------------------------- sub format_fields { my($self, $default) = @_; $default ||= {}; # Ensure all fields, except menus, have defaults. for (qw/amount day error gst_amount petty_cash_in petty_cash_out private_use_amount private_use_percent reference timestamp/) { if (! defined $$default{$_}) { $$default{$_} = ''; } } $self -> row_count($self -> row_count + 1); # The 'day' field will either come from the timestamp in a pre-existing record, or a total, or we leave it blank. if ($$default{'timestamp'}) { ($$default{'day'} = substr($$default{'timestamp'}, 8, 2) ) =~ s/^0//; } else { if ($$default{'day'} ne 'Total') { $$default{'day'} = ''; } } my($field_width) = $self -> field_width; my($row_count) = $self -> row_count; my($result) = { amount => qq||, category => $self -> build_select('payment', 'category_code', "_$row_count", $$default{'category_code_id'} || 1), day => qq||, error => $$default{'error'}, gst_amount => qq||, gst_category => $self -> build_select('payment', 'gst_code', "_$row_count", $$default{'gst_code_id'} || 1), payment_method => $self -> build_select('payment', 'payment_method', "_$row_count", $$default{'payment_method_id'} || 1), petty_cash_in => qq||, petty_cash_out => qq||, private_use_amount => qq||, private_use_percent => qq||, reference => qq||, submit => qq||, tx_detail => $self -> build_select('payment', 'tx_detail', "_$row_count", $$default{'tx_detail_id'} || 1), }; # Clean up the total line. if (! $$default{'timestamp'}) { if ($$default{'day'} eq 'Total') { for (qw/category tx_detail gst_category payment_method private_use_percent reference submit/) { $$result{$_} = ''; } } } return $result; } # End of format_fields. # ----------------------------------------------- sub initialize { my($self) = @_; my($input) = Business::AU::Ledger::Util::Validate -> new(db => $self -> db, query => $self -> query) -> initialize_payments; # Output all existing data (or errors). my($output) = $self -> process($input); # Output a row for new data. push @$output, $self -> format_fields; $self -> log(__PACKAGE__ . '. Leaving initialize'); return JSON::XS -> new -> encode({results => $output}); } # End of initialize. # ----------------------------------------------- sub log { my($self, $s) = @_; $self -> db -> log($s); } # End of log. # ----------------------------------------------- sub process { my($self, $input) = @_; my($msgs) = $input -> msgs; my(%prompt) = map{my($s) = $_; $s =~ s/^field_//; $s =~ tr/_/ /; ($_ => ucfirst $s)} keys %$msgs; my($error) = $input -> has_invalid || $input -> has_missing; my($output); if ($error) { my($msg); my(@msg); my($prompt); for $msg (sort keys %$msgs) { if ($msg =~ /^field_/) { # Remove row_count part of field name. # I don't know why the row_count is preceeded by ' ' and not '_'. ($prompt = $prompt{$msg}) =~ s/\s\d+$//; push @msg, "$prompt: $$msgs{$msg}"; } } $output = [{error => [@msg]}]; } else { # Use scalar context to retrieve a hash ref. $input = $input -> valid; # Init phase uses 'initialize', payment phase uses 'month'. # # If the current month is before the first month of the financial year, # then it (the current month) is in the next year. my($month_number) = $self -> db -> get_month_number($$input{'initialize'} || $$input{'month'}); my($year) = $self -> session -> param('start_year'); my($start_month) = $self -> session -> param('start_month'); my($start_month_number) = $self -> db -> get_month_number($start_month); if ($month_number < $start_month_number) { $year++; } if (! $$input{'initialize'}) { # Remove row_count from field names. my($id); my($key); # But first ... # Save the row_count associated with submit, since it's the only # row of input we are going to process. (The problem is that all # rows are submitted because they're all on the same form). for $key (keys %$input) { if ($key !~ /^submit/) { next; } ($_ = $key) =~ s/_(\d+)$//; $id = $1; } # Now zap all input except the values with the same id. my($value); for $key (keys %$input) { # Skip the special cases, which are zapped below. if ($key !~ /\w_\d+/) { next; } $value = delete $$input{$key}; ($_ = $key) =~ s/_(\d+)$//; if ($id != $1) { next; } $$input{$_} = $value; } # Convert user's value for 'day' into a timestamp. $$input{'timestamp'} = $self -> calculate_timestamp($$input{'month'}, $$input{'day'}); # Remove CGI fields which are not database fields. for (qw/day rm sid submit/) { delete $$input{$_}; } # Set defaults for optional fields. $$input{'comment'} ||= ''; $$input{'gst_amount'} ||= 0.00; $$input{'month'} = $month_number; $$input{'petty_cash_in'} ||= 0.00; $$input{'petty_cash_out'} ||= 0.00; $$input{'private_use_amount'} ||= 0.00; $$input{'private_use_percent'} ||= 0.00; $$input{'reference'} ||= ''; for (sort keys %$input) { $self -> log("Saving $_ => $$input{$_}"); } $self -> db -> payment -> add($input); } # This code goes here so we pick up the new record. $output = $self -> db -> payment -> get_payments_via_ym($year, $month_number); if ($#$output >= 0) { $output = $self -> total($output); } $output = $self -> format($output); } $self -> log(__PACKAGE__ . '. Leaving process'); return $output; } # End of process. # -------------------------------------------------- sub submit { my($self) = @_; my($input) = Business::AU::Ledger::Util::Validate -> new(db => $self -> db, query => $self -> query) -> payment; # Output all existing data (or errors). my($output) = $self -> process($input); # Output a row for new data. push @$output, $self -> format_fields; $self -> log(__PACKAGE__ . ". Leaving submit. Row count now: @{[scalar @$output]}"); return JSON::XS -> new -> encode({results => $output}); } # End of submit. # ----------------------------------------------- sub total { my($self, $output) = @_; my(@key) = (qw/amount gst_amount petty_cash_in petty_cash_out private_use_amount/); my(%total); for (@key) { $total{$_} = 0; } $total{'day'} = 'Total'; my($row); for $row (@$output) { for (@key) { $total{$_} += $$row{$_}; } } my($field_width) = $self -> field_width; for (@key) { # We avoid '%-' to right-justify, to make the total line stand out. $total{$_} = sprintf "\%$$field_width{$_}.2f", $total{$_}; } $self -> log(__PACKAGE__ . '. Leaving total'); push @$output, \%total; return $output; } # End of total. # -------------------------------------------------- __PACKAGE__ -> meta -> make_immutable; 1;