package App::POS::Hardware::Handlers;
use strict;
use warnings;
use UUID::Tiny;
use URI::Escape;
use LWP::Simple qw($ua get);
use Number::Format;
use Time::HiRes qw( sleep stat );
use MIME::Base64 qw( encode_base64 decode_base64 );
use Data::Dumper;
use Device::SerialPort qw( :STAT );
use Template;
use Template::Stash;
use Template::Config;
use IO::File;
use Net::Ping;
use File::Slurp qw( read_dir read_file write_file append_file );
use POSIX qw( strftime ceil );
use File::Path qw( make_path );
use File::Copy qw( copy move );
use Encode qw( decode encode from_to is_utf8 );
use Encode::Guess;
use File::Find qw( find );
use Date::Calc qw( Date_to_Time Delta_Days Add_Delta_Days Today );
use File::Remove 'remove';
use File::Basename qw( basename dirname );
use Benchmark ':hireswallclock';
use IO::Socket;
use JSON::XS;
use HTML::Entities;
use Digest::MD5 qw( md5_hex );
use Text::Wrap;
use File::ChangeNotify;
use App::POS::Config;
use App::POS::Utils qw(
  translate my_round my_conv date_based_on_closing_hour md5_from_file
  send_mail sales post memcache read_translation_file date_is_bigger
  date_is_bigger_or_equal is_hour is_date is_number has_numbers
  url_encode db_table_fix_dup_ids my_split
);
use App::POS::Counters;
use App::POS::Check;
use App::POS::Printer;
use App::POS::Hardware::PrinterLogo;
use App::POS::Hardware::CustomerDisplays::BLEEP750;
use App::POS::Hardware::CustomerDisplays::ICF2002;
use App::POS::Hardware::CustomerDisplays::MINIO2;
use App::POS::Hardware::CustomerDisplays::MINIPOS;
use App::POS::Hardware::CustomerDisplays::PLAIN;
use App::POS::Hardware::CustomerDisplays::PLAIN_Slower;
use App::POS::Hardware::CustomerDisplays::IBM;
use App::POS::Hardware::CustomerDisplays::CCTV;
use App::POS::Hardware::CustomerDisplays::SAM4S;
use App::POS::Hardware::CustomerDisplays::TOSHIBA;
use App::POS::Hardware::CustomerDisplays::TOSHIBA_C10;
use App::POS::Hardware::CustomerDisplays::VFD;
use App::POS::Hardware::CustomerDisplays::VFD_Inverted;
use App::POS::Hardware::CustomerDisplays::VIRTUAL;
use App::POS::Hardware::CustomerDisplays::AUDITORNOVA;
use App::POS::Hardware::CustomerDisplays::CSI_STARVISION;
use App::POS::Hardware::CustomerDisplays::VFD_SERIE_ESC_POS;
use App::POS::Hardware::CustomerDisplays::LED8;
use App::POS::Hardware::Scales::ASEP30P;
use App::POS::Hardware::Scales::CAS_ER_PLUS;
use App::POS::Hardware::Scales::DIBAL2;
use App::POS::Hardware::Scales::DIBAL;
use App::POS::Hardware::Scales::PROTOCOL_DOLLAR_96008N1;
use App::POS::Hardware::Scales::RUBY;
use App::POS::Hardware::Scales::BIZERBA_CS300;
use App::POS::Hardware::Scales::CAS_PD_II_PARAM6;
use App::POS::Hardware::Scales::CAS_PR_II;
use App::POS::Hardware::Scales::DIBAL3;
use App::POS::Hardware::Scales::DIGI_DS_788;
use App::POS::Hardware::Scales::RUBY2;
use App::POS::Hardware::Scales::RUBY3;
use App::POS::Hardware::Scales::RUBY_RK100;
use App::POS::Hardware::Scales::TOWA_OZ15L;
use App::POS::Hardware::Scales::SIGMA_SG100_P18;
use App::POS::Hardware::Scales::EPELSA;
use App::POS::Hardware::Scales::GRAM_ZFOC_30P_RS;
use App::POS::Hardware::Scales::LW_QEP30;
use App::POS::Hardware::Scales::VectronServiceCall;
use Exporter::Easy (OK=>[qw/
  read_magnetic_card
  read_dallas_reader
  read_barcode
  read_scale
  printers_monitor_group
  files_monitor
  server_monitor
  queue_manager
  /]);
&File::Basename::fileparse_set_fstype('MSWin32');
my$print_bill_md5_valid='';
my$print_bill_md5_cur=undef;
my$print_invoice_md5_valid='';
my$print_invoice_md5_cur=undef;
my$REAL_PRINT=1;
my$SERVER_FILE_MONITOR_METHOD='';
my$DEBUG_FILES=0;
my$DEBUG_TIMINGS=0;
my$LAST_INTEGRATED_CHECK;
my$DECIMAL_PLACES=2;
my$CFG_LANG_TRANSLATION='';
my$SOFTWARE_INFO=[];
my$TAXFREE_VAT_ISENTION='M05_Isento Artigo 14. do CIVA (ou similar)';
my$TAXFREE_VAT_ISENTION_IDX='9';
my%DOC_VIAS=(1=>'Original',2=>'Duplicado',3=>'Triplicado',4=>'Quadruplicado',5=>'Quintuplicado',);
my$NFORMATER=Number::Format->new();
$Template::Config::STASH='Template::Stash::XS';
$Template::Stash::SCALAR_OPS->{"mycustomerwrap"}=sub{my$scalar=shift;
$Text::Wrap::columns=31;
return wrap('','',($scalar));};
$Template::Stash::SCALAR_OPS->{"format_qtty"}=sub{my$scalar=shift;
my$decimal_places=shift||3;
my$allow_negatives=shift;
$scalar=$scalar*1;
if(!defined$allow_negatives||!$allow_negatives){$scalar=abs($scalar);}if($scalar=~/\./){return sprintf("%.".$decimal_places."f",$scalar);}else{return$scalar*1;}};
$Template::Stash::SCALAR_OPS->{"to_positive"}=sub{my$scalar=shift;
my$decimals=shift||$DECIMAL_PLACES;
$scalar||=0;
if($scalar){if($scalar=~/^\-/){$scalar=$scalar*-1;}$scalar=sprintf("%.0".$decimals."f",$scalar);}return$scalar;};
$Template::Stash::SCALAR_OPS->{"myround"}=sub{my$scalar=shift;
my$decimals=shift;
$decimals=$DECIMAL_PLACES unless defined$decimals;
my$n=my_round($scalar,$decimals);
if($decimals==0){my@a=split/\./,$n;
return$a[0];}return my_round($scalar,$decimals);
};
$Template::Stash::SCALAR_OPS->{"myround_qtty"}=sub{my$d=shift;
my$r=shift||$DECIMAL_PLACES;
my@a=split/\./,$d;
my$ret=$d;
if(scalar@a==2){$a[1]=substr($a[1],0,$r);
$ret=join(".",@a);}return$ret;};
$Template::Stash::SCALAR_OPS->{"mydate"}=sub{my$scalar=shift;
if($CFG_LANG_TRANSLATION eq 'PT'){return$scalar}my$y="";
my$m="";
my$d="";
my$time="";
($y,$m,$d)=$scalar=~/^(\d\d\d\d)\-(\d\d)\-(\d\d)$/;
$y||="";
if(!$y){($y,$m,$d,$time)=$scalar=~/^(\d\d\d\d)\-(\d\d)\-(\d\d)\s(.*?)$/;
$y||="";}if($y){if($CFG_LANG_TRANSLATION=~/^ES/){if($y&&$time){my$ret="$d-$m-$y $time";
return$ret;}elsif($y){my$ret="$d-$m-$y";
return$ret;}}}return$scalar;};
$Template::Stash::SCALAR_OPS->{"chars"}=sub{my$scalar=shift;
my$num=shift;
return$scalar unless defined$num;
my$is_utf8=is_utf8($scalar);
my$decoded=$is_utf8?$scalar:decode("utf8",$scalar);
if(length($decoded)>$num){$decoded=substr($decoded,0,$num);}elsif(length($decoded)<$num){my$spaces=$num-length($decoded);
$decoded.=" " x$spaces;}$decoded=$is_utf8?$decoded:encode("utf8",$decoded);
return$decoded;};
my%BARCODES=();
my$STATION_MAIN_PRINTER=undef;
my$TEMPLATE=undef;
my$PRINTING_PRIORITIES=undef;
my$PRODUCTS_INFO=undef;
my$PRODUCTS_EXTRAS=undef;
my$PRODUCTS_MESSAGES=undef;
my$PRODUCTS_EXTRA_DATA=undef;
my$PRODUCTS_MENUS=undef;
my$VAT_ISENTIONS=undef;
my$VAT_ISENTIONS_IDX=undef;
my$CLERKS_INFO=undef;
my$CLERKS_DRAWER={};
my$PRODUCTS_TRANSLATIONS=undef;
my$CLERKS_IDS_BY_LOGIN=undef;
my$PRODUCTS_IDS_BY_HANDY_PLU=undef;
my$PRINTING_ZONES_CONFIG=undef;
my$PRINTING_ZONES_FILTERS=undef;
my%CHECKS_REQUESTED=();
my%AREAS_INFO=();
my%HAPPYHOUR_INFO=();
my%TABLES_TO_CARDS=();
my%MAIN_PRINTERS=();
my$CAN_DELIVER_PRODUCT_ID=undef;
my%X64_CONTROL=();
my%MOBILE_PRINT_BILLS=();
my%MOBILE_PRINT_INVOICES=();
my%DOC_TYPE_PRINTER_OVERRIDES=();
my%CASHIER_NAMES=();
my%RECEIPT_PRINT_VIAS=();
my%RECEIPT_PRINT_VIAS_WHERE_TO_APPLY=();
my%OWNER_INFO_CACHE=();
my%DELIVERY_AREAS=();
my%VOIDS_MONITORING=();
my$SPLIT_AREA_IDX=-1;
my$STATION_NAMES={};
my%MEMC_CONNECTIONS;
my%DOCS_TO_HISTORY=(7=>1,5=>1,8=>1,12=>1,13=>1,14=>1,32=>1,33=>1,34=>1,35=>1,90=>1,);
my$CLOSE_HOUR=undef;
my%EXECUTE_FILTERS=('drawer#'=>'execute_cash_drawer','customer_display#'=>'execute_customer_display','new_config\.txt$'=>'activate_new_config','.close$'=>'check_finalize','\d+\.hold$'=>'print_check_hold','print#\d+#\d+#\d+#.*?\.txt'=>'print_file','reprint_check#\d+#\d+'=>'check_reprint','print_reprint_checks#\d+#\d+'=>'print_reprint_checks','pdf_to_station_print#\d+#'=>'pdf_to_station_print','printer_error#\d+'=>'printer_error','server_sql_query#'=>'server_sql_query','print_notclosed_check#\d+'=>'print_notclosed_check','print_offer_ticket#\d+'=>'print_offer_ticket','moneycount#\d+#\d+'=>'print_moneycount','report#closeday'=>'print_report_closeday','report#resumed#'=>'print_report_resumed','report#detailed#'=>'print_report_detailed','report#user#'=>'print_report_clerk','report#tables#'=>'print_report_tables','clerk_in_out#'=>'integrate_clerk_in_out','clerk_in#'=>'integrate_clerk_in','clerk_out#'=>'integrate_clerk_out','recover_document_stock#'=>'recover_document_stock','keyboard\.txt$'=>'keyboard_process','print_voucher#\d+'=>'create_voucher','burn_voucher#\d+'=>'burn_voucher','restart_software#\d+\.txt$'=>'restart_software','restart_hardware_handlers#\d+\.txt$'=>'restart_hardware_handlers','station_message#\d+'=>'station_message','register_cashdrawer_open#\d+'=>'register_cashdrawer_open','table_transfer#\d+'=>'table_transfer','printers_register#\d+'=>'printers_register','x64_request_'=>'x64_request','x64_transfer_'=>'x64_transfer','x64_printbill_'=>'x64_printbill','x64_void_'=>'x64_void','print_daily_specials#\d+'=>'print_daily_specials','print_tech_report#\d+'=>'print_tech_report','print_to_main_printer#\d+'=>'print_to_main_printer','reprint_orders#\d+'=>'reprint_orders','ccreport#'=>'print_cc_report','doc_history#'=>'print_cc_doc_history','printer_screen#'=>'printer_screen','rgpd_customer_export#'=>'rgpd_customer_export','rgpd_print_auth_statement#'=>'rgpd_print_auth_statement','report_stk_below_minimal#'=>'report_stk_below_minimal','report_stk_current#'=>'report_stk_current','report_stk_movements#'=>'report_stk_movements','report_products_list#'=>'report_products_list','print_schedule#\d+'=>'print_schedule','print_birthdates#\d+'=>'print_birthdates','print_customer_profile#\d+'=>'print_customer_profile','cashier_open#\d+#\d+'=>'cashier_open','cashier_in_out#\d+#\d+'=>'cashier_in_out','cashier_close#\d+#\d+'=>'cashier_close','print_barcode_label#\d+#\d+'=>'print_barcode_label','print_shelf_label#\d+#\d+'=>'print_shelf_label','execute_command\-\d+'=>'execute_command','barcode\.txt'=>'process_barcode','msg_to_ui#\d+'=>'process_msg_to_ui','scale\.txt'=>'process_scale','shutdown#'=>'process_shutdown','shutdown\.txt'=>'process_kill_application','sms_report'=>'sms_report','cashier_report_resumed#'=>'cashier_report_resumed','cashier_report_detailed#'=>'cashier_report_detailed','points_customer_statement'=>'points_customer_statement','cache_refresh#\d+'=>'cache_refresh','execute_file#\d+'=>'execute_file','check_.*\.pdf$'=>'print_taxfree_file','rfid_card_operations#'=>'rfid_card_operations','rfid_card_balance#'=>'rfid_card_balance','check_import#'=>'check_import',);
sub queue_manager{my($daemon_name,$machine,$devices)=@_;
$machine->config()->__cache("");
$devices=$machine->config()->devices_by_type($daemon_name);
$devices||=[];
my$num;
my$info;
my$valid=undef;
foreach my $d(@$devices){$num=$d->{num};
$info=$d->{device};
$info->{Inactive}||=0;
if(!exists$info->{Device}||!$info->{Device}||$info->{Inactive}){print STDERR"Device $num: Missing 'Device' parameter or 'Inactive' device... skipping...\n";
next;}print STDERR"Device $num - WILL USE: ".$info->{Model}." - ".$info->{Device}."\n";
$valid=1;
last;}if(!defined$valid){print STDERR"Didn't find any valid device, aborting...\n";
return;}my$cfg=$machine->config()->load_fixed();
$info->{InitSystemCommand}||="";
if($info->{InitSystemCommand}){my$device=$info->{Device};
$info->{InitSystemCommand}=~s/\@DEVICE\@/$device/g;
print STDERR"Will execute: ".$info->{InitSystemCommand}."\n";
my$res=system($info->{InitSystemCommand});
print STDERR"Result: $res\n";}my$strobe=chr(13).chr(8).chr(83).chr(1).chr(151);
eval{my$PortObj=Device::SerialPort->new($info->{Device});
if(defined$PortObj){$PortObj->baudrate(2400);
$PortObj->parity("none");
$PortObj->databits(8);
$PortObj->stopbits(1);
$PortObj->handshake("none");
my$last_read=-1;
my$write=0;
while(1){if(-e$machine->dir_tmp()."/queue.txt"){local$/=undef;
open(F,"< ".$machine->dir_tmp()."/queue.txt");
my$cnt=<F>;
chop($cnt);
close(F);
$cnt||=0;
my$num1=0;
my$num2=0;
if($cnt!=0){if($cnt!=$last_read){$write=1;
$last_read=$cnt;}else{$write=0;}if($cnt<10){$num2=$cnt;}else{my@a=split//,$cnt;
$num1=$a[0];
$num2=$a[1];}}if($write){my$strobe=chr(13).chr(8).chr(83).chr(1).chr(151);
my$sleep=0.05;
print STDERR"num1: $num1, num2: $num2\n";
my$str;
my$checksum;
$checksum=256-(13+0+65+ord($num1))%256;
$str=chr(13).chr(0).chr(65).$num1.chr($checksum);
$PortObj->write($str);
sleep($sleep);
$checksum=(256-(13+1+65+ord($num2)))%256;
$str=chr(13).chr(1).chr(65).$num2.chr($checksum);
$PortObj->write($str);
sleep($sleep);
$PortObj->write($strobe);}else{sleep 1;}}else{sleep 2;}}}else{print STDERR"Error (1) on $info->{Device}\n";}};
if($@){print STDERR"Error (1) on $info->{Device} - $@\n";}}sub read_dallas_reader{my($daemon_name,$machine,$devices)=@_;
$machine->config()->__cache("");
$devices=$machine->config()->devices_by_type($daemon_name);
my$cfg=$machine->config()->load_fixed();
$cfg->{HARDWARE_DEBUG}||=0;
$devices||=[];
my$num;
my$info;
my$valid=undef;
foreach my $d(@$devices){$num=$d->{num};
$info=$d->{device};
$info->{Inactive}||=0;
if(!exists$info->{Device}||!$info->{Device}||$info->{Inactive}){print STDERR"Device $num: Missing 'Device' parameter or 'Inactive' device... skipping...\n";
next;}print STDERR"Device $num - WILL USE: ".$info->{Model}." - ".$info->{Device}."\n";
$valid=1;
last;}if(!defined$valid){print STDERR"Didn't find any valid device, aborting...\n";
return;}print STDERR"> read_dallas_reader - ".$info->{Device}."\n";
$info->{InitSystemCommand}||="";
$info->{Driver}||="";
if($info->{InitSystemCommand}){my$device=$info->{Device};
$info->{InitSystemCommand}=~s/\@DEVICE\@/$device/g;
print STDERR"Will execute: ".$info->{InitSystemCommand}."\n";
my$res=system($info->{InitSystemCommand});
print STDERR"Result: $res\n";}eval{my$PortObj=Device::SerialPort->new($info->{Device});
if(defined$PortObj){$PortObj->baudrate(9600);
$PortObj->parity("none");
$PortObj->databits(8);
$PortObj->stopbits(1);
$PortObj->handshake("none");
if($info->{Driver}eq 'CUSTOM1'){while(1){my($count_in,$string_in)=$PortObj->read(255);
if($cfg->{HARDWARE_DEBUG}){print STDERR"####### count_in: $count_in, string_in: #$string_in#\n";}if(defined$count_in&&$count_in>0){chop($string_in);chop($string_in);
if($string_in&&$string_in=~/^\w+$/){print STDERR"#$string_in#\n";
if($string_in eq 'OUT'){&lazy_file_create("/opt/pos/common/tmp/logout.txt",1,$machine);}else{_send_to_ui('BARCODE',$string_in);}}}sleep 0.2;}return;}elsif($info->{Driver}eq 'ITS'){$PortObj->baudrate(115200);
$PortObj->parity("none");
$PortObj->databits(8);
$PortObj->stopbits(1);
$PortObj->handshake("none");
while(1){my($count_in,$string_in)=$PortObj->read(255);
if($cfg->{HARDWARE_DEBUG}){print STDERR"####### count_in: $count_in, string_in: #$string_in#\n";}if(defined$count_in&&$count_in>0){$string_in=~s/\n//g;
$string_in=~s/\r//g;
$string_in=~s/\%DALL//;
_send_to_ui('BARCODE',$string_in);}sleep 0.2;}return;}elsif($info->{Driver}eq 'CSI_STARVISION'){while(1){my($count_in,$string_in)=$PortObj->read(255);
if($cfg->{HARDWARE_DEBUG}){print STDERR"####### count_in: $count_in, string_in: #$string_in#\n";}if(defined$count_in&&$count_in>0){$string_in=~s/^.(.*?).$/$1/;
if($string_in){if($string_in eq 'T'){&lazy_file_create("/opt/pos/common/tmp/logout.txt",1,$machine);}else{_send_to_ui('BARCODE',$string_in);}}}sleep 0.2;}return;}elsif($info->{Driver}eq 'CSI_CHD'){while(1){my($count_in,$string_in)=$PortObj->read(255);
if($cfg->{HARDWARE_DEBUG}){print STDERR"####### count_in: $count_in, string_in: #$string_in#\n";}if(defined$count_in&&$count_in>0){my@chars=split//,$string_in;
my$logout=1;
my$idx=0;
my$read="";
for my $c(@chars){my$n=ord($c);
if($idx==0&&$n==39){$logout=1;}elsif($idx>0&&$n!=0){$logout=0;
$read.=$n;}$idx++;}if($logout){&lazy_file_create("/opt/pos/common/tmp/logout.txt",1,$machine);}elsif($read){_send_to_ui('BARCODE',$read);}}sleep 0.2;}return;}elsif($info->{Driver}eq 'AUDITOR'){while(1){my($count_in,$string_in)=$PortObj->read(255);
if($cfg->{HARDWARE_DEBUG}){print STDERR"####### count_in: $count_in, string_in: #$string_in#\n";}if(defined$count_in&&$count_in>0){chop($string_in);
chop($string_in);
_send_to_ui('BARCODE',$string_in);}sleep 0.2;}return;}my$collect=0;
my$read="";
my$logout=0;
while(1){my($count_in,$string_in)=$PortObj->read(255);
if($cfg->{HARDWARE_DEBUG}){print STDERR"####### count_in: $count_in, string_in: #$string_in#\n";}if($count_in>0){my@chars=split//,$string_in;
for my $c(@chars){my$n=ord($c);
if($n==242){$collect=0;
$read="";
$logout=1;}elsif($n==241){$collect=1;
$logout=0;}else{if($collect){$read.=$c;}}}if($logout){print STDERR"### logout: $logout, read: $read\n";
&lazy_file_create("/opt/pos/common/tmp/logout.txt",1,$machine);}else{if(length($read)>16){$read=substr($read,0,15);}if(length($read)==16){print STDERR"### login!!!, read: $read\n";
_send_to_ui('BARCODE',$read);
$read="";}}}sleep 0.2;}}else{print STDERR"Error (2) on $info->{Device}\n";}};
if($@){print STDERR"Error (2) on $info->{Device} - $@\n";}}sub read_magnetic_card{my($daemon_name,$machine,$devices)=@_;
my$db=$machine->database();
$machine->config()->__cache("");
$devices=$machine->config()->devices_by_type($daemon_name);
my$cfg=$machine->config()->load_fixed();
$cfg->{HARDWARE_DEBUG}||=0;
$devices||=[];
my$num;
my$info;
my$valid=undef;
my%mappings=(176=>'0',177=>'1',178=>'2',179=>'3',180=>'4',181=>'5',182=>'6',183=>'7',184=>'8',185=>'9',);
foreach my $d(@$devices){$num=$d->{num};
$info=$d->{device};
$info->{Inactive}||=0;
if(!exists$info->{Device}||!$info->{Device}||$info->{Inactive}){print STDERR"Device $num: Missing 'Device' parameter or 'Inactive' device... skipping...\n";
next;}print STDERR"Device $num - WILL USE: ".$info->{Model}." - ".$info->{Device}."\n";
$valid=1;
last;}if(!defined$valid){print STDERR"Didn't find any valid device, aborting...\n";
return;}print STDERR"> read_magnetic_card - ".$info->{Device}."\n";
$info->{InitSystemCommand}||="";
$info->{UseTrack}||=2;
$info->{Baudrate}||='9600';
$info->{Parity}||='none';
$info->{Databits}||='8';
$info->{Stopbits}||='1';
$info->{RtsCts}||=0;
$info->{DtrDsr}||=0;
$info->{Handshake}||="";
eval{my$PortObj=Device::SerialPort->new($info->{Device});
if(defined$PortObj){$PortObj->baudrate($info->{Baudrate})if$info->{Baudrate};
$PortObj->parity($info->{Parity})if$info->{Parity};
$PortObj->databits($info->{Databits})if$info->{Databits};
$PortObj->stopbits($info->{Stopbits})if$info->{Stopbits};
$PortObj->rts_active($info->{RtsCts})if$info->{RtsCts};
$PortObj->dtr_active($info->{DtrDsr})if$info->{DtrDsr};
$PortObj->handshake($info->{Handshake})if$info->{Handshake};
if(exists$info->{MatchMask}){if(ref$info->{MatchMask}eq 'HASH'){my$tmp=delete$info->{MatchMask};
push@{$info->{MatchMask}},$tmp;}}else{$info->{MatchMask}||=[];}while(1){my($count_in,$string_in)=$PortObj->read(255);
if($cfg->{HARDWARE_DEBUG}){print STDERR"####### count_in: $count_in, string_in: #$string_in#\n";}my$final_str="";
if($count_in>0&&$string_in){my@a=split//,$string_in;
for(@a){my$c=ord($_);
if(exists$mappings{$c}){$final_str.=$mappings{$c};}else{if($c>=32&&$c<=126){$final_str.=$_;}}}$string_in=$final_str;}my$i=&_decode_card_data($string_in);
if($i->{type}eq 'card'&&exists$i->{$info->{UseTrack}}){my$cnt=$i->{$info->{UseTrack}};
$cnt=~s/\n//g;
$cnt=~s/\r//g;
if($cnt=~/^\d+$/){$cnt=int($cnt);}if(scalar@{$info->{MatchMask}}){foreach my $mask(@{$info->{MatchMask}}){my$pattern=$mask->{Pattern};
my$customer=$mask->{Customer};
if($cnt=~/$pattern/){print STDERR"######> $pattern - fez match ($customer)\n";
if(uc($customer)eq '@TABLE_MATCH@'){my$filter=$1;
print STDERR"TABLE_MATCH: $filter\n";
my$rec=$db->select(qq{
                    SELECT id
                    FROM customers
                    WHERE deleted = 0 AND (id = ? OR card_number = ?)
                    ORDER BY deleted
                  },$filter,$filter);
if(!scalar@$rec){print STDERR"Can't find any record in table customers with id or card_number: $filter\n";}else{_send_to_ui('BARCODE',$rec->[0]->{id});}}else{_send_to_ui('BARCODE',$customer);}last;}else{print STDERR"######> $pattern - nao fez match ($customer)\n";}}}else{_send_to_ui('BARCODE',$cnt);}}elsif($i->{type}eq 'dallas'){if($i->{read}eq '1'){print STDERR"### dallas logout\n";
&lazy_file_create("/opt/pos/common/tmp/logout.txt",1,$machine);}else{my$read=$i->{read};
if(length($read)>16){$read=substr($read,0,15);}if(length($read)==16){print STDERR"### dallas login - $read\n";
_send_to_ui('BARCODE',$read);}}}else{if($final_str){_send_to_ui('BARCODE',$final_str);}}sleep 0.2;}}else{print STDERR"Error (3) on $info->{Device}\n";}};
if($@){print STDERR"Error (3) on $info->{Device} - $@\n";}}sub read_barcode{my($daemon_name,$machine,$devices)=@_;
$machine->config()->__cache("");
$devices=$machine->config()->devices_by_type($daemon_name);
my$cfg=$machine->config()->load_fixed();
$cfg->{HARDWARE_DEBUG}||=0;
$devices||=[];
my$num;
my$info;
my$valid=undef;
my%mappings=(176=>'0',177=>'1',178=>'2',179=>'3',180=>'4',181=>'5',182=>'6',183=>'7',184=>'8',185=>'9',);
foreach my $d(@$devices){$num=$d->{num};
$info=$d->{device};
$info->{Inactive}||=0;
if(!exists$info->{Device}||!$info->{Device}||$info->{Inactive}){print STDERR"Device $num: Missing 'Device' parameter or 'Inactive' device... skipping...\n";
next;}print STDERR"Device $num - WILL USE: ".$info->{Device}."\n";
$valid=1;
last;}if(!defined$valid){print STDERR"Didn't find any valid device, aborting...\n";
return;}print STDERR"> read_barcode - ".$info->{Device}."\n";
my$chars_ignore_before=exists$info->{NumCharsToIgnoreBefore}&&defined$info->{NumCharsToIgnoreBefore}?$info->{NumCharsToIgnoreBefore}:0;
my$chars_ignore_after=exists$info->{NumCharsToIgnoreAfter}&&defined$info->{NumCharsToIgnoreAfter}?$info->{NumCharsToIgnoreAfter}:0;
$info->{Baudrate}||='9600';
$info->{Parity}||='none';
$info->{Databits}||='8';
$info->{Stopbits}||='1';
$info->{RtsCts}||=0;
$info->{DtrDsr}||=0;
eval{my$PortObj=Device::SerialPort->new($info->{Device});
if(defined$PortObj){$PortObj->baudrate($info->{Baudrate});
$PortObj->parity($info->{Parity});
$PortObj->databits($info->{Databits});
$PortObj->stopbits($info->{Stopbits});
$PortObj->rts_active($info->{RtsCts});
$PortObj->dtr_active($info->{DtrDsr});
my$final_str="";
my$ended=0;
while(1){my($count_in,$string_in)=$PortObj->read(255);
if($cfg->{HARDWARE_DEBUG}){print STDERR"####### count_in: $count_in, string_in: #$string_in#\n";}if($count_in>0&&$string_in){my@a=split//,$string_in;
for(@a){my$c=ord($_);
next if$c==13;
if($c==10){$ended=1;}else{if(exists$mappings{$c}){$final_str.=$mappings{$c};}else{if($c>=32&&$c<=126){$final_str.=$_;}}}}}else{$ended=1 if$final_str;}if($ended==1&&$final_str ne ''){if($chars_ignore_after){$final_str=substr($final_str,0,length($final_str)-$chars_ignore_after);}if($chars_ignore_before){$final_str=substr($final_str,$chars_ignore_before,length($final_str));}if($cfg->{HARDWARE_DEBUG}){print STDERR"   will send to ui: #$final_str#\n";}_send_to_ui('BARCODE',$final_str);
$ended=0;
$final_str="";}sleep 0.2;}}else{print STDERR"Error (4) on $info->{Device}\n";}};
if($@){print STDERR"Error (4) on $info->{Device} - $@\n";}}sub read_scale{my($daemon_name,$machine,$devices)=@_;
$machine->config()->__cache("");
$devices=$machine->config()->devices_by_type($daemon_name);
my$cfg=$machine->config()->load_fixed();
$cfg->{HARDWARE_DEBUG}||=0;
$devices||=[];
my$num;
my$info;
my$valid=undef;
foreach my $d(@$devices){$num=$d->{num};
$info=$d->{device};
$info->{Inactive}||=0;
if(!exists$info->{Device}||!$info->{Device}||$info->{Inactive}){print STDERR"Device $num: Missing 'Device' parameter or 'Inactive' device... skipping...\n";
next;}print STDERR"Device $num - WILL USE: ".$info->{Model}." - ".$info->{Device}."\n";
$valid=1;
last;}if(!defined$valid){print STDERR"Didn't find any valid device, aborting...\n";
return;}print STDERR"> read_scale -> ".$info->{Device}."\n";
$info->{Model}||='';
die"Missing 'Model' parameter..." unless$info->{Model};
my$class="App::POS::Hardware::Scales::".$info->{Model};
my$obj=$class->new(machine=>$machine,device=>$info->{Device},info=>$info,debug_readings=>$cfg->{HARDWARE_DEBUG},);
$obj->start();}sub printers_register{my($machine,$file)=@_;
print STDERR"> printers_register - $file\n";
my$cfg=$machine->config()->load_fixed();
$cfg->{STATION_NUMBER}||=0;
my($station)=$file=~/printers_register#(\d+)\./;
if($station!=$cfg->{STATION_NUMBER}){return 2;}my$dir_server_tmp=$machine->server_dir_tmp();
my$register_file="$dir_server_tmp/printers_register#".$station.".txt";
my$devices=$machine->config()->devices_by_type("Printers");
print STDERR"Will register printers for station $station...\n";
&_register_printers($machine,$devices,1);}sub server_monitor{my($daemon_name,$machine,$devices)=@_;
my$config=$machine->config()->load();
$config->{STATION_NUMBER}||=0;
$devices=$machine->config()->devices_by_type("Printers");
local$SIG{ALRM}=sub{die"alarm\n"};
my$reg_file=$machine->dir_server().'/server/data/registered_stations/'.$config->{STATION_NUMBER};
my$register_printers=0;
my$was_in_error=0;
my$count=0;
while(1){print STDERR"Loop...\n";
if(-e"/tmp/server_error.txt"){print STDERR"Server error, will try to mount server...\n";
$was_in_error=1;
eval{alarm(3);
$machine->mount_server();
alarm(0);};
if($@){alarm(0);}}else{if($was_in_error){print STDERR"Was in error, will register station and printers...\n";
if(!-e$reg_file){eval{alarm(3);
$machine->register_on_server();
alarm(0);};
if($@){print STDERR"ERROR ON register_on_server()\n";}else{$register_printers=1;}}if($register_printers){print STDERR"Will register printers for station ".$config->{STATION_NUMBER}."...\n";
&_register_printers($machine,$devices,1);
foreach my $device(@$devices){my$num=$device->{num};
print STDERR"Will register device $num...\n";
my$info=$device->{device};
$info->{Device}||="";
next if uc($info->{Device})eq '@SCREEN@';
&_handle_printer($machine,$config,$num,$info,$config->{STATION_NUMBER});}$register_printers=0;}$was_in_error=0;}else{my$db=$machine->database();
if($count++ ==10){eval{alarm(3);
$machine->register_on_server();
alarm(0);};
if($@){print STDERR"ERROR ON register_on_server()\n";}$count=0;}}}sleep 3;}}sub files_monitor{my($daemon_name,$machine)=@_;
$machine->config()->__cache("");
my$config=$machine->config()->load();
print STDERR"> files_monitor\n";
my%REGEXPS=();
foreach my $filter(keys%EXECUTE_FILTERS){my$Regex=qr/$filter/;
$REGEXPS{$filter}=$Regex;}my@folders_to_monitor=();
my@folders_cleanup=();
my$pt_certified_software=-e"/opt/pos/common/skins/".$config->{LANG}."/sign_server"?1:0;
if($pt_certified_software){my$cmd="";
if($machine->is_64_bits()){$cmd="/opt/pos/common/skins/".$config->{LANG}."/sign_server.src --saft --cli";}else{$cmd="/opt/pos/common/skins/".$config->{LANG}."/sign_server --saft --cli";}my$info=`$cmd`;
chop($info);
my@toks=split/#/,$info;
$SOFTWARE_INFO=\@toks;}{my$close_hour=$config->{END_BUSINESS_HOUR}||"05:00";
$CLOSE_HOUR=$close_hour;
my@tmp=split/:/,$close_hour;
$CLOSE_HOUR.=":00" if scalar@tmp==2;}$STATION_NAMES=_station_names($machine);
if($machine->is_station()){$config->{STATION_NUMBER}||=0;
push@folders_cleanup,$machine->dir_tmp();
push@folders_to_monitor,$machine->dir_tmp();}else{my$db=$machine->database();
$db->do("UPDATE counters SET locked = 0 WHERE locked != 0");
push@folders_cleanup,$machine->dir_tmp();
push@folders_cleanup,$machine->server_dir_tmp();
push@folders_to_monitor,$machine->dir_tmp();
push@folders_to_monitor,$machine->dir_areas();
push@folders_to_monitor,$machine->server_dir_tmp();
$config->{CAN_DELIVER_PRODUCT_NAME}||="";
if($config->{CAN_DELIVER_PRODUCT_NAME}ne ''){my$prod=$db->select("SELECT id FROM products WHERE name = ? AND deleted = 0",$config->{CAN_DELIVER_PRODUCT_NAME});
if(scalar@$prod){$CAN_DELIVER_PRODUCT_ID=$prod->[0]->{id};}}}$config->{TAXFREE_CLASS}||="";
push@folders_to_monitor,'/home/pos/Downloads' if-d '/home/pos/Downloads'&&$config->{TAXFREE_CLASS};
print STDERR Dumper(\@folders_cleanup);
print STDERR Dumper(\@folders_to_monitor);
eval{&find(sub{return unless/\.txt$/||/^#db#/;
return if/printer_ignore/;
return if/queue\.txt$/;
return if/money_transit\.txt$/;
return if/printer_redirect/;
my$st=unlink($File::Find::name);
print STDERR"####> CLEANUP: $File::Find::name - $_ - $st\n";},@folders_cleanup);};
if($@){print STDERR"ERROR IN CLEANUP: $@\n";}my$a_info=$machine->config()->areas_info();
%AREAS_INFO=%$a_info;
foreach my $area_num(keys%AREAS_INFO){my$arr=$AREAS_INFO{$area_num};
if(scalar@$arr>=5){if($arr->[4]eq 'delivery'){$DELIVERY_AREAS{$area_num}=1;}elsif($arr->[4]eq 'splits'){$SPLIT_AREA_IDX=$area_num;}}}{my$happy_hour=$config->{AREA_HAPPY_HOUR}||[];
if(scalar@$happy_hour){my%names_to_number=();
for(keys%AREAS_INFO){$names_to_number{$AREAS_INFO{$_}->[0]}=$_;}for(@$happy_hour){my@toks=split/,/;
my$area_num=$names_to_number{$toks[0]};
if(defined$area_num){my$price=$toks[2];
my$vat=$toks[3];
$HAPPYHOUR_INFO{$area_num}=[$price,$vat];}}}}&get_cashier_names($machine);
my$fname=$machine->dir_server()."/server/data/areas/tables_to_cards.txt";
if(-e$fname){if(open(F,"< $fname")){while(<F>){chop;
my@t=split/=/,$_;
next unless defined$t[1];
my@t2=split/,/,$t[1];
for my $t(@t2){$TABLES_TO_CARDS{$t}=$t[0];}}close(F);}}my$printers=$machine->printers();
$printers||=[];
for(my$idx=0;$idx<scalar@$printers;$idx++){my$prn=$printers->[$idx];
$prn->{Main}||=0;
if($prn->{Main}){$STATION_MAIN_PRINTER=$idx;}}$config->{PRINT_RECEIPT_PRINTER_VIAS}||=[];
if(exists$config->{DECIMAL_PLACES}&&$config->{DECIMAL_PLACES}ne ''){$DECIMAL_PLACES=$config->{DECIMAL_PLACES};}$CFG_LANG_TRANSLATION=$config->{LANG_TRANSLATION};
eval{for(@{$config->{PRINT_RECEIPT_PRINTER_VIAS}}){my@a=split/,/;
my@b=split/\-/,$a[0];
if(scalar@a>=3){$RECEIPT_PRINT_VIAS_WHERE_TO_APPLY{$b[0]}=$a[2];}$RECEIPT_PRINT_VIAS{$b[0]}=$a[1];}};
print STDERR"WILL USE readir() TO MONITOR FILES...\n";
my$db=$machine->database();
if($machine->is_server()){my$t1=new Benchmark;
my$t2;
_fix_sequences($db);
$t2=new Benchmark;
print STDERR"_fix_sequences> took: ".timestr(timediff($t2,$t1))."\n";
db_table_fix_dup_ids($db,'groups');
db_table_fix_dup_ids($db,'products');
db_table_fix_dup_ids($db,'customers');
db_table_fix_dup_ids($db,'clerks');
db_table_fix_dup_ids($db,'product_menus');
if($pt_certified_software&&$machine->software_version>=202301){_atcud_checks($machine,$config)}}my%to_ignore_cache=();
my$regex1=qr/print#\d+#\d+/;
my$regex2=qr/drawer#\d+#\d/;
my$regex3=qr/\/tmp$/;
my$regex4=qr/tables_refresh#\d+\.txt$/;
my$regex5=qr/\.lock$/;
my$regex6=qr/tmp\/opened_windows\//;
my$regex7=qr/\.delivered$/;
&get_products_information($db);
&get_clerks_information($db);
&get_products_translations($db);
my$memc=memcache($machine->server_ip(),'pos_files',\%MEMC_CONNECTIONS);
my$watcher;
if($machine->is_server()){$memc->flush_all();
$watcher=File::ChangeNotify->instantiate_watcher(directories=>\@folders_to_monitor,);}my$hardlock=`/opt/pos/common/bin/machine_id`;
$memc->set('hardlock',$hardlock);
my$is_first_time=1;
while(1){my@events=();
eval{local$SIG{ALRM}=sub{die"alarm\n"};
alarm(3);
if(!$is_first_time){if($machine->is_server()){if(-e"/boot/config.txt"){sleep 0.2;}else{@events=$watcher->wait_for_events();
sleep 0.1;}}else{sleep 0.5;}}alarm(0);};
if($machine->is_server()&&!$is_first_time){if(!scalar@events){sleep 0.1;
next;}}my@server_files=();
my@files=();
find({wanted=>sub{my$full_path=$File::Find::name;
return unless-f$full_path;
return if$full_path=~/$regex6/;
my$fileName=$_;
my@stat=stat($full_path);
if($machine->is_server()){if($fileName=~/$regex1/||$fileName=~/$regex2/||$fileName=~/$regex4/){my$last_mod=time-$stat[8];
if($last_mod>=240){unlink($full_path);
return;}}elsif($fileName=~/$regex7/||$fileName=~/$regex5/){my$last_mod=time-$stat[9];
if($last_mod>259200){unlink($full_path);}}my$dir1=$machine->dir_areas();
my$dir2=$machine->server_dir_tmp();
if($is_first_time){if($File::Find::dir=~/^$dir1/){push@server_files,$File::Find::name;}elsif($File::Find::dir=~/^$dir2/){push@server_files,$File::Find::name;}}else{my$last_mod_secs=time-$stat[9];
if($last_mod_secs<=15){if($File::Find::dir=~/^$dir1/){push@server_files,$File::Find::name;}elsif($File::Find::dir=~/^$dir2/){push@server_files,$File::Find::name;}}}}if(exists$to_ignore_cache{$fileName}){if($File::Find::dir=~/$regex3/){my$last_mod=time-$stat[8];
if($last_mod>=240){unlink($full_path);}}return;}push@files,$File::Find::name;}},@folders_to_monitor);
$is_first_time=0;
if($machine->is_server()){$memc->set('server_files',\@server_files,10);}else{my@myfiles=$memc->get('server_files');
if(scalar@myfiles){my$f=$myfiles[0];
@server_files=@$f;
for(@server_files){$_=~s/\/opt\/pos/\/shares\/pos/;
push@files,$_;}}}my@hold_files=();
my@close_files=();
my@final_files=();
for(@files){if(/\d+\.hold$/){push@hold_files,$_;}elsif(/\d+\.close$/){push@close_files,$_;}else{push@final_files,$_;}}@files=();
push@files,@hold_files if scalar@hold_files;
push@files,@final_files if scalar@final_files;
push@files,@close_files if scalar@close_files;
if($DEBUG_FILES){foreach my $file(@files){if(-e$file){my@st=stat($file);
my$str=$st[9];
print STDERR"FILE> ".sprintf("%-17s",$st[9])." - $file\n";}}print STDERR"--------------------------------------------------\n" if scalar@files;}&_apply_regexp_rules($machine,\@files,\%REGEXPS,\%to_ignore_cache);}}sub _refresh_gambas_caches{my$machine=shift;
my$dir_server_tmp=$machine->server_dir_tmp();
my$dir_registered_stations=$machine->dir_server().'/server/data/registered_stations';
write_file("$dir_server_tmp/data_refresh#0.txt","1");
my@stations=read_dir($dir_registered_stations);
for(@stations){write_file("$dir_server_tmp/data_refresh#".$_.".txt","1");}}sub _apply_regexp_rules{my($machine,$files,$regexps,$cache)=@_;
my$did_one_filter=0;
my$filter_idx=0;
foreach my $full_path(@$files){if($DEBUG_FILES){my$fsize=-s$full_path;
$fsize||=0;
print STDERR"FILE: $full_path, SIZE: $fsize\n";}my$fileName=&basename($full_path);
foreach my $filter(keys%$regexps){my$re=$regexps->{$filter};
if($fileName=~/$re/){$did_one_filter=1;
no strict 'refs';
my$ret=0;
eval{$ret=&{"$EXECUTE_FILTERS{$filter}"}($machine,$full_path);};
$ret||=0;
if($@){print STDERR"### filter: $filter\n";
print STDERR"AN ERROR OCURRED, saved in /root/ERRORS.log!!!! - $@\n";
open(Y,">> /root/ERRORS.log");
print Y"##################################################\n";
print Y$@."\n";
close(Y);}if($ret!=2){unlink($full_path);}}$filter_idx++;}if($did_one_filter==0){$cache->{$fileName}=1;}}}sub server_sql_query{my($machine,$file)=@_;
if($machine->is_server()){if(open(F,"< $file")){local$/=undef;
my$query=<F>;
close(F);
my$db=$machine->database();
$db->do($query)if defined$query&&$query;}}else{return 2;}}sub keyboard_process{my($machine,$file)=@_;
print STDERR"> keyboard_process - $file\n";
if(open(F,"< $file")){local$/=undef;
my$cnt=<F>;
close(F);
if(length($cnt)>0){$cnt=~s/[^A-Za-z0-9]+//g;
$cnt='*LOGOUT*' if$cnt eq '0';
print STDERR"Got: #$cnt#\n";
if($cnt ne ''){_send_to_ui('BARCODE',$cnt);}}}}sub register_cashdrawer_open{my($machine,$file)=@_;
if($machine->is_server()){if(open(F,"< $file")){my($date,$station,$clerk,$clerk_name,$business_date)=("","","","","");
while(<F>){chop;
if($_){my@a=split/#/;
($date,$station,$clerk,$clerk_name,$business_date)=($a[0],$a[1],$a[2],$a[3],$a[4]);}}close(F);
my$cfg=$machine->config()->load_fixed();
$cfg->{CASHDRAWER_OPENS_REGISTER}||=0;
$cfg->{INTERNET_DASHBOARD}||=0;
if($date&&$cfg->{CASHDRAWER_OPENS_REGISTER}){my$db=$machine->database();
my$id=$db->next_id("cashdrawer_opens");
$db->do("INSERT INTO cashdrawer_opens VALUES (?, ?, ?, ?, ?, ?)",$id,$date,$station,$clerk,$clerk_name,$business_date);
if($cfg->{INTERNET_DASHBOARD}){open(F,"> /opt/pos/server/data/tmp/dashboard.txt");
print F"1\n";
close(F);}}}return 1;}else{return 2;}}sub execute_cash_drawer{my($machine,$file)=@_;
my$server_dir_tmp=$machine->server_dir_tmp();
my$server_dir_data=$machine->server_dir_data();
my$db=$machine->database();
&get_clerks_information($db);
my$cfg=$machine->config()->load_fixed();
$cfg->{Hardware}->{CashDrawers}->{Device}||=[];
my($station,$clerk,$manual,$pnum,$drawer_num,$clerk_decoded,$business_date)=$file=~/\/drawer#(\d+)#(\d+)#(\d+)#(\d+)#(\d+)#(\d+)#(.*?)#/;
return unless defined$station&&defined$clerk;
my$station_original=$station;
if($station>80){$station=0;}if($station!=$cfg->{STATION_NUMBER}){return 2;}$manual||=0;
my$date=&strftime("%Y-%m-%d %H:%M:%S",localtime);
my$clerk_name=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'UNKNOWN';
my$ret_code=1;
if(!$clerk_decoded){$ret_code=&clerk_cashdrawer_decode($machine,$station_original,$clerk,$manual,$pnum);}return unless defined$ret_code;
$cfg->{REDIRECT_TO_NETWORK_PRINTERS_BY_STATION}||={};
my$ip_redirect="";
my$redirect_key='REDIRECT_TO_NETWORK_PRINTERS_BY_STATION';
if(exists$cfg->{$redirect_key}->{$station_original}){$ip_redirect=$cfg->{$redirect_key}->{$station_original};}my$did_open=0;
$cfg->{Hardware}->{CashDrawers}->{Device}||=[];
my$devices=$cfg->{Hardware}->{CashDrawers}->{Device};
if($pnum eq '9999'){$cfg->{Hardware}->{CashDrawers}->{Device}||=[];
my$cd=$cfg->{Hardware}->{CashDrawers}->{Device}||=[];
my$dnum=$drawer_num-1;
my$info=$cd->[$dnum];
if(!defined$info){print STDERR"INVALID DRAWER NUMBER - $drawer_num\n";
return;}$drawer_num=1;
$devices=[$info];}my$was_ip_redirected=0;
if($ip_redirect){my$printer=App::POS::Printer->new(driver=>'EpsonTM88');
my$p_class=$printer->get_class();
my$drawer_num=1;
my$method="cashdrawer".$drawer_num."_open";
my$seq=$p_class->$method();
$did_open=0;
my$no_cut=0;
if($ip_redirect=~/android/i){$no_cut=1;
$ip_redirect=~s/android//i;}if(!$no_cut){my($printer_ip,$printer_port)=_network_printer_decode($ip_redirect);
my$sock=_get_socket($printer_ip,$printer_port);
if(defined$sock){print$sock $seq;
close($sock);
sleep 0.4;
$did_open=1;
$was_ip_redirected=1;}else{$machine->send_alert_to_ui(&translate($machine,"PRINTER ERROR"." - $ip_redirect"));
return;}}}if(!$was_ip_redirected){my$devices_idx=-1;
foreach my $info(@{$devices}){$info->{Device}||="";
$info->{Printer}||="";
$info->{Script}||="";
$info->{Inactive}||=0;
$devices_idx++;
if($info->{Inactive}){next;}if($info->{Device}&&$pnum!=99&&$pnum!=9999){eval{my$PortObj=Device::SerialPort->new($info->{Device});
if(defined$PortObj){$PortObj->baudrate(9600);
$PortObj->parity("even");
$PortObj->databits(8);
$PortObj->stopbits(1);
$PortObj->handshake("none");
$PortObj->write(chr(0x05));
$did_open=1;}else{print STDERR"Error (5) on $info->{Device}\n";}};
if($@){print STDERR"Error (5) on $info->{Device} - $@\n";}}if($info->{Script}){if(!-e$info->{Script}){print STDERR"CAN'T FIND SCRIPT - ".$info->{Script}."\n";}else{$did_open=1;
system($info->{Script});}}if($info->{Printer}){my$printer_num=$info->{Printer}-1;
$printer_num=0 if$printer_num<0;
if($pnum!=99&&$pnum!=9999){$printer_num=$pnum;}my@files=&read_dir($server_dir_data);
my$redirected=0;
foreach my $file(@files){if(my($station_to,$printer_to)=$file=~/^printer_redirect#$station#$printer_num#(\d+)#(\d+)\./){my$r=int(rand(10000));
my$f=$server_dir_tmp."/drawer#$station_to#$clerk#$manual#$printer_to#".time."$r.txt";
print STDERR"REDIRECT OPEN CASHDRAWER - $f\n";
$redirected=1;
if(open(F,"> $f")){print F"1\n";}else{print STDERR"ERROR: Can't write to - $f\n";}}}if(!$redirected){my$pp=$cfg->{Hardware}->{Printers}->{Device}->[$printer_num];
my$info=$pp;
if(defined$pp&&!$was_ip_redirected){$pp->{Inactive}||=0;
if($pp->{Inactive}){foreach my $prn(@{$cfg->{Hardware}->{Printers}->{Device}}){$prn->{Device}||="";
$prn->{Inactive}||=0;
$prn->{Main}||=0;
next if$prn->{Device}=~/^.SCREEN.$/;
if(!$prn->{Inactive}&&$prn->{Main}){$pp=$prn;
$info=$prn;
last;}}}my$driver=$info->{Driver}||'';
my$printer=App::POS::Printer->new(driver=>$driver);
my$p_class=$printer->get_class();
$info->{Device}||="";
$info->{IP}||="";
$info->{CupsQueue}||="";
my$method="cashdrawer".$drawer_num."_open";
my$seq=$p_class->$method();
if($info->{CupsQueue}){$did_open=1;
my$r=$$."_".int(rand(10000));
my$file="/tmp/$r.txt";
open(F,"> $file");
print F$seq;
close(F);
system("lp -d \"".$info->{CupsQueue}."\" $file");
unlink($file);}elsif($info->{IP}){my($printer_ip,$printer_port)=_network_printer_decode($info->{IP});
my$sock=_get_socket($printer_ip,$printer_port);
if(defined$sock){print$sock $seq;
$did_open=1;
$sock->close();
sleep 0.4;}else{print STDERR"Could not create socket to ".$info->{IP}.": $!\n";}}elsif($info->{Device}){if(-l$info->{Device}){my$file_link=readlink($info->{Device});
$file_link//="";
return unless$file_link;
if($file_link=~/^\/dev\//&&!-e$file_link){return;}}elsif(!-e$info->{Device}){return;}eval{local$SIG{ALRM}=sub{die"alarm\n"};
alarm(2);
if(open(F,"> ".$info->{Device})){$did_open=1;
print F$seq;
close(F);}else{print STDERR"ERROR: Can't write to ".$info->{Device}.": $!\n";}alarm(0);};
if($@){print STDERR"ERROR ALARM() OPENING CASHDRAWER - $@\n";}}}else{print STDERR"ERROR: printer specified for cashdrawer doesn't exists...\n";}}}}}if($manual==1&&$did_open){my$r=int(rand(10000));
my$new_file=$machine->server_dir_tmp()."/register_cashdrawer_open#".time."$r.txt";
my$cnt=$date."#".$station_original."#".$clerk."#".$clerk_name."#".$business_date."\n";
open(F,"> $new_file");
print F$cnt;
close(F);}}sub activate_new_config{my($machine,$file)=@_;
print STDERR"> activate_new_config - $file\n";
&copy("/opt/pos/etc/config.ini","/opt/pos/etc/config.ini.bak");
&move($file,"/opt/pos/etc/config.ini");
unlink$file;
my$pid=`ps xauf | grep gbr2 | grep -v grep | awk '{print \$2}'`;
if($pid ne ''){system("kill -9 $pid 2> /dev/null");}$pid=`ps xauf | grep gbr3 | grep -v grep | awk '{print \$2}'`;
if($pid ne ''){system("kill -9 $pid 2> /dev/null");}if(open(F,"> /tmp/restart.txt")){print F time."\n";
close(F);}system("/opt/pos/common/bin/restart_hardware_handler.sh");}sub restart_software{my($machine,$file)=@_;
print STDERR"> restart_station - $file\n";
my$cfg=$machine->config()->load_fixed();
my$station=$cfg->{STATION_NUMBER}||"0";
my($station_restart)=$file=~/restart_software#(\d+)\.txt$/;
if($station eq$station_restart){unlink$file;
system("ps xauf | grep gbr3 | grep -v grep | awk '{print \$2}' | xargs kill -9 2> /dev/null");
system("ps xauf | grep gbr2 | grep -v grep | awk '{print \$2}' | xargs kill -9 2> /dev/null");
return 1;}return 2;}sub restart_hardware_handlers{my($machine,$file)=@_;
print STDERR"> restart_hardware_handlers - $file\n";
my$cfg=$machine->config()->load_fixed();
my$station=$cfg->{STATION_NUMBER}||"0";
my($station_restart)=$file=~/restart_hardware_handlers#(\d+)\.txt$/;
if($station eq$station_restart){unlink$file;
system("/opt/pos/common/bin/restart_hardware_handler.sh");
return 1;}return 2;}sub cashier_open{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}my($station,$clerk,$cashier,$amount)=$file=~/cashier_open#(\d+)#(\d+)#(\d+)#(.*?)#/;
print STDERR"> cashier_open - $file, $station, $clerk, $cashier, $amount\n";
my$cfg=$machine->config()->load_fixed();
$cfg->{STATION_NAME}||="";
my$db=$machine->database();
&get_clerks_information($db);
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my%info=();
my$content="";
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{station_name}=$cfg->{STATION_NAME};
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{cashier_number}=$cashier;
$info{amount}=$amount||0;
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('cashier_open.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub cashier_in_out{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}my($station,$clerk,$cashier,$op,$amount)=$file=~/cashier_in_out#(\d+)#(\d+)#(\d+)#(.*?)#(.*?)#/;
print STDERR"> cashier_in_out - $file, $station, $clerk, $cashier, $amount, $op\n";
my$cfg=$machine->config()->load_fixed();
$cfg->{STATION_NAME}||="";
my$db=$machine->database();
&get_clerks_information($db);
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my$justification="";
if(open(F,"< $file")){local$/=undef;
$justification=<F>;
close(F)}my%info=();
my$content="";
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{station_name}=$cfg->{STATION_NAME};
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{justification}=$justification;
$info{cashier_number}=$cashier;
$info{amount}=$amount||0;
my$template;
if($op eq 'in'){$template="cashier_in.tmpl";}else{$template="cashier_out.tmpl";}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template,\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub execute_file{my($machine,$file)=@_;
print STDERR"> execute_file - $file\n";
my$r=int(rand(100000));
my$file_execute="/tmp/execute_$r.txt";
move($file,$file_execute);
system("bash $file_execute");
unlink$file_execute;
return 1;}sub cashier_report_resumed{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}my($station,$clerk,$from,$to,$to_screen,$id)=$file=~/cashier_report_resumed#(\d+)#(\d+)#(.*?)#(.*?)#(.*?)#(\d+)\.txt$/;
print STDERR"> cashier_report_resumed - $file, $station, $clerk, $from, $to, $to_screen, $id\n";
my$cfg=$machine->config()->load_fixed();
$cfg->{STATION_NAME}||="";
my$db=$machine->database();
&get_clerks_information($db);
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my%info=();
my$content="";
my$cashier_info=$db->select(qq{
    SELECT *
    FROM cashiers
    WHERE business_date >= ? AND business_date <= ?
    ORDER BY date_opened
  },$from,$to);
foreach my $rec(@$cashier_info){next unless$rec->{date_opened};
my($hour_opened)=$rec->{date_opened}=~/^\d\d\d\d\-\d\d\-\d\d (.*?)$/;
$hour_opened=substr($hour_opened,0,5);
my$hour_closed="";
if(defined$rec->{date_closed}){($hour_closed)=$rec->{date_closed}=~/^\d\d\d\d\-\d\d\-\d\d (.*?)$/;
$hour_closed=substr($hour_closed,0,5);}$rec->{hour_opened}=$hour_opened;
$rec->{hour_closed}=$hour_closed;
$info{cashiers}->{$rec->{number}}->{from_previous}+=$rec->{from_previous};
$info{cashiers}->{$rec->{number}}->{to_next}+=$rec->{to_next};
$info{cashiers}->{$rec->{number}}->{counted}+=$rec->{counted};
$info{cashiers}->{$rec->{number}}->{sales}+=$rec->{sales};
my@params=($rec->{date_opened},$rec->{number},);
my$sql_aux="";
if($hour_closed){$sql_aux.="AND sh.timestamp <= ?";
push@params,$rec->{date_closed};}my$sales_arr=$db->select(qq{
      SELECT sh.doc_type, sha1.key_value AS invoice_is_payed, sha2.key_value AS to_customer_account,
        sp.payment_media, SUM(sp.value) AS sales
      FROM sales_headers sh
      LEFT JOIN sales_payments sp ON (sp.header = sh.id)
      LEFT JOIN sales_headers_aux sha1 ON (sh.id = sha1.header AND sha1.key_name = 'invoice_is_payed')
      LEFT JOIN sales_headers_aux sha2 ON (sh.id = sha2.header AND sha2.key_name = 'to_customer_account')
      WHERE sh.timestamp >= ? AND sh.cashier = ? $sql_aux AND sh.doc_type IN (1, 2, 4, 5, 8, 90, 12, 7, 13)
      GROUP BY sh.doc_type, sha1.key_value, sha2.key_value, sp.payment_media
    },@params);
my$sales_by_media=[];
my$sales=0;
for(@$sales_arr){if(($_->{doc_type}==7||$_->{doc_type}==13)&&$_->{to_customer_account}){next;}$info{cashiers}->{$rec->{number}}->{sales}+=$_->{sales};
$info{cashiers}->{$rec->{number}}->{sales_by_media}->{$_->{payment_media}}+=$_->{sales};}@params=($rec->{id},$from,);
$sql_aux="";
if($hour_closed){$sql_aux.="AND business_date <= ?";
push@params,$to}my$ins=$db->select(qq{
      SELECT *
      FROM cashier_operations
      WHERE cashier = ? AND operation = 'IN' AND business_date >= ? $sql_aux
      ORDER BY date
    },@params);
for(@$ins){$info{cashiers}->{$rec->{number}}->{ins}->{$_->{description}}+=$_->{amount};
$info{cashiers}->{$rec->{number}}->{total_ins}+=$_->{amount};}my$outs=$db->select(qq{
      SELECT *
      FROM cashier_operations
      WHERE cashier = ? AND operation = 'OUT' AND business_date >= ? $sql_aux
      ORDER BY date
    },@params);
for(@$outs){$info{cashiers}->{$rec->{number}}->{outs}->{$_->{description}}+=$_->{amount};
$info{cashiers}->{$rec->{number}}->{total_outs}+=$_->{amount};}}$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{date_from}=$from;
$info{date_to}=$to;
$info{station}=$station;
$info{station_name}=$cfg->{STATION_NAME};
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_cashiers.tmpl',\%info,\$content,)||die$TEMPLATE->error;
if($to_screen){my$dir_tmp=$machine->server_dir_tmp();
my$cnt_tmp=$content;
$cnt_tmp=~s/\@size_.*?\@//g;
$cnt_tmp=~s/\@paper_cut\@//g;
$cnt_tmp=~s/\@justify_.*?\@//g;
my$server_cfg=$machine->is_server()?$cfg:$machine->server_config->load();
_replace_saft_tags($server_cfg,\$cnt_tmp);
open(F,">> $dir_tmp/printer_screen#$station.txt");
print F$cnt_tmp;
print F"=======================================\n\n\n";
close(F);
return;}return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub cashier_report_detailed{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}my($station,$clerk,$from,$to,$to_screen,$id)=$file=~/cashier_report_detailed#(\d+)#(\d+)#(.*?)#(.*?)#(.*?)#(\d+)\.txt$/;
print STDERR"> cashier_report_detailed - $file, $station, $clerk, $from, $to, $to_screen, $id\n";
my$cfg=$machine->config()->load_fixed();
$cfg->{STATION_NAME}||="";
my$db=$machine->database();
&get_clerks_information($db);
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my%info=();
my$content="";
my$cashier_info=$db->select(qq{
    SELECT *
    FROM cashiers
    WHERE business_date >= ? AND business_date <= ?
    ORDER BY date_opened
  },$from,$to);
my%count=();
foreach my $rec(@$cashier_info){next unless$rec->{date_opened};
my($hour_opened)=$rec->{date_opened}=~/^\d\d\d\d\-\d\d\-\d\d (.*?)$/;
$hour_opened=substr($hour_opened,0,5);
my$hour_closed="";
if(defined$rec->{date_closed}){($hour_closed)=$rec->{date_closed}=~/^\d\d\d\d\-\d\d\-\d\d (.*?)$/;
$hour_closed=substr($hour_closed,0,5);}$rec->{hour_opened}=$hour_opened;
$rec->{hour_closed}=$hour_closed;
my$sql_aux="";
my@params=($rec->{date_opened},$rec->{number},);
if($hour_closed){$sql_aux.="AND sh.timestamp <= ?";
push@params,$rec->{date_closed};}my$sales_arr=$db->select(qq{
      SELECT sh.doc_type, sha1.key_value AS invoice_is_payed, sha2.key_value AS to_customer_account,
        sp.payment_media, SUM(sp.value) AS sales
      FROM sales_payments sp
      LEFT JOIN sales_headers sh ON (sp.header = sh.id)
      LEFT JOIN sales_headers_aux sha1 ON (sh.id = sha1.header AND sha1.key_name = 'invoice_is_payed')
      LEFT JOIN sales_headers_aux sha2 ON (sh.id = sha2.header AND sha2.key_name = 'to_customer_account')
      WHERE sh.timestamp >= ? AND sh.cashier = ? AND sh.doc_type IN (1, 2, 4, 5, 8, 90, 12, 7, 13) $sql_aux
      GROUP BY sh.doc_type, sha1.key_value, sha2.key_value, sp.payment_media
    },@params);
my$sales_by_media=[];
my$sales=0;
for(@$sales_arr){if(($_->{doc_type}==7||$_->{doc_type}==13)&&$_->{to_customer_account}){next;}$info{cashiers}->{$rec->{number}}->{$hour_opened}->{sales}+=$_->{sales};
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{sales_by_media}->{$_->{payment_media}}+=$_->{sales};}$sql_aux="";
@params=($rec->{id},$from,$to,$rec->{date_opened},);
if($hour_closed){push@params,$rec->{date_closed};
$sql_aux=" AND date <= ?";}my$ins=$db->select(qq{
      SELECT *
      FROM cashier_operations
      WHERE cashier = ? AND operation = 'IN' AND business_date >= ? AND business_date <= ? AND
        date >= ? $sql_aux
      ORDER BY date
    },@params);
for(@$ins){$info{cashiers}->{$rec->{number}}->{$hour_opened}->{ins}->{$_->{description}}+=$_->{amount};
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{total_ins}+=$_->{amount};}$info{cashiers}->{$rec->{number}}->{$hour_opened}->{total_ins}||=0;
$sql_aux="";
@params=($rec->{id},$from,$to,$rec->{date_opened});
if($hour_closed){push@params,$rec->{date_closed};
$sql_aux=" AND date <= ?";}my$outs=$db->select(qq{
      SELECT *
      FROM cashier_operations
      WHERE cashier = ? AND operation = 'OUT' AND business_date >= ? AND business_date <= ? AND
        date >= ? AND date <= ?
      ORDER BY date
    },$rec->{id},$from,$to,$rec->{date_opened},$rec->{date_closed});
for(@$outs){$info{cashiers}->{$rec->{number}}->{$hour_opened}->{outs}->{$_->{description}}+=$_->{amount};
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{total_outs}+=$_->{amount};}$info{cashiers}->{$rec->{number}}->{$hour_opened}->{total_outs}||=0;
$count{$rec->{business_date}."#".$rec->{number}}++;
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{shift_number}=$count{$rec->{business_date}."#".$rec->{number}};
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{hour_opened}=$hour_opened;
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{hour_closed}=$hour_closed;
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{from_previous}+=$rec->{from_previous};
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{to_next}+=$rec->{to_next};
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{counted}+=$rec->{counted};
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{from_previous}||=0;
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{to_next}||=0;
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{counted}||=0;
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{sales}||=0;
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{outs}||={};
$info{cashiers}->{$rec->{number}}->{$hour_opened}->{ins}||={};}$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{date_from}=$from;
$info{date_to}=$to;
$info{station}=$station;
$info{station_name}=$cfg->{STATION_NAME};
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_cashiers_detailed.tmpl',\%info,\$content,)||die$TEMPLATE->error;
if($to_screen){my$dir_tmp=$machine->server_dir_tmp();
my$cnt_tmp=$content;
$cnt_tmp=~s/\@size_.*?\@//g;
$cnt_tmp=~s/\@paper_cut\@//g;
$cnt_tmp=~s/\@justify_.*?\@//g;
my$server_cfg=$machine->is_server()?$cfg:$machine->server_config->load();
_replace_saft_tags($server_cfg,\$cnt_tmp);
open(F,">> $dir_tmp/printer_screen#$station.txt");
print F$cnt_tmp;
print F"=======================================\n\n\n";
close(F);
return;}return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub cashier_close{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}my($station,$clerk,$cashier,$counted,$sales,$to_next,$id)=$file=~/cashier_close#(\d+)#(\d+)#(\d+)#(.*?)#(.*?)#(.*?)#(\d+)#/;
print STDERR"> cashier_close - $file, $station, $clerk, $cashier, $counted, $sales, $to_next, $id\n";
my$sales_arr=[];
$sales=my_conv($sales);
if($sales=~/§/){my@tt=split/§/,$sales;
foreach my $v(@tt){my@vv=split/=/,$v;
push@$sales_arr,{media=>$vv[0],amount=>$vv[1],}}}else{push@$sales_arr,{media=>'SALES',amount=>$sales,}}my$cfg=$machine->config()->load_fixed();
$cfg->{STATION_NAME}||="";
$cfg->{CONSUMPTIONS_AFFECT_SALES}||=0;
my$db=$machine->database();
&get_clerks_information($db);
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my%info=();
my$content="";
my$cashier_info=$db->select("SELECT * FROM cashiers WHERE id = ?",$id);
my$ins=$db->select("SELECT * FROM cashier_operations WHERE cashier = ? AND operation = 'IN' ORDER BY date",$id);
my$outs=$db->select("SELECT * FROM cashier_operations WHERE cashier = ? AND operation = 'OUT' ORDER BY date",$id);
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{station_name}=$cfg->{STATION_NAME};
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{cashier_number}=$cashier;
$info{sales}=$sales||0;
$info{sales_arr}=$sales_arr||0;
$info{counted}=$counted||0;
$info{to_next}=$to_next||0;
$info{cashier}=$cashier_info->[0];
$info{ins}=$ins;
$info{outs}=$outs;
my$sales_arr2=[];
if($cfg->{CONSUMPTIONS_AFFECT_SALES}){$sales_arr2=$db->select(qq{
      SELECT sh.doc_type, sha1.key_value AS invoice_is_payed, sha2.key_value AS to_customer_account,
        sha3.key_value AS canceled,
        sp.payment_media, SUM(sp.value) AS sales
      FROM sales_headers sh
      LEFT JOIN sales_payments sp ON (sp.header = sh.id)
      LEFT JOIN sales_headers_aux sha1 ON (sh.id = sha1.header AND sha1.key_name = 'invoice_is_payed')
      LEFT JOIN sales_headers_aux sha2 ON (sh.id = sha2.header AND sha2.key_name = 'to_customer_account')
      LEFT JOIN sales_headers_aux sha3 ON (sh.id = sha3.header AND sha3.key_name = 'canceled')
      WHERE sh.timestamp >= ? AND sh.timestamp <= ? AND sh.doc_type IN (1, 2, 4, 5, 8, 90, 12, 7, 13) AND sh.cashier = ?
      GROUP BY sh.doc_type, sha1.key_value, sha2.key_value, sha3.key_value, sp.payment_media
    },$info{cashier}->{date_opened},$info{cashier}->{date_closed},$info{cashier}->{number});}else{$sales_arr2=$db->select(qq{
      SELECT sh.doc_type, sha1.key_value AS invoice_is_payed, sha2.key_value AS to_customer_account,
        sha3.key_value AS canceled,
        sp.payment_media, SUM(sp.value) AS sales
      FROM sales_headers sh
      LEFT JOIN sales_payments sp ON (sp.header = sh.id)
      LEFT JOIN sales_headers_aux sha1 ON (sh.id = sha1.header AND sha1.key_name = 'invoice_is_payed')
      LEFT JOIN sales_headers_aux sha2 ON (sh.id = sha2.header AND sha2.key_name = 'to_customer_account')
      LEFT JOIN sales_headers_aux sha3 ON (sh.id = sha3.header AND sha3.key_name = 'canceled')
      WHERE sh.timestamp >= ? AND sh.timestamp <= ? AND sh.doc_type IN (1, 2, 5, 8, 90, 12, 7, 13) AND sh.cashier = ?
      GROUP BY sh.doc_type, sha1.key_value, sha2.key_value, sha3.key_value, sp.payment_media
    },$info{cashier}->{date_opened},$info{cashier}->{date_closed},$info{cashier}->{number});}my$sales_by_media=[];
for(@$sales_arr2){$_->{canceled}||=0;
if(($_->{doc_type}==7||$_->{doc_type}==13)&&$_->{to_customer_account}||$_->{canceled}){next;}$info{sales_by_media}->{$_->{payment_media}}+=$_->{sales};}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('cashier_close.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub execute_command{my($machine,$file)=@_;
my($station)=$file=~/execute_command\-(\d+)\./;
print STDERR"> execute_command - $station\n";
my$db=$machine->database();
my$cfg=$machine->config()->load_fixed();
if($station ne$cfg->{STATION_NUMBER}){return 2;}open(F,"< $file");
local$/=undef;
my$cmd=<F>;
$cmd=~s/\n//;
$cmd=~s/\r//;
close(F);
print STDERR"WILL EXECUTE: $cmd\n";
system($cmd);
print STDERR"DONE!\n";}sub print_taxfree_file{my($machine,$file)=@_;
print STDERR"Will print taxfree file - $file\n";
my$tmp_file="/tmp/".time."_".int(rand(100000)).".pdf";
move($file,$tmp_file);
system("lp $tmp_file");}sub print_shelf_label{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}my($station,$clerk,$count)=$file=~/print_shelf_label#(\d+)#(\d+)#(\d+)#/;
print STDERR"> print_shelf_label - $file, $station, $clerk, $count\n";
return unless$count;
if(!-e"/tmp/label_final.bmp"){print STDERR"Can't find file /tmp/label_final.bmp... did you generate it?\n";
return;}my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
my$main_file=$machine->dir_server()."/server/data/registered_printers/".$station."/main";
if(!-e$main_file){print STDERR"Can't find main file for station $station ($main_file)\n";
print STDERR"CHECK IF THAT STATION HAS A 'Main' PRINTER!\n";}else{my$printer=read_file($main_file);
chop($printer);
print STDERR"Will load BMP...\n";
my$o=App::POS::Hardware::PrinterLogo->new(image=>"/tmp/label_final.bmp",use_cache=>0);
my$cnt=$o->image_data();
$cnt.=chr(0x1B).chr(0x32);
$cnt.=chr(0x1B).chr(0x64).chr(0x06).chr(0x1B).chr(0x69);
print STDERR"Done loading BMP...\n";
for(my$i=0;$i<$count;$i++){my$r=int(rand(100000));
my$print_file=$machine->server_dir_tmp()."/print#$station#".$printer."#0#binary".time."$r.txt";
open(F,"> $print_file");
binmode(F);
print F$cnt;
close(F);}print STDERR"Done\n";}return;}sub print_barcode_label{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}my@tok=split/#/,$file;
my($station,$clerk,$barcode,$count,$product_id,$tmplf)=("","","","","","");
if(scalar@tok==8){($station,$clerk,$barcode,$count,$product_id,$tmplf)=$file=~/print_barcode_label#(\d+)#(\d+)#(.*?)#(\d+)#(\d+)#(.*?)#/;}else{($station,$clerk,$barcode,$count,$product_id)=$file=~/print_barcode_label#(\d+)#(\d+)#(.*?)#(\d+)#(\d+)#/;}if($barcode=~/,/){my@multibarcodes=split/\,/,$barcode;
my$singlebarcode=$multibarcodes[-1];
$barcode=$singlebarcode;}print STDERR"> print_barcode_label - $file, $station, $clerk, $barcode, $count, $product_id, $tmplf\n";
my%prods_by_qtty=();
if($tmplf=~/\.glabels$/i&&-e"/tmp/glabels.products.txt"){my$cnt=read_file("/tmp/glabels.products.txt");
$cnt||="";
unlink"/tmp/glabels.products.txt";
if($cnt){my@rec=split/#/,$cnt;
foreach my $r(@rec){my@rec2=split/=/,$r;
push@{$prods_by_qtty{$rec2[1]*1}},$rec2[0];}}
}return unless$count||scalar keys%prods_by_qtty;
my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
$tmplf||="";
my$cmd="";
if($tmplf=~/\.glabels$/i){my$dir_server_data=$machine->server_dir_data();
my$glabels="/usr/bin/glabels-3-batch";
$glabels="/usr/bin/glabels-batch-qt" if-e"/usr/bin/glabels-batch-qt";
if(scalar%prods_by_qtty){foreach my $qty(keys%prods_by_qtty){my$ids=join(",",@{$prods_by_qtty{$qty}});
$cmd="$dir_server_data/glabels/gen_products.sh \"\" \"\" \"$ids\"";
system("$cmd");
unlink"/tmp/glabels.$station.pdf";
system("$glabels -o /tmp/glabels.$station.pdf $dir_server_data/glabels/$tmplf");
if(-e"/tmp/glabels.$station.pdf"){for(my$i=0;$i<$qty;$i++){system("lp /tmp/glabels.$station.pdf");}}}}else{my@t=split/\@/,$tmplf;
my$cmd="";
my$label="";
if(scalar@t==2){$cmd="$dir_server_data/glabels/gen_products.sh \"\" $t[0]";
$label=$t[1];}else{$cmd="$dir_server_data/glabels/gen_products.sh $product_id";
$label=$t[0];}system("$cmd");
unlink"/tmp/glabels.$station.pdf";
system("$glabels -o /tmp/glabels.$station.pdf $dir_server_data/glabels/$label");
if(-e"/tmp/glabels.$station.pdf"){for(my$i=0;$i<$count;$i++){system("lp /tmp/glabels.$station.pdf");}}}return;}&get_clerks_information($db);
&get_products_information($db);
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my%info=();
my$content="";
my$label_system=$cfg->{BARCODE_LABEL_SYSTEM}||"";
$label_system='' if$label_system&&uc($label_system)eq 'DEFAULT';
$label_system=$tmplf if$tmplf;
my$tmpl='print_barcode_label.tmpl';
if($label_system){if(!-e"/opt/pos/common/print_templates/labels/".$label_system.".tmpl"){print STDERR"ERROR! Can't find - /opt/pos/common/print_templates/labels/".$label_system.".tmpl\n";
return;}$tmpl="labels/".$label_system.".tmpl";}$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{product}=$PRODUCTS_INFO->{$product_id};
$barcode//="";
if($barcode){$info{product}->{barcode}=$barcode;}my$extra_data=$db->select("SELECT field_name, field_value FROM products_extra_data WHERE product_id = ?",$product_id);
if(scalar@$extra_data){$info{product_extra_data}=$extra_data;
foreach my $e(@$extra_data){my$name=$e->{field_name};
$name=~s/\s//g;
$info{product_extra_data_by_key}->{$name}=$e->{field_value};
$info{product_extra_data_by_key}->{lc($name)}=$e->{field_value};}}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($tmpl,\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my$printers=$machine->config()->devices_by_type("Printers")||[];
my$main_printer=-1;
my$label_printer=-1;
my$has_label_printer=0;
my$printer_to_use=-1;
my$cups_queue="";
my$cfg_main={};
my$cfg_label={};
my$cfg_final={};
for(my$i=0;$i<scalar(@$printers);$i++){my$dev=$printers->[$i];
$dev->{device}->{PrintLabels}||="";
$dev->{device}->{Main}||=0;
if($dev->{device}->{PrintLabels}){$has_label_printer=1;
$label_printer=$i;
$cfg_label=$dev;
$cups_queue=$dev->{device}->{CupsQueue};}if($dev->{device}->{Main}){$main_printer=$i;
$cfg_main=$dev;}}if($label_printer!=-1){$printer_to_use=$label_printer;
$cfg_final=$cfg_label;}elsif($main_printer!=-1){$printer_to_use=$main_printer;
$cfg_final=$cfg_main;}if($printer_to_use!=-1){if($label_system&&$label_printer==-1){print STDERR"ERROR! there's a label system defined - $label_system - "."but didn't find a printer for labels...\n";
return;}if($label_system){if(!$cups_queue){my$dev=$cfg_final->{device}->{Device}//"";
if($dev){for(my$i=0;$i<$count;$i++){if(open(F,"> $dev")){print F$content;
close(F);}else{print STDERR"ERROR WRITING TO: $dev - $!\n";
return;}}}else{print STDERR"ERROR (label_system: $label_system) Can't find cups queue or device on the printers to print the label...\n";
return;}return;}else{write_file("/tmp/barcode_label.txt",$content);
for(my$i=0;$i<$count;$i++){system("lp -o raw -d \"".$cups_queue."\" /tmp/barcode_label.txt");}}}else{$cfg_final->{device}->{GenerateGraphicalLabel}||=0;
$cfg_final->{device}->{CupsQueue}||=0;
if($cfg_final->{device}->{GenerateGraphicalLabel}&&$cfg_final->{device}->{CupsQueue}){my$product=$info{product};
my$name=$product->{name}||="";
my$line2=sprintf("%.3f",$product->{sell_price1})." - ".sprintf("%.2f",$product->{sell_vat1});
my$file=`/opt/pos/common/bin/gen_barcode_label.pl "$barcode" "$name" "$line2"`;
for(my$i=0;$i<$count;$i++){system("lp -d \"".$cfg_final->{device}->{CupsQueue}."\" $file");}return 1;}my$dir_server_tmp=$machine->server_dir_tmp();
my$timestamp=time.$$.int(rand(10000));
my$print_file="$dir_server_tmp/print#$station#$printer_to_use#0#$timestamp.txt";
if($count==1){my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$print_file;
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,$barcode,5);
open(F,"> $print_file");
print F$content;
close(F);}else{for(my$i=0;$i<$count;$i++){my$str=$print_file;
$str=~s/\.txt$/$i.txt/;
my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$str;
$a[4]=$i.$a[4];
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,$barcode,5);
$str=join("#",@a);
open(F,"> $str");
print F$content;
close(F);}}}}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub x64_void{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}print STDERR"> x64_void - $file\n";
my$cfg=$machine->config()->load_fixed();
my$station=$cfg->{STATION_NUMBER}||"0";
my$db=$machine->database();
&get_products_information($db);
&get_clerks_information($db);
my($comando,$clerk,$area,$table,$msg_num)=$file=~/x64_void_(\d+)_(\d+)_(\d+)_(\d+)_.*?_(\d+)\./;
my$login=$clerk;
my$clerk_info=$CLERKS_INFO->{$clerk};
$clerk_info->{can_void_products}||=0;
$clerk_info->{used_for_training}||=0;
if(!$clerk_info->{can_void_products}&&!$clerk_info->{used_for_training}){my$msg=&x64_txt_message("$login","Nao pode anular");
open(NNN,"> /tmp/x64_message_$comando.txt");
print NNN$msg;
close(NNN);
return;}my$date=&strftime("%Y-%m-%d %H:%M:%S",localtime);
my$hour=&strftime("%H:%M:%S",localtime);
my%void_products=();
my$did_products=0;
my@prods=();
my$line_num=0;
my@ndup_products=();
my$ignored=0;
if(open(F,"< $file")){while(<F>){s/\n//g;
s/\r//g;
my($prod,$qtty)=split/#/;
$prod||="";
next unless$prod;
my$prod_id=$PRODUCTS_IDS_BY_HANDY_PLU->{$prod};
next unless defined$prod_id;
push@prods,{id=>$prod_id,qty=>$qtty,line_num=>$line_num};
$line_num++;}close(F);}for(@prods){my$md5_str="v#".$comando."#".$clerk."#".$area."#".$table."#".$_->{id}."#".$_->{qty}."#".$_->{line_num};
my$md5=&md5_hex($md5_str);
if(!exists$X64_CONTROL{$md5}){push@ndup_products,$_;}else{my$cur_time=time;
my$diff=$cur_time-$X64_CONTROL{$md5};
if($diff<=8){$ignored=1;
my$msg="VOID - DUPLICATE! ($diff secs), comando: $comando, clerk: $clerk, area: $area, "."table: $table, product: ".$_->{id}.", qty: ".$_->{qty}.", linenum: ".$_->{line_num}."\n";
&x64_log($msg);}else{my$msg="VOID - IN MD5, BUT OK! ($diff secs), comando: $comando, clerk: $clerk, area: $area, "."table: $table, product: ".$_->{id}.", qty: ".$_->{qty}.", linenum: ".$_->{line_num}."\n";
&x64_log($msg);
push@ndup_products,$_;}}$X64_CONTROL{$md5}=time;}&x64_log("PRODS TO VOID: ".(scalar@ndup_products).", comando: $comando, clerk: $clerk, area: $area, table: $table");
if(!scalar@ndup_products){return;}@prods=@ndup_products;
map{$void_products{$_->{id}}+=$_->{qty}}@prods;
my$checks_dir=$machine->dir_server()."/server/data/areas";
my$check_file="$checks_dir/$area/$table";
my$check_o=App::POS::Check->new(file=>$check_file);
my$data=$check_o->data();
$data->{details}||=[];
if(!scalar@{$data->{details}}){print STDERR"x64_void> Check without details, returning...\n";
return;}my$new_data={};
$new_data->{extra_data}=$data->{extra_data};
$new_data->{header}=$data->{header};
$new_data->{details_deleted}=$data->{details_deleted};
$new_data->{header}||=[];
$new_data->{details}||=[];
$new_data->{details_deleted}||=[];
if(!scalar@{$new_data->{header}}){my$business_date=&date_based_on_closing_hour($date,$CLOSE_HOUR);
push@{$new_data->{header}},$check_o->generate_empty_header($date,$business_date,$clerk,$area);}my@details=();
foreach my $d(reverse@{$data->{details}}){next unless scalar@$d;
my$to_remove=$void_products{$d->[1]};
if(!defined$to_remove){push@details,$d;
next;}my$rand=&strftime("%Y%m%d%H%M%S",localtime).rand();
$rand=~s/\.//g;
if($void_products{$d->[1]}>=$d->[3]){$void_products{$d->[1]}-=$d->[3];
my$num_extras_to_remove=0;
for(my$i=scalar(@details)-1;$i>=0;$i--){last if!$details[$i]->[13];
$num_extras_to_remove++;}for(my$i=0;$i<$num_extras_to_remove;$i++){pop@details;}my@new_p=@$d;
$new_p[0]=$rand;
push@{$new_data->{details_deleted}},\@new_p;}else{my@new_p=@$d;
$new_p[3]=$to_remove;
$new_p[0]=$rand;
push@{$new_data->{details_deleted}},\@new_p;
$d->[3]-=$void_products{$d->[1]};
push@details,$d;
delete$void_products{$d->[1]};}}@details=reverse@details;
$new_data->{details}=\@details;
$check_o->set_data($new_data);
$check_o->save();
open(NNN,"> $check_file.99.hold");
print NNN"1\n";
close(NNN);
&copy($check_file,$check_file.".99.hold.check");
open(NNN,"> $check_file.refresh");
print NNN"1\n";
close(NNN);
if(-e"$check_file.total"){local$/=undef;
open(NNN,"< $check_file.total");
my$total=<NNN>;
close(NNN);
if($total ne ''){$total=$total*1;
if($total==0){unlink"$check_file.total";}}}}sub x64_transfer{my($machine,$file)=@_;
print STDERR"> x64_transfer - $file\n";
print STDERR"X64 transfers are disabled...\n";
&x64_log("X64 transfers are disabled...");
return;}sub x64_printbill{my($machine,$file)=@_;
print STDERR"> x64_printbill - $file\n";
my$cfg=$machine->config()->load_fixed();
my($comando,$clerk,$area,$table,$is_internal,$station)=$file=~/x64_printbill_(\d+)_(\d+)_(\d+)_(\d+)_(\d+)_(\d+)#/;
my$db=$machine->database();
&get_clerks_information($db);
my$checks_dir=$machine->dir_server()."/server/data/areas";
my$check_file="$checks_dir/$area/$table";
my$clerk_info=$CLERKS_INFO->{$clerk};
$clerk_info->{cant_print_bill}||=0;
$clerk_info->{cant_print_bill_internal}||=0;
if($clerk_info->{cant_print_bill}&&$clerk_info->{cant_print_bill_internal}){print STDERR"Clerk isn't allowed to print any kind of bills, returning...\n";
my$msg=&x64_txt_message("$clerk","Nao autorizado");
open(F,"> /tmp/x64_message_$comando.txt");
print F$msg;
close(F);
return;}if($is_internal&&$clerk_info->{cant_print_bill_internal}){print STDERR"clerk can't print internal bills, ignoring...\n";
my$msg=&x64_txt_message("$clerk","Nao autorizado");
open(F,"> /tmp/x64_message_$comando.txt");
print F$msg;
close(F);
return;}if(!$is_internal&&$clerk_info->{cant_print_bill}){print STDERR"clerk can't print bills, ignoring...\n";
my$msg=&x64_txt_message("$clerk","Nao autorizado");
open(F,"> /tmp/x64_message_$comando.txt");
print F$msg;
close(F);
return;}my$file_print=$machine->dir_server()."/server/data/tmp/print_notclosed_check#".$station."#$area#$is_internal#0#$table#1.txt";
if($is_internal){unlink("$check_file.bill");}else{if(open(F,"> $check_file.bill")){print F"1\n";
close(F);}}if(open(F,"> $file_print")){print F"1\n";
close(F);}&copy($check_file,$check_file.".toprint");}sub rgpd_print_auth_statement{my($machine,$file)=@_;
my($station,$clerk)=$file=~/rgpd_print_auth_statement#(\d+)#(\d+)#/;
my$db=$machine->database();
&get_clerks_information($db);
my$cfg=$machine->config()->load_fixed();
my%info=(date=>&strftime("%Y-%m-%d",localtime),hour=>&strftime("%H:%M:%S",localtime),datetime=>&strftime("%Y-%m-%d %H:%M:%S",localtime),station=>$station,clerk_code=>$clerk,clerk_name=>exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>',store_nif=>$cfg->{SAFT_CompanyId}||"",store_name=>$cfg->{SAFT_CompanyName}||"",store_biz_name=>$cfg->{SAFT_BusinessName}||"",store_address=>$cfg->{SAFT_AddressDetail}||"",store_city=>$cfg->{SAFT_City}||"",store_postal_code=>$cfg->{SAFT_PostalCode}||"",);
my$content;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('rgpd_auth_declaration.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
open(F,"> $print_file");
print F$content;
close(F);}sub rgpd_customer_export{my($machine,$file)=@_;
my($to_screen,$customer_id,$station,$clerk)=$file=~/rgpd_customer_export#(\d+)#(\d+)#(\d+)#(\d+)#/;
my$db=$machine->database();
&get_clerks_information($db);
my%fields=();
read_translation_file("/opt/pos/etc/fields.txt",\%fields);
return unless exists$fields{customers};
my$cfields=$fields{customers}||[];
return unless scalar@$cfields;
my$c=$db->select("SELECT * FROM customers WHERE id = ?",$customer_id);
return unless scalar@$c;
$c=$c->[0];
my@customer_fields=();
for(@$cfields){my($field_name,$field_desc)=each%$_;
push@customer_fields,{name=>translate($machine,$field_desc),value=>$c->{$field_name}||"--",};}push@customer_fields,{name=>translate($machine,"Modificado em"),value=>substr($c->{modified},0,19),};
my$cfg=$machine->config()->load_fixed();
my%info=(customer=>$c,customer_fields=>\@customer_fields,date=>&strftime("%Y-%m-%d",localtime),hour=>&strftime("%H:%M:%S",localtime),datetime=>&strftime("%Y-%m-%d %H:%M:%S",localtime),station=>$station,clerk_code=>$clerk,clerk_name=>exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>',to_xls=>$to_screen eq '2'?1:0,store_nif=>$cfg->{SAFT_CompanyId}||"",store_name=>$cfg->{SAFT_CompanyName}||"",store_biz_name=>$cfg->{SAFT_BusinessName}||"",store_address=>$cfg->{SAFT_AddressDetail}||"",store_city=>$cfg->{SAFT_City}||"",store_postal_code=>$cfg->{SAFT_PostalCode}||"",);
my$content;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('rgpd_customer_export.tmpl',\%info,\$content,)||die$TEMPLATE->error;
if($to_screen eq '2'){$content=~s/\@.*?\@//g;
my$xls_file="/opt/pos/common/data/rgpd_customer_export_".$c->{id}."_".$info{date}.".xls";
&_to_xls($content,$info{date},$info{date},$xls_file,"##");}elsif($to_screen){$content=~s/\@.*?\@//g;
open(F,"> ".$machine->dir_tmp()."/printer_screen#$station.txt");
print F$content;
close(F);}else{return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
open(F,"> $print_file");
print F$content;
close(F);}}sub report_stk_below_minimal{my($machine,$file)=@_;
my($to_screen,$station,$clerk)=$file=~/report_stk_below_minimal#(\d+)#(\d+)#(\d+)#/;
my$db=$machine->database();
&get_clerks_information($db);
my$prods=$db->select(qq{
    SELECT id, handy_plu, name, barcode, stock_current, stock_min
    FROM products
    WHERE deleted = 0 AND stock_current <= stock_min AND stock_use = 1 AND stock_min != 0
    ORDER BY name
  });
return unless scalar@$prods;
my$cfg=$machine->config()->load_fixed();
my%info=(products=>$prods,date=>&strftime("%Y-%m-%d",localtime),hour=>&strftime("%H:%M:%S",localtime),datetime=>&strftime("%Y-%m-%d %H:%M:%S",localtime),station=>$station,clerk_code=>$clerk,clerk_name=>exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>',);
my$content;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_stock_below_minimal.tmpl',\%info,\$content,)||die$TEMPLATE->error;
if($to_screen eq '2'){$content=~s/\@.*?\@//g;
my$xls_file="/opt/pos/common/data/stock_below_minimal_".$info{date}.".xls";
&_to_xls($content,$info{date},$info{date},$xls_file);}elsif($to_screen){$content=~s/\@.*?\@//g;
open(F,"> ".$machine->dir_tmp()."/printer_screen#$station.txt");
print F$content;
close(F);}else{my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
open(F,"> $print_file");
print F$content;
close(F);}}sub report_stk_current{my($machine,$file)=@_;
my($to_xls,$station,$clerk,$email)=$file=~/report_stk_current#(\d+)#(\d+)#(\d+)#(.*?)#/;
my$db=$machine->database();
&get_clerks_information($db);
my%cost_prices=();
my$recs=$db->select(qq{
    SELECT *
    FROM stock_movements
    WHERE cost_price > 0
    ORDER BY date
  });
map{$cost_prices{$_->{product_id}}=$_->{cost_price}}@$recs;
my$prods=$db->select(qq{
    SELECT id, handy_plu, name, barcode, stock_current, stock_min, cost_price, sell_price1
    FROM products
    WHERE deleted = 0 AND stock_use = 1
    ORDER BY name
  });
if(!scalar@$prods){print STDERR"No stock products... returning...\n";
return;}for(@$prods){if(exists$cost_prices{$_->{id}}){$_->{cost_price}=$cost_prices{$_->{id}};}$_->{cost_price}||=0;
$_->{sell_price1}||=0;
$_->{cost_price}=$_->{sell_price1}unless$_->{cost_price};
$_->{name_orig}=$_->{name};
my$len=length($_->{name});
if($len<26){$_->{name}=$_->{name}.("_" x(26-$len));}}my$cfg=$machine->config()->load_fixed();
my%info=(cost_prices=>\%cost_prices,products=>$prods,date=>&strftime("%Y-%m-%d",localtime),hour=>&strftime("%H:%M:%S",localtime),datetime=>&strftime("%Y-%m-%d %H:%M:%S",localtime),station=>$station,clerk_code=>$clerk,clerk_name=>exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>',to_xls=>$to_xls==2||$to_xls==3?1:0,);
my$content;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_stock_current.tmpl',\%info,\$content,)||die$TEMPLATE->error;
if($to_xls==2||$to_xls==3){$content=~s/\@.*?\@//g;
my$xls_file="/opt/pos/common/data/stock_current_".$info{date}.".xls";
&_to_xls($content,$info{date},$info{date},$xls_file);
if($email){my$LANG=$cfg->{LANG}||"";
my$SAFT_TaxRegistratioNumber=$cfg->{SAFT_TaxRegistratioNumber}||"";
my$SAFT_BusinessName=$cfg->{SAFT_BusinessName}||"";
my$stat=send_mail(to=>$email,subject=>"[$LANG] ".&translate($machine,"MAPA DE EXISTÊNCIAS")." - ".$SAFT_BusinessName,content=>"$LANG<br>====<p>$SAFT_BusinessName<p>$SAFT_TaxRegistratioNumber<br>",attachment=>[$xls_file],);}}elsif($to_xls==1){$content=~s/\@.*?\@//g;
open(F,"> ".$machine->dir_tmp()."/printer_screen#$station.txt");
print F$content;
close(F);}else{my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
open(F,"> $print_file");
print F$content;
close(F);}}sub report_stk_movements{my($machine,$file)=@_;
my($to_xls,$station,$clerk,$date_from,$date_to,$product_id)=$file=~/report_stk_movements#(\d+)#(\d+)#(\d+)#(.*?)#(.*?)#(\d+)#/;
my$where_str1=$product_id?" AND sm.product_id = $product_id":"";
my$where_str2=$product_id?" AND sd.product = $product_id":"";
my$db=$machine->database();
&get_clerks_information($db);
my%data=();
my$recs=$db->select(qq{
    SELECT sm.date, p.name, sm.operation, SUM(sm.quantity) AS qtty
    FROM stock_movements sm
    LEFT JOIN products p ON (sm.product_id = p.id)
    WHERE sm.date >= ? AND sm.date <= ? AND p.stock_use = 1$where_str1
    GROUP BY sm.date, p.name, sm.operation
  },$date_from,$date_to);
for(@$recs){$_->{qtty}=$_->{qtty}*-1 if$_->{qtty}<0;
$data{my_conv($_->{name})}->{$_->{date}}->{$_->{operation}}=sprintf("%.2f",$_->{qtty});}my$sales=sales($db,$date_from,$date_to,$product_id,1);
$sales->{dates}||={};
foreach my $date(keys%{$sales->{dates}}){$sales->{dates}->{$date}||={};
foreach my $product(keys%{$sales->{dates}->{$date}}){$data{$product}->{$date}->{"qtty_sold"}+=sprintf("%.2f",$sales->{dates}->{$date}->{$product}->{total_qttys});}}foreach my $product(keys%data){foreach my $date(keys%{$data{$product}}){$data{$product}->{$date}->{"qtty_sold"}//=0;
$data{$product}->{$date}->{"in"}//=0;
$data{$product}->{$date}->{"out"}//=0;
$data{$product}->{$date}->{"balance"}=$data{$product}->{$date}->{"in"}-($data{$product}->{$date}->{"qtty_sold"}+$data{$product}->{$date}->{"out"});}}my%info=(products=>\%data,date=>&strftime("%Y-%m-%d",localtime),hour=>&strftime("%H:%M:%S",localtime),datetime=>&strftime("%Y-%m-%d %H:%M:%S",localtime),station=>$station,clerk_code=>$clerk,clerk_name=>exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>',);
my$content;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_stock_movements.tmpl',\%info,\$content,)||die$TEMPLATE->error;
if($to_xls==2){$content=~s/\@.*?\@//g;
my$xls_file="/opt/pos/common/data/stock_movements_".$info{date}.".xls";
&_to_xls($content,$info{date},$info{date},$xls_file);}elsif($to_xls==1){$content=~s/\@.*?\@//g;
open(F,"> ".$machine->dir_tmp()."/printer_screen#$station.txt");
print F$content;
close(F);}else{my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
open(F,"> $print_file");
print F$content;
close(F);}}sub rfid_card_operations{my($machine,$file)=@_;
my($where_to_print,$card_id,$balance,$exp_date,$station,$clerk,$date_from,$date_to,$read_from)=$file=~/rfid_card_operations#(.*?)#(.*?)#(.*?)#(.*?)#(\d+)#(\d+)#(.*?)#(.*?)#(.*?)#/;
my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
&get_clerks_information($db);
my%data=();
my$recs;
if($read_from eq 'internet'){$cfg->{RFID_TOKEN}||="";
my$token=$cfg->{RFID_TOKEN}//"";
my$api_passwd=$cfg->{RFID_API_PASSWD}//"";
if(!$token||!$api_passwd){print STDERR"Missing RFID_TOKEN or RFID_API_PASSWD in config.ini\n";
return;}my%params=(token=>$token,api_passwd=>$cfg->{RFID_API_PASSWD},date_from=>$date_from,date_to=>$date_to,card_id=>$card_id,);
my$cnt=post('http://apisv2.net-bo.com/pos_rfid/card_transactions',\%params,5);
$cnt||="";
my$ref=[];
if($cnt){eval{$ref=decode_json($cnt)};
$ref//=[];
foreach my $r(@$ref){my@hour=split/:/,$r->{hour};
my$op_type=$r->{operation_type}==1?'credit':'debit';
push@$recs,{station=>$r->{station},clerk=>$r->{clerk},amount=>$r->{amount},operation_type=>$op_type,business_date=>$r->{business_date},token_id=>$r->{token},station_name=>$r->{station_name},hour=>"$hour[0]:$hour[1]",operation_signal=>$r->{operation_type}==1?'+':'-',};}}}else{$recs=$db->select(qq{
      SELECT station, clerk, amount, operation_type, business_date, token_id,
        station_name,
        to_char(datetime,'HH24') || ':' || to_char(datetime,'MI') AS hour,
        CASE WHEN operation_type = 'credit' THEN '+' ELSE '-' END AS operation_signal
      FROM rfid_operations
      WHERE business_date >= ? AND business_date <= ? AND card_id = ?
      ORDER BY datetime
    },$date_from,$date_to,$card_id);}$data{total_debit}=0;
$data{total_credit}=0;
for(@$recs){push@{$data{dates}->{$_->{business_date}}->{records}},$_;
if($_->{operation_type}eq 'debit'){$data{dates}->{$_->{business_date}}->{total_debit}+=$_->{amount},$data{total_debit}+=$_->{amount},}else{$data{dates}->{$_->{business_date}}->{total_credit}+=$_->{amount},$data{total_credit}+=$_->{amount},}}my%info=(card_id=>$card_id,card_balance=>$balance,card_expire_date=>$exp_date,date=>&strftime("%Y-%m-%d",localtime),hour=>&strftime("%H:%M:%S",localtime),datetime=>&strftime("%Y-%m-%d %H:%M:%S",localtime),station=>$station,clerk_code=>$clerk,clerk_name=>exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>',records=>\%data,);
my$content;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('rfid_card_operations.tmpl',\%info,\$content,)||die$TEMPLATE->error;
if($where_to_print eq 'screen'){$content=~s/\@.*?\@//g;
open(F,"> ".$machine->dir_tmp()."/printer_screen#$station.txt");
print F$content;
close(F);}elsif($where_to_print eq 'printer'){my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
open(F,"> $print_file");
print F$content;
close(F);}}sub rfid_card_balance{my($machine,$file)=@_;
my@a=split/#/,$file;
$a[-1]=~s/\.txt$//;
my$clerk="";
my$card_id="";
my$balance="";
my$exp_date="";
my$spent_today="";
my$limit="";
if(scalar@a==7){($clerk,$card_id,$balance,$exp_date,$spent_today,$limit)=($a[1],$a[2],$a[3],$a[4],$a[5],$a[6]);}else{($clerk,$card_id,$balance,$exp_date)=($a[1],$a[2],$a[3],$a[4]);}my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
&get_clerks_information($db);
my$station=$cfg->{STATION_NUMBER}||"0";
my%info=(card_id=>$card_id,card_balance=>$balance,card_expire_date=>$exp_date,date=>&strftime("%Y-%m-%d",localtime),hour=>&strftime("%H:%M:%S",localtime),datetime=>&strftime("%Y-%m-%d %H:%M:%S",localtime),station=>$station,clerk_code=>$clerk,clerk_name=>exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>',spent_today=>$spent_today||0,daily_limit=>$limit||0,);
my$content;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('rfid_card_balance.tmpl',\%info,\$content,)||die$TEMPLATE->error;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
open(F,"> $print_file");
print F$content;
close(F);}sub check_import{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}print STDERR"check_import - $file\n";
my($id,$hold_now)=$file=~/check_import#(.*)#(\d+)\./;
$id||="";
$hold_now||=0;
if(!$id){print STDERR"$file - missing id...\n";
return 1;}my$created_stuff_in_db=0;
my$db=$machine->database();
my$recs;
my%clerks;
my%products;
my$extra_data={has_pzones=>1};
$recs=$db->select("SELECT id, login FROM clerks WHERE deleted = 0");
for(@$recs){$clerks{$_->{login}}=$_->{id};}$recs=$db->select("SELECT id, handy_plu, name FROM products WHERE deleted = 0");
for(@$recs){$_->{handy_plu}||="";
if($_->{handy_plu}){$products{by_plu}->{lc($_->{handy_plu})}=$_->{name};}$_->{name}||="";
if($_->{name}){$products{by_name}->{lc($_->{name})}=$_->{id};}$products{by_id}->{$_->{id}}=$_->{name};}my@lines=read_file($file);
my%info=();
my$did_products=0;
my$clerk_open_id="";
my$clerk_close_id="";
my$clerk_id="";
for(my$i=0;$i<scalar(@lines);$i++){my$l=$lines[$i];
$l=~s/[\r\n]+$//;
my@a=my_split($l,'#');
if($i==0){next unless scalar@a>=10;
print STDERR"$i - headers - $l\n";
$info{header}->{table_num}=$a[0]||0;
$info{header}->{clerk_open}=$a[1];
$info{header}->{clerk_close}=$a[2];
$info{header}->{customer_nif}=$a[3];
$info{header}->{customer_name}=$a[4];
$info{header}->{discount}=$a[5];
$info{header}->{area}=$a[6];
$extra_data->{obs}=$a[7];
$info{header}->{finalize}=$a[8];
$info{header}->{to_print}=$a[9];
if(!exists$clerks{$info{header}->{clerk_open}}){my$id=$db->next_id("clerks");
$db->do("INSERT INTO clerks (id, login, can_login) VALUES (?, ?, 0)",$id,$info{header}->{clerk_open},);
$created_stuff_in_db=1;
$clerks{$info{header}->{clerk_open}}=$id;}if(!exists$clerks{$info{header}->{clerk_close}}){my$id=$db->next_id("clerks");
$db->do("INSERT INTO clerks (id, login) VALUES (?, ?)",$id,$info{header}->{clerk_close},);
$created_stuff_in_db=1;
$clerks{$info{header}->{clerk_close}}=$id;}}elsif(scalar@a==2){print STDERR"$i - footers - $l\n";
push@{$info{footers}},{total=>$a[0],media=>$a[1],};}elsif(scalar@a==3){print STDERR"$i - extra_data - $l\n";
$extra_data->{$a[1]}=$a[2];}else{next unless scalar@a>=7;
print STDERR"$i - product - $l\n";
$info{details}=[]unless exists$info{details};
my$product_code=$a[1]||0;
if(!exists$PRODUCTS_INFO->{$product_code}){_send_to_ui('ALERT','check_import - '.translate($machine,"ID PRODUTO INEXISTENTE").' - '.$product_code);
next;}$did_products=1;
my$unit_price=$a[3]||0;
my$vat=$a[4]||0;
if($unit_price==-1){$unit_price=$PRODUCTS_INFO->{$product_code}->{sell_price1}||0;}if($vat==-1){$vat=$PRODUCTS_INFO->{$product_code}->{sell_vat1}||0;}my$d={clerk=>$a[0],product_id=>$product_code,quantity=>$a[2],unit_price=>$unit_price,vat=>$vat,discount=>$a[5],obs=>$a[6],};
push@{$info{details}},$d;
if(!exists$clerks{$d->{clerk}}){my$id=$db->next_id("clerks");
$db->do("INSERT INTO clerks (id, login) VALUES (?, ?)",$id,$d->{clerk},);
$created_stuff_in_db=1;
$clerks{$d->{clerk}}=$id;}}}if(!$did_products){print STDERR"no valid products... returning...\n";
return;}if($created_stuff_in_db){$CLERKS_INFO=undef;
$PRODUCTS_INFO=undef;
&get_products_information($db);
&get_clerks_information($db);
_refresh_gambas_caches($machine);}if($info{header}->{finalize}){if(!exists$info{header}||!exists$info{details}||!exists$info{footers}){print STDERR"$file - missing header or details or footers...\n";
return 1;}}else{if(!exists$info{header}||!exists$info{details}){print STDERR"$file - missing header or details...\n";
return 1;}}my$dir=$machine->dir_areas()."/".$info{header}->{area};
make_path($dir)unless-d$dir;
if(!exists$AREAS_INFO{$info{header}->{area}}){print STDERR"ERROR! area $info{header}->{area} doesn't exists in config, ignoring...\n";
return 1;}my$area_tables=$AREAS_INFO{$info{header}->{area}}->[1]||0;
my$table_num=0;
if($area_tables>0&&!$info{header}->{table_num}){print STDERR"ERROR! we're using an area with tables and didn't provide a table num, will ignore...\n";
return 1;}if($area_tables>0){$table_num=$info{header}->{table_num};}else{my$next=0;
if(-e"$dir/nextid"){$table_num=read_file("$dir/nextid");
$table_num=~s/[\r\n]+$//;
$next=$table_num+1;}else{$table_num=1;
$next=$table_num+1;}write_file("$dir/nextid",$next);}_send_to_ui('REFRESHTABLES','');
my$o=App::POS::Check->new(file=>"$dir/".$table_num);
my$datetime=strftime("%Y-%m-%d %H:%M:%S",localtime);
my$biz_date=date_based_on_closing_hour($datetime,$CLOSE_HOUR);
my@header=$o->generate_empty_header($datetime,$biz_date,$clerks{$info{header}->{clerk_open}},$info{header}->{area},0,);
$header[3]=$clerks{$info{header}->{clerk_close}};
$header[6]=$info{header}->{area};
$header[7]=$info{header}->{area};
$header[10]=$info{header}->{discount}||'';
$header[12]=$info{header}->{customer_name};
$header[13]=$info{header}->{customer_nif};
my@products=();
my@payments=();
my%data=(header=>\@header,details=>\@products,products_extra_data=>{},extra_data=>$extra_data,footers=>\@payments,);
for(my$i=0;$i<scalar@{$info{details}};$i++){my$d=$info{details}->[$i];
my$hour=strftime("%H:%M:%S",localtime);
my$gbi=strftime("%Y%m%d%H%M%S",localtime).int(rand(1000));
$gbi=~s/\.//g;
my$discount=$d->{discount}||'';
push@products,[$i,$d->{product_id},$hour,$d->{quantity},$d->{unit_price},'',$d->{vat},$clerks{$d->{clerk}},"","",1,$discount,0,"",0,0,1,$biz_date,$gbi,];
if(defined$d->{obs}&&$d->{obs}ne ''){if($d->{obs}!~/;$/){$d->{obs}.=';';}$data{products_extra_data}->{$gbi}=$d->{obs};}}foreach my $f(@{$info{footers}}){push@payments,[$f->{media},$f->{total}];}$o->set_data(\%data);
$o->save();
if($hold_now){_send_to_ui('CHECKHOLD',"$info{header}->{area}#$table_num");}if($info{header}->{finalize}){$o->finalize($info{header}->{to_print});}return 1;}sub recover_document_stock{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}my($id)=$file=~/recover_document_stock#(\d+)#/;
return unless$id;
my$db=$machine->database();
my$lines=$db->select("SELECT * FROM sales_headers_aux WHERE header = ? AND key_name = 'invoiced'",$id);
if(!scalar@$lines){my$lines=$db->select("SELECT quantity, product FROM sales_details WHERE header = ? AND operation_type = 1",$id);
my%stock_ids=();
for(@$lines){$stock_ids{$_->{product}}+=$_->{quantity};}foreach my $id(keys%stock_ids){$db->do("UPDATE products SET stock_current = stock_current + ".$stock_ids{$id}." WHERE id = ".$id." AND stock_use = 1");}}}sub x64_request{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}print STDERR"> x64_request - $file\n";
my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
&get_products_information($db);
&get_clerks_information($db);
my($comando,$clerk,$area,$table,$msg_num,$station)=$file=~/x64_request_(\d+)_(\d+)_(\d+)_(\d+)_.*?_(\d+)_(\d+)\./;
my$bck=$clerk;
$clerk=$CLERKS_IDS_BY_LOGIN->{$clerk};
if(!defined$clerk){$clerk=$bck;}my$clerk_info=$CLERKS_INFO->{$clerk};
$clerk_info->{cant_use_others_tables}||=0;
my$dir_server=$machine->dir_server();
if(open(F,"< $file")){my$checks_dir=$dir_server."/server/data/areas";
my$check_file="$checks_dir/$area/$table";
my$area_o=App::POS::Area->new(machine=>$machine,number=>$area);
my$price_field=$area_o->price_field();
my$vat_field=$area_o->vat_field();
my$check=App::POS::Check->new(file=>$check_file);
my$data=$check->data();
my$date=&strftime("%Y-%m-%d %H:%M:%S",localtime);
my$hour=&strftime("%H:%M:%S",localtime);
if(!scalar@{$data->{header}}){push@{$data->{header}},$check->generate_empty_header($date,&date_based_on_closing_hour($date,$CLOSE_HOUR),$clerk,$area);}if($clerk_info->{cant_use_others_tables}&&$clerk ne$data->{header}->[2]){print STDERR"Clerk can't use some other's table, returning...\n";
my$msg=&x64_txt_message("Acesso negado"," a mesa $table");
open(NNN,"> /tmp/x64_message_$comando.txt");
print NNN$msg;
close(NNN);
return;}my$print_bill_internal=-1;
my$did_products=0;
my@prods=();
my@prods_bad=();
my$line_num=0;
my$has_invalid_qty=0;
while(<F>){s/\n//g;
s/\r//g;
my($prod,$qtty)=split/#/;
$prod||="";
next unless$prod;
if($prod eq '9999'){$print_bill_internal=1;
last;}elsif($prod eq '9998'){$print_bill_internal=0;
last;}my$prod_id=$PRODUCTS_IDS_BY_HANDY_PLU->{$prod};
next unless defined$prod_id;
if($qtty==0){$has_invalid_qty=1;
push@prods_bad,{id=>$prod_id,qty=>$qtty,line_num=>$line_num,handy=>$prod};}else{push@prods,{id=>$prod_id,qty=>$qtty,line_num=>$line_num,handy=>$prod};}$line_num++;}close(F);
if($has_invalid_qty){my$area_name=$AREAS_INFO{$area}->[0];
my$clerk_name=$CLERKS_INFO->{$clerk}->{login};
my$txt="----------------------------------------\n"."      !!!!!! INVALID REQUEST !!!!!!\n"."----------------------------------------\n".$area_name." / ".$table."\n".$clerk_name."\n"."----------------------------------------\n";
for(@prods_bad){my$name=$PRODUCTS_INFO->{$_->{id}}->{name};
$name=sprintf("%-25s",substr($name,0,20));
$txt.=sprintf("%6s",$_->{handy})." ".$name." ".$_->{qty}."\n";}$txt.="\@paper_cut\@";
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
print STDERR$txt."\n";
open(F,"> $print_file");
print F$txt;
close(F);}if($print_bill_internal!=-1){my$file=$dir_server."/server/data/tmp/x64_printbill_".$comando."_".$clerk."_".$area."_".$table."_".$print_bill_internal."_$station#".time.$$.".txt";
open(NNN,"> $file");
print NNN"1\n";
close(NNN);
return;}my@ndup_products=();
my$ignored=0;
for(@prods){my$md5_str="r#".$comando."#".$clerk."#".$area."#".$table."#".$_->{id}."#".$_->{qty}."#".$_->{line_num};
my$md5=&md5_hex($md5_str);
if(!exists$X64_CONTROL{$md5}){push@ndup_products,$_;}else{my$cur_time=time;
my$diff=$cur_time-$X64_CONTROL{$md5};
if($diff<=8){$ignored=1;
my$msg="REQ - DUPPLICATE! ($diff secs), comando: $comando, clerk: $clerk, area: $area, "."table: $table, product: ".$_->{id}.", qty: ".$_->{qty}.", linenum: ".$_->{line_num}."\n";
&x64_log($msg);}else{my$msg="REQ - IN MD5, BUT OK! ($diff secs), comando: $comando, clerk: $clerk, area: $area, "."table: $table, product: ".$_->{id}.", qty: ".$_->{qty}.", linenum: ".$_->{line_num}."\n";
&x64_log($msg);
push@ndup_products,$_;}}$X64_CONTROL{$md5}=time;}&x64_log("PRODS TO REQUEST: ".(scalar@ndup_products).", comando: $comando, clerk: $clerk, area: $area, table: $table");
if(!scalar@ndup_products){return;}@prods=();
my%prod_idx=();
for(my$i=0;$i<scalar(@ndup_products);$i++){my$cur=$ndup_products[$i];
my$next=$ndup_products[$i+1];
if(exists$PRODUCTS_EXTRAS->{$cur->{id}}){push@prods,$cur;}else{if(defined$next&&exists$PRODUCTS_EXTRAS->{$next->{id}}){push@prods,$cur;}else{if(exists$prod_idx{$cur->{id}}){$prods[$prod_idx{$cur->{id}}]->{qty}+=$cur->{qty};}else{push@prods,$cur;
$prod_idx{$cur->{id}}=scalar@prods-1;}}}}foreach my $pp(@prods){my$prod=$pp->{id};
my$qty=$pp->{qty};
my$rand=&strftime("%Y%m%d%H%M%S",localtime).rand();
$rand=~s/\.//g;
my$prod_info=$PRODUCTS_INFO->{$prod};
my$price=$prod_info->{$price_field}||0;
my$vat=$prod_info->{$vat_field}||0;
my@r=();
if(exists$PRODUCTS_EXTRAS->{$prod}){@r=(0,$prod,$hour,$qty,$price,0,$vat,$clerk,"","","","",99,"EXTRA",0,1,1,$rand);}else{@r=(0,$prod,$hour,$qty,$price,0,$vat,$clerk,"","","","",99,"",0,1,1,$rand);}$did_products=1;
push@{$data->{details}},\@r;}if(!$did_products){print STDERR"Didn't find any valid product, aborting...\n";
return;}if(-e"$check_file.bill"){unlink("$check_file.bill");}$check->save();
open(NNN,"> $check_file.refresh");
print NNN"1\n";
close(NNN);
open(NNN,"> $check_file.99.hold");
print NNN"1\n";
close(NNN);
&copy($check_file,$check_file.".99.hold.check");
if(-e"$check_file.total"){local$/=undef;
open(NNN,"< $check_file.total");
my$total=<NNN>;
close(NNN);
if($total ne ''){$total=$total*1;
if($total==0){unlink"$check_file.total";}}}}}sub x64_request2{my($machine,$file)=@_;
if(!$machine->is_server()){return 2;}print STDERR"> x64_request - $file\n";
my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
&get_products_information($db);
&get_clerks_information($db);
my($comando,$clerk,$area,$table,$msg_num,$station)=$file=~/x64_request_(\d+)_(\d+)_(\d+)_(\d+)_.*?_(\d+)_(\d+)\./;
my$bck=$clerk;
$clerk=$CLERKS_IDS_BY_LOGIN->{$clerk};
if(!defined$clerk){$clerk=$bck;}my$clerk_info=$CLERKS_INFO->{$clerk};
$clerk_info->{cant_use_others_tables}||=0;
my$dir_server=$machine->dir_server();
if(open(F,"< $file")){my$checks_dir=$dir_server."/server/data/areas";
my$check_file="$checks_dir/$area/$table";
my$area_o=App::POS::Area->new(machine=>$machine,number=>$area);
my$price_field=$area_o->price_field();
my$vat_field=$area_o->vat_field();
my$check=App::POS::Check->new(file=>$check_file);
my$data=$check->data();
my$date=&strftime("%Y-%m-%d %H:%M:%S",localtime);
my$hour=&strftime("%H:%M:%S",localtime);
if(!scalar@{$data->{header}}){push@{$data->{header}},$check->generate_empty_header($date,&date_based_on_closing_hour($date,$CLOSE_HOUR),$clerk,$area);}if($clerk_info->{cant_use_others_tables}&&$clerk ne$data->{header}->[2]){print STDERR"Clerk can't use some other's table, returning...\n";
my$msg=&x64_txt_message("Acesso negado"," a mesa $table");
open(NNN,"> /tmp/x64_message_$comando.txt");
print NNN$msg;
close(NNN);
return;}my$print_bill_internal=-1;
my$did_products=0;
my@prods=();
my$line_num=0;
my$has_invalid_qty=0;
while(<F>){s/\n//g;
s/\r//g;
my($prod,$qtty)=split/#/;
$prod||="";
next unless$prod;
if($prod eq '9999'){$print_bill_internal=1;
last;}elsif($prod eq '9998'){$print_bill_internal=0;
last;}my$prod_id=$PRODUCTS_IDS_BY_HANDY_PLU->{$prod};
next unless defined$prod_id;
if($qtty==0){$has_invalid_qty=1;}push@prods,{id=>$prod_id,qty=>$qtty,line_num=>$line_num,handy=>$prod};
$line_num++;}close(F);
if($has_invalid_qty){my$area_name=$AREAS_INFO{$area}->[0];
my$clerk_name=$CLERKS_INFO->{$clerk}->{login};
my$txt="----------------------------------------\n"."      !!!!!! INVALID REQUEST !!!!!!\n"."----------------------------------------\n".$area_name." / ".$table."\n".$clerk_name."\n"."----------------------------------------\n";
for(@prods){my$name=$PRODUCTS_INFO->{$_->{id}}->{name};
$name=sprintf("%-25s",substr($name,0,20));
$txt.=sprintf("%6s",$_->{handy})." ".$name." ".$_->{qty}."\n";}$txt.="\@paper_cut\@";
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
print STDERR$txt."\n";
open(F,"> $print_file");
print F$txt;
close(F);
return;}if($print_bill_internal!=-1){my$file=$dir_server."/server/data/tmp/x64_printbill_".$comando."_".$clerk."_".$area."_".$table."_".$print_bill_internal."_$station#".time.$$.".txt";
open(NNN,"> $file");
print NNN"1\n";
close(NNN);
return;}my@ndup_products=();
my$ignored=0;
for(@prods){my$md5_str="r#".$comando."#".$clerk."#".$area."#".$table."#".$_->{id}."#".$_->{qty}."#".$_->{line_num};
my$md5=&md5_hex($md5_str);
if(!exists$X64_CONTROL{$md5}){push@ndup_products,$_;}else{my$cur_time=time;
my$diff=$cur_time-$X64_CONTROL{$md5};
if($diff<=8){$ignored=1;
my$msg="REQ - DUPPLICATE! ($diff secs), comando: $comando, clerk: $clerk, area: $area, "."table: $table, product: ".$_->{id}.", qty: ".$_->{qty}.", linenum: ".$_->{line_num}."\n";
&x64_log($msg);}else{my$msg="REQ - IN MD5, BUT OK! ($diff secs), comando: $comando, clerk: $clerk, area: $area, "."table: $table, product: ".$_->{id}.", qty: ".$_->{qty}.", linenum: ".$_->{line_num}."\n";
&x64_log($msg);
push@ndup_products,$_;}}$X64_CONTROL{$md5}=time;}&x64_log("PRODS TO REQUEST: ".(scalar@ndup_products).", comando: $comando, clerk: $clerk, area: $area, table: $table");
if(!scalar@ndup_products){return;}@prods=();
my%prod_idx=();
for(my$i=0;$i<scalar(@ndup_products);$i++){my$cur=$ndup_products[$i];
my$next=$ndup_products[$i+1];
if(exists$PRODUCTS_EXTRAS->{$cur->{id}}){push@prods,$cur;}else{if(defined$next&&exists$PRODUCTS_EXTRAS->{$next->{id}}){push@prods,$cur;}else{if(exists$prod_idx{$cur->{id}}){$prods[$prod_idx{$cur->{id}}]->{qty}+=$cur->{qty};}else{push@prods,$cur;
$prod_idx{$cur->{id}}=scalar@prods-1;}}}}foreach my $pp(@prods){my$prod=$pp->{id};
my$qty=$pp->{qty};
&_send_product_request_to_ui($machine,$station,$clerk,$area,$table,$prod,$qty);}}}sub station_message{my($machine,$file)=@_;
print STDERR"> station_message - $file\n";
my$cfg=$machine->config()->load_fixed();
my$station=$cfg->{STATION_NUMBER}||"0";
my($station_from,$station_to,$clerk_who_sent)=$file=~/station_message#(\d+)#(\d+)#(\d+)\.txt$/;
if($station eq$station_from){my$db=$machine->database();
&get_clerks_information($db);
local$/=undef;
open(F,"< $file");
my$cnt=<F>;
close(F);
my$clerk_name=exists$CLERKS_INFO->{$clerk_who_sent}?$CLERKS_INFO->{$clerk_who_sent}->{login}:'UNKNOWN';
my$date=&strftime("%Y-%m-%d %H:%M:%S",localtime);
my$full_msg="==========================================\n"."================ MENSAGEM ================\n"."==========================================\n"."DATA         : $date\n"."ENVIADO POR  : $clerk_name\n"."POSTO ORIGEM : $station_from\n"."POSTO DESTINO: $station_to\n"."\n"."$cnt"."==========================================\n";
open(F,"> ".$machine->dir_tmp()."/printer_screen.txt");
print F$full_msg;
close(F);}}sub burn_voucher{my($machine,$file)=@_;
print STDERR"> burn_voucher - $file\n";
if($machine->is_server()){my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
if(!defined$CLOSE_HOUR){my$close_hour=$cfg->{END_BUSINESS_HOUR}||"05:00";
$CLOSE_HOUR=$close_hour;
my@tmp=split/:/,$close_hour;
$CLOSE_HOUR.=":00" if scalar@tmp==2;}my$date=&strftime("%Y-%m-%d %H:%M:%S",localtime);
my($voucher_id,$amount,$unknown_id)=$file=~/burn_voucher#(\d+)#(.*?)#(.*?)\.txt$/;
print STDERR"#### voucher_id: $voucher_id, amount: $amount, unknown_id: $unknown_id\n";
if($voucher_id==0){my$tmp=$db->select("SELECT MAX(id) AS doc_id FROM sales_headers");
my$gen_doc={};
if(scalar@$tmp){my$doc=$db->select("SELECT * FROM sales_headers WHERE id = ?",$tmp->[0]->{doc_id});
$gen_doc=$doc->[0];}my$generated_doc=$gen_doc?$gen_doc->{fo_doc_number}:"";
my$business_date=&date_based_on_closing_hour($date,$CLOSE_HOUR);
my$id=$db->next_id("vouchers");
$db->do("INSERT INTO vouchers VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",$id,$business_date,$date,'2200-01-01',undef,$generated_doc,1,$amount,1,$unknown_id,$date);}else{my$rec=$db->select("SELECT id FROM vouchers WHERE id = ? OR external_id = ?",$voucher_id,$voucher_id,);
if(scalar@$rec){$db->do("UPDATE vouchers SET date_used = ?, was_used_already = 1 WHERE id = ?",$date,$rec->[0]->{id},);}}}else{return 2;}}sub create_voucher{my($machine,$file)=@_;
if($machine->is_server()){print STDERR"> create_voucher - $file\n";
my($orig_doc_id,$created_rvd_gambas_id,$station,$clerk,$customer_id)=("","","","",0);
my@a=split/#/,$file;
if(scalar@a==5){($orig_doc_id,$created_rvd_gambas_id,$station,$clerk)=$file=~/print_voucher#(\d+)#(\d+)#(\d+)#(\d+)\.txt$/;}else{($orig_doc_id,$created_rvd_gambas_id,$station,$clerk,$customer_id)=$file=~/print_voucher#(\d+)#(\d+)#(\d+)#(\d+)#(\d+)\.txt$/;}my$cfg=$machine->config()->load_fixed();
$cfg->{VOUCHER_EXPIRES}||='';
$cfg->{VOUCHERS_SYNC_TOKEN}||='';
$cfg->{VOUCHERS_FOR_APP_ONLY}||=0;
my$doc_serie="";
if($cfg->{VOUCHERS_SYNC_TOKEN}){$doc_serie=$machine->decode_doctype_serie();}if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}if(!defined$CLOSE_HOUR){my$close_hour=$cfg->{END_BUSINESS_HOUR}||"05:00";
$CLOSE_HOUR=$close_hour;
my@tmp=split/:/,$close_hour;
$CLOSE_HOUR.=":00" if scalar@tmp==2;}my$gen_doc={};
my$orig_doc={};
my$db=$machine->database();
if($orig_doc_id){my$doc=$db->select("SELECT * FROM sales_headers WHERE id = ?",$orig_doc_id);
$orig_doc=$doc->[0];}if($created_rvd_gambas_id){my$tmp=$db->select("SELECT MAX(id) AS doc_id FROM sales_headers");
if(scalar@$tmp){my$doc=$db->select("SELECT * FROM sales_headers WHERE id = ?",$tmp->[0]->{doc_id});
$gen_doc=$doc->[0];}}local$/=undef;
open(F,"< $file");
my$cnt=<F>;
$cnt=~s/\n//g;
$cnt=~s/\r//g;
close(F);
if(!$cnt){print STDERR"ERROR! Can't find voucher value in file $file\n";
return;}my$date_created=&strftime("%Y-%m-%d %H:%M:%S",localtime);
my$expires=$cfg->{VOUCHER_EXPIRES}?&_decode_expire($cfg->{VOUCHER_EXPIRES}):'';
my$original_doc=$orig_doc?$orig_doc->{fo_doc_number}:"";
my$generated_doc=$gen_doc?$gen_doc->{fo_doc_number}:"";
my$business_date=&date_based_on_closing_hour($date_created,$CLOSE_HOUR);
my$new_id=$db->next_id("vouchers");
my$cloud_id="";
if($doc_serie){$cloud_id=$doc_serie.$new_id;
$cloud_id=~s/\D//g;}$expires='2300-01-01' if$expires eq"";
$db->do("INSERT INTO vouchers VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",$new_id,$business_date,$date_created,$expires,$original_doc,$generated_doc,0,$cnt,0,$cloud_id,undef,0,0,$customer_id,);
my$inserted_id=$new_id;
my$content="";
my%info=(business_date=>$business_date,date_created=>$date_created,expiration_date=>$expires,value=>$cnt,voucher_num=>$inserted_id,voucher_cloud_num=>$cloud_id,original_doc=>$original_doc,generated_doc=>$generated_doc,);
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('voucher_create.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$print_file;
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,$cloud_id?$cloud_id:$inserted_id,5);
open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}else{return 2;}}sub execute_customer_display{my($machine,$file)=@_;
my($area,$check_id)=$file=~/\/customer_display#(\d+)#(.*?)#/;
my$str="";
eval{local$SIG{ALRM}=sub{die"alarm\n"};
alarm(1);
if(open(F,"< ".$file)){local$/=undef;
$str=<F>;
$str=~s/\n//g;
$str=~s/\r//g;
close(F);
unlink($file);}alarm(0);};
if($@){print STDERR"ERROR ALARM() READING CUSTOMER DISPLAY FILE - $@\n";
return;}my$cfg=$machine->config()->load_fixed();
$cfg->{VIDEO_RECORDER_PORT}||="";
if($cfg->{VIDEO_RECORDER_PORT}){system("/opt/pos/common/bin/send_to_recorder.pl ".$cfg->{VIDEO_RECORDER_PORT}." $area \"".$AREAS_INFO{$area}->[0]."\" $check_id customerdisplay \"$str\"");}$cfg->{Hardware}->{CustomerDisplays}->{Device}||=[];
foreach my $info(@{$cfg->{Hardware}->{CustomerDisplays}->{Device}}){$info->{Model}||='TOSHIBA';
$info->{Baudrate}||='';
$info->{Parity}||='';
$info->{Databits}||='';
$info->{Stopbits}||='';
$info->{RtsCts}||=0;
$info->{DtrDsr}||=0;
$info->{Inactive}||=0;
if($info->{Inactive}){next;}my$obj=undef;
eval{my$class="App::POS::Hardware::CustomerDisplays::".$info->{Model};
$obj=$class->new(info=>$info);};
if($@){print STDERR"ERROR USING DRIVER - ".$info->{Model}." : $@\n";
return;}if($obj->type()eq 'SERIAL'){eval{my$PortObj=Device::SerialPort->new($info->{Device});
if(defined$PortObj){$PortObj->baudrate($info->{Baudrate})if$info->{Baudrate};
$PortObj->parity($info->{Parity})if$info->{Parity};
$PortObj->databits($info->{Databits})if$info->{Databits};
$PortObj->stopbits($info->{Stopbits})if$info->{Stopbits};
$PortObj->rts_active($info->{RtsCts})if$info->{RtsCts};
$PortObj->dtr_active($info->{DtrDsr})if$info->{DtrDsr};
$obj->serial_obj($PortObj);
$obj->write($str);}else{print STDERR"Error (6) on $info->{Device}\n";}};
if($@){print STDERR"Error (6) on $info->{Device} - $@\n";}}elsif($obj->type eq 'VIRTUAL'){$obj->write($str);}else{print STDERR"Customer display's type (".$obj->type().") is not supported at this moment...\n";
return;}}}sub print_offer_ticket{my($machine,$file)=@_;
my($id,$station)=$file=~/\/print_offer_ticket#(\d+)#(\d+)\./;
my$db=$machine->database();
my$cfg=$machine->config()->load_fixed();
my$headers=$db->select(qq{
    SELECT h.*, sh.hash, sh.hash_small
    FROM sales_headers h
    LEFT JOIN sales_hashes sh ON (h.id = sh.header)
    WHERE h.id = ?
  },$id);
my$details=$db->select("SELECT * FROM sales_details WHERE header = ? AND operation_type = 1",$id);
my$footers=$db->select("SELECT * FROM sales_payments WHERE header = ?",$id);
return unless scalar@$headers;
my%offer_lines=();
my@offer_lines=();
{local$/=undef;
open(N,"< $file");
my$cnt=<N>;
chop($cnt);
close(N);
@offer_lines=split/\n/,$cnt;
if(scalar@offer_lines&&$offer_lines[0]!=0){map{$offer_lines{$_}=1}@offer_lines;}}my$clerk_closed=0;
my$h=$headers->[0];
&get_products_information($db);
&get_clerks_information($db);
$cfg->{STATION_PRINT_CHECKS}=exists$cfg->{STATION_PRINT_CHECKS}?$cfg->{STATION_PRINT_CHECKS}:99;
$cfg->{STATION_PRINT_CHECKS}=99 if$cfg->{STATION_PRINT_CHECKS}==$cfg->{STATION_NUMBER};
$cfg->{CHECK_GROUP_PRODUCTS_TEXT}||="";
my$mult=1;
return if$h->{doc_type}==3;
my$template="check_offer";
$clerk_closed=$h->{clerk_close};
my$total_discounts=0;
my$total_offers=0;
if($template){if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}if($cfg->{PRINT_BARCODE_IN_CHECK}){my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$file;
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,"77777".sprintf("%07s",$h->{id}),5);}$h->{clerk_open_name}=exists$CLERKS_INFO->{$h->{clerk_open}}?$CLERKS_INFO->{$h->{clerk_open}}->{login}:'UNKNOWN';
$h->{clerk_close_name}=exists$CLERKS_INFO->{$h->{clerk_close}}?$CLERKS_INFO->{$h->{clerk_close}}->{login}:'UNKNOWN';
my%vat=();
$h->{discount}||="";
my$global_discount_ratio=0;
if($h->{discount}ne ''){my@a=split/\@/,$h->{discount};
$global_discount_ratio=$a[1];}$h->{value}=$h->{value}*$mult;
($h->{hour_opened})=$h->{date}=~/^.*?\s(\d\d\:\d\d)/;
($h->{hour_closed})=$h->{date_closed}=~/^.*?\s(\d\d\:\d\d)/;
my$medias=[];
foreach my $d(sort{$a->{payment_media}cmp$b->{payment_media}}@{$footers}){$d->{payment_media_orig}=$d->{payment_media};
$d->{payment_media}=&my_conv($d->{payment_media},20);
$d->{value}=$d->{value}*$mult;
push@$medias,$d;}$footers=$medias;
my$my_details=[];
my$products_quantities=0;
foreach my $d(@{$details}){if(scalar keys%offer_lines){next unless exists$offer_lines{$d->{gb_identifier}};}next if$d->{quantity}*1==0;
$d->{quantity}=$d->{quantity}*$mult;
$d->{clerk_name}=exists$CLERKS_INFO->{$d->{clerk}}?$CLERKS_INFO->{$d->{clerk}}->{login}:'UNKNOWN';
if(exists$PRODUCTS_INFO->{$d->{product}}){foreach my $k(keys%{$PRODUCTS_INFO->{$d->{product}}}){$d->{"product_".$k}=$PRODUCTS_INFO->{$d->{product}}->{$k};}}$d->{vat}||=0;
if($d->{quantity}*($d->{price}-$d->{discount}-$d->{offer})>0&&$global_discount_ratio>0){my$v=$d->{quantity}*($d->{price}-$d->{discount}-$d->{offer})*$global_discount_ratio;
$d->{discount}+=$v;}$total_discounts+=$d->{discount};
$total_offers+=$d->{offer};
my$price=$d->{price}-$d->{discount}-$d->{offer};
my$wo_vat=$d->{price}-(($d->{price}*$d->{vat})/100);
my$line_total=$d->{quantity}*($price);
$vat{$d->{vat}}->{w_vat}+=sprintf("%.2f",$d->{quantity}*$price);
$vat{$d->{vat}}->{wo_vat}+=sprintf("%.2f",$wo_vat);
$vat{$d->{vat}}->{vat_v}+=sprintf("%.2f",$d->{quantity}*$price-$wo_vat);
$d->{line_total}=sprintf("%7s",sprintf("%.02f",$line_total));
$d->{unit_price}=sprintf("%7s",sprintf("%.02f",$price));
$d->{product_name}=&my_conv($d->{product_name},26);
$d->{txts}=[];
if($d->{free_text}&&$d->{free_text}ne 'EXTRA'){my@t=split/;/,$d->{free_text};
for my $tok(@t){next unless$tok;
$Text::Wrap::columns=38;
my$l=wrap('','',$tok);
my@lines=split/\n/,$l;
for(@lines){push@{$d->{txts}},$_;}}if(scalar@{$d->{txts}}==1&&$d->{txts}->[0]eq 'MENU'){$d->{txts}=[];}}$products_quantities+=$d->{quantity}unless exists$PRODUCTS_EXTRAS->{$d->{product}};
push@$my_details,$d;}$details=$my_details;
my%info=(check=>{headers=>$h,details=>$details,payments=>$footers,products_number=>scalar@$details,products_quantities=>$products_quantities,total_discounts=>$total_discounts,},);
my$content="";
$info{vat_info}=\%vat;
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template.'.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}sub reprint_orders{my($machine,$file)=@_;
if($machine->is_server()){my($station,$printer,$number_to_print,$type)=$file=~/\/reprint_orders#(\d+)#(\d+)#(\d+)#(.*?)\./;
print STDERR"##> $file - $station - $printer - $number_to_print\n";
my$dir_to_use=$type eq 'orders'?'order_archive':'print_checks_archive';
my$archive_dir=$machine->dir_server().'/server/data/'.$dir_to_use.'/'.$station."/".$printer;
return 1 unless-d$archive_dir;
$number_to_print=3 unless$number_to_print;
my$done_count=0;
opendir(DIR,$archive_dir);
my@dir=readdir(DIR);
closedir(DIR);
@dir=sort{-M"$archive_dir/$a"<=>-M"$archive_dir/$b"}(@dir);
foreach my $file(@dir){next if$file=~/^\./;
if($done_count>10){unlink$archive_dir."/".$file;
return;}if($done_count<$number_to_print){&copy($archive_dir."/".$file,$machine->server_dir_tmp()."/".$file);}$done_count++;}}else{return 2;}}sub print_reprint_checks{my($machine,$file)=@_;
my($station,$clerk,$business_date)=$file=~/\/print_reprint_checks#(\d+)#(\d+)#(.*?)#/;
print STDERR"##> $file - $station - $clerk - $business_date\n";
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my$db=$machine->database();
&get_clerks_information($db);
my%info=();
my$content="";
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{business_date}=$business_date;
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
my$doc_types=$machine->config()->doc_types_by_id();
my@recs=();
my$total=0;
if(open(F,"< $file")){while(<F>){chop;
my($id,$doc_type,$fo_doc_num,$datetime,$tmp1,$tmp2,$value,$clerk_open,$clerk_close)=split/#/,$_;
my$doc_type_name=$doc_types->{$doc_type};
$total+=$value;
push@recs,{id=>$id,doc_type=>$doc_type,doc_type_name=>$doc_type_name,fo_doc_number=>$fo_doc_num,datetime=>$datetime,area_table=>defined$tmp1&&$tmp1?$tmp1:"        ",tmp2=>$tmp2,value=>$value,clerk_open=>$clerk_open,clerk_close=>$clerk_close,};}close(F);}$info{tickets}=\@recs;
$info{total}=$total;
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_reprint_checks.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub print_customer_profile{my($machine,$file)=@_;
my($customer_id,$to_screen,$station,$clerk)=$file=~/\/print_customer_profile#(\d+)#(\d+)#(\d+)#(\d+)\./;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my$db=$machine->database();
my$cfg=$machine->config()->load_fixed();
&get_clerks_information($db);
&get_products_information($db);
my%info=();
my$content="";
my$customer=$db->select("SELECT * FROM customers WHERE id = ?",$customer_id);
if(scalar@$customer){$info{name}=$customer->[0]->{name};
$info{nif}=$customer->[0]->{nif};
$customer->[0]->{products}//="";
$info{associated_products}=[];
if($customer->[0]->{products}){my$prods=$db->select("SELECT id, name FROM products WHERE id IN (".$customer->[0]->{products}.") ORDER BY name");
$info{associated_products}=$prods;}}my$sth=$db->dbh()->prepare(qq{
    SELECT h.id, d.clerk_name, COUNT(1)
    FROM sales_details d
    LEFT JOIN sales_headers h ON (d.header = h.id)
    WHERE h.customer_id = ? AND d.operation_type = 1 AND h.doc_type IN (1, 5, 90, 12, 7)
    GROUP BY h.id, d.clerk_name
  });
$sth->execute($customer_id);
my%qttys_control=();
my%quantities_sold_by_clerk=();
while(my$row=$sth->fetchrow_hashref){if(!exists$qttys_control{$row->{clerk_name}."#".$row->{id}}){$qttys_control{$row->{clerk_name}."#".$row->{id}}=1;
$quantities_sold_by_clerk{$row->{clerk_name}}++;}}$sth=$db->dbh()->prepare(qq{
    SELECT d.product, h.customer_nif, h.customer,
      SUM(d.quantity) AS qtty, MIN(h.business_date) AS first_visit,
      MAX(h.business_date) AS last_visit
    FROM sales_details d
    LEFT JOIN sales_headers h ON (d.header = h.id)
    WHERE h.customer_id = ? AND d.operation_type = 1 AND h.doc_type IN (1, 5, 90, 12, 7)
    GROUP BY d.product, h.customer_nif, h.customer
    ORDER BY SUM(d.quantity) DESC
  });
$sth->execute($customer_id);
my%sales_by_factor_tmp=();
my%son_products=();
while(my$row=$sth->fetchrow_hashref){$info{name}=$row->{customer}unless exists$info{name};
$info{first_visit}=$row->{first_visit}unless exists$info{first_visit};
$info{last_visit}=$row->{last_visit}unless exists$info{last_visit};
$info{nif}=$row->{customer_nif};
my$product_name=exists$PRODUCTS_INFO->{$row->{product}}?$PRODUCTS_INFO->{$row->{product}}->{name}:'UNKNOWN';
push@{$info{products}},{id=>$row->{product},quantity=>$row->{qtty},name=>$product_name,last_purchase=>$row->{last_visit},}}$info{qttys_by_clerk}=\%quantities_sold_by_clerk;
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_customer_profile.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
if($to_screen){$content=~s/\@.*?\@//g;
open(F,"> ".$machine->dir_tmp()."/printer_screen#$station.txt");
print F$content;
close(F);}else{my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}sub print_birthdates{my($machine,$file)=@_;
my($date_start,$date_end,$to_screen,$station,$clerk)=$file=~/\/print_birthdates#(.*?)#(.*?)#(\d+)#(\d+)#(\d+)\./;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my$db=$machine->database();
my$cfg=$machine->config()->load_fixed();
&get_clerks_information($db);
my$cur_year=&strftime("%Y",localtime);
my%info=();
my$content="";
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{date_start}=$date_start;
$info{date_end}=$date_end;
my$q=qq{
    SELECT *, (NOW()::date - birthdate::date) / 365 AS age
    FROM customers
    WHERE (NOW()::date - birthdate::date) / 365 > 0 AND
    ('$cur_year' || '-' || DATE_PART('month', birthdate) || '-' || DATE_PART('day', birthdate))::DATE <= '$date_end' AND
    ('$cur_year' || '-' || DATE_PART('month', birthdate) || '-' || DATE_PART('day', birthdate))::DATE >= '$date_start'
  };
my$recs=$db->select($q);
my$year=&strftime("%Y",localtime);
foreach my $r(@$recs){my$date=$r->{birthdate};
$date=~s/^\d\d\d\d/$year/;
$r->{contact}||="";
push@{$info{birthdates}->{$date}},{%$r};}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_birthdays.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
if($to_screen){$content=~s/\@.*?\@//g;
open(F,"> ".$machine->dir_tmp()."/printer_screen#$station.txt");
print F$content;
close(F);}else{my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}sub print_schedule{my($machine,$file)=@_;
my($schedule,$date_start,$date_end,$to_screen,$station,$clerk)=$file=~/\/print_schedule#(\d+)#(.*?)#(.*?)#(\d+)#(\d+)#(\d+)\./;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my$db=$machine->database();
my$cfg=$machine->config()->load_fixed();
my$schedules_tmp=$cfg->{SCHEDULES}||"";
my$schedules=[];
if($schedules_tmp){if(!ref$schedules_tmp){push@$schedules,$schedules_tmp;}else{$schedules=$schedules_tmp;}}if(!scalar@$schedules){print STDERR"No schedules available...\n";
return;}&get_clerks_information($db);
my%info=();
my$content="";
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{name}=$schedules->[$schedule];
$info{date_start}=$date_start;
$info{date_end}=$date_end;
my$recs=$db->select(qq{
    SELECT *
    FROM schedules
    WHERE schedule = ? AND schedule_date >= ? AND schedule_date <= ? ORDER BY start_hour, end_hour
  },$schedule,$date_start,$date_end);
foreach my $r(@$recs){push@{$info{schedules}->{$r->{schedule_date}}},{start_hour=>$r->{start_hour},end_hour=>$r->{end_hour},description=>$r->{description},};}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_schedule.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
if($to_screen){$content=~s/\@.*?\@//g;
open(F,"> ".$machine->dir_tmp()."/printer_screen#$station.txt");
print F$content;
close(F);}else{my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}sub report_products_list{my($machine,$file)=@_;
my($report_type,$gid,$parent_gid,$code,$price,$sort,$station,$clerk)=$file=~/\/report_products_list#(.*?)#(.*?)#(.*?)#(.*?)#(.*?)#(.*?)#(\d+)#(\d+)\./;
print STDERR"##> $file - $station - $clerk - $report_type - $gid - $parent_gid - $code - $price - $sort\n";
my$db=$machine->database();
my$where="";
my@groups=();
my$group_name="";
my$department_name="";
if($report_type eq 'group'){if($parent_gid eq '0'){my$recs=$db->select("SELECT name FROM groups WHERE id = $gid");
$group_name=$recs->[0]->{name};
$department_name="";
$recs=$db->select("SELECT id FROM groups WHERE parent = $gid AND deleted = 0");
map{push@groups,$_->{id}}@$recs;}else{my$recs=$db->select("SELECT name, parent FROM groups WHERE id = $gid");
$department_name=$recs->[0]->{name};
$recs=$db->select("SELECT name FROM groups WHERE id = ".$recs->[0]->{parent});
$group_name=$recs->[0]->{name};
push@groups,$gid;}if(scalar@groups){$where.=" AND pg.group_id IN (".join(",",@groups).")";}}if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}&get_clerks_information($db);
my%info=();
my$content="";
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{group_name}=$group_name;
$info{department_name}=$department_name;
my$code_field=$code eq 'handy_plu'?'handy_plu':'id';
my$sort_field="name";
if($sort eq 'code'){$sort_field=$code_field;}elsif($sort eq 'price'){$sort_field="sell_price".$price;}my$recs=$db->select(qq{
    SELECT p.id, p.$code_field AS product_code, p.sell_price$price AS product_price, p.name AS product_name
    FROM products p
    LEFT JOIN product_groups pg ON (pg.product_id = p.id)
    WHERE p.deleted = 0$where
    ORDER BY p.$sort_field
  });
my%tmp;
for(@$recs){next if exists$tmp{$_->{id}};
$tmp{$_->{id}}=1;
$_->{product_name}=string_normalize(my_conv($_->{product_name}));}$info{records}=$recs;
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_product_list.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub print_to_main_printer{my($machine,$file)=@_;
my($station,$clerk)=$file=~/\/print_to_main_printer#(\d+)#(\d+)#/;
open(F,"< $file");
local$/=undef;
my$content=<F>;
close(F);
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub print_tech_report{my($machine,$file)=@_;
my($station,$clerk)=$file=~/\/print_tech_report#(\d+)#(\d+)\./;
print STDERR"##> $file - $station - $clerk\n";
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my$db=$machine->database();
&get_clerks_information($db);
my%info=();
my$content="";
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_tech.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub print_daily_specials{my($machine,$file)=@_;
my($station,$clerk)=$file=~/\/print_daily_specials#(\d+)#(\d+)#.*?\./;
print STDERR"##> $file - $station - $clerk\n";
if(open(F,"< $file")){my$db=$machine->database();
&get_products_information($db);
my@products=();
while(<F>){chop;
next unless$_;
my@a=split/#/;
my$name=exists$PRODUCTS_INFO->{$a[0]}?$PRODUCTS_INFO->{$a[0]}->{name}:'UNKNOWN';
push@products,{id=>$a[0],price=>$a[1],name=>$name,};}close(F);
if(scalar@products){my$content="";
my%info;
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{products}=\@products;
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('daily_specials.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}}sub check_reprint{my($machine,$file)=@_;
return 2 unless$machine->is_server();
my($id,$meals,$station,$check_date,$time,$lang,$to_screen,$is_dup,$pdf_only)=$file=~/\/reprint_check#(\d+)#(\d+)#(\d+)#(.*?)#(.*?)#(.*?)#(\d+)#(\d+)#(\d+)\./;
my$email_addr="";
if($lang=~/=/){my@a=split/=/,$lang;
$lang=$a[0];
$email_addr=$a[1];}my($y,$m)=$check_date=~/^(\d+)\-(\d+)\-/;
my$db=$machine->database();
my$cfg=$machine->config()->load_fixed();
if($lang){&get_products_translations($db);}$pdf_only||=0;
my$headers=$db->select(qq{
    SELECT h.*, sh.hash, sh.hash_small, sha1.key_value AS check_name, sha2.key_value AS obs,
      sha3.key_value AS canceled, sha4.key_value AS car_plate, sha5.key_value AS delivery_site,
      sha6.key_value AS load_site, sha7.key_value AS kept_in_source,
      sha8.key_value AS print_to_simplified_invoice, sha9.key_value AS at_code,
      sha10.key_value AS delivery_hour, sha11.key_value AS load_hour, sha12.key_value AS load_date,
      sha13.key_value AS delivery_date, sha14.key_value AS manualdoc_serie,
      sha15.key_value AS manualdoc_total_w_vat, sha16.key_value AS manualdoc_total_vat,
      sha17.key_value AS manualdoc_docnum, sha18.key_value AS manualdoc_date,
      sha19.key_value AS manualdoc_type, sha20.key_value AS lines_source_assoc,
      sha21.key_value AS refunded, sha22.key_value AS external_doc_name,
      sha23.key_value AS to_external_printer, sha24.key_value AS in_training_mode
    FROM sales_headers h
    LEFT JOIN sales_headers_aux sha1  ON (h.id = sha1.header  AND sha1.key_name  = 'check_name')
    LEFT JOIN sales_headers_aux sha2  ON (h.id = sha2.header  AND sha2.key_name  = 'obs')
    LEFT JOIN sales_headers_aux sha3  ON (h.id = sha3.header  AND sha3.key_name  = 'canceled')
    LEFT JOIN sales_headers_aux sha4  ON (h.id = sha4.header  AND sha4.key_name  = 'car_plate')
    LEFT JOIN sales_headers_aux sha5  ON (h.id = sha5.header  AND sha5.key_name  = 'delivery_site')
    LEFT JOIN sales_headers_aux sha6  ON (h.id = sha6.header  AND sha6.key_name  = 'load_site')
    LEFT JOIN sales_headers_aux sha7  ON (h.id = sha7.header  AND sha7.key_name  = 'kept_in_source')
    LEFT JOIN sales_headers_aux sha8  ON (h.id = sha8.header  AND sha8.key_name  = 'print_to_simplified_invoice')
    LEFT JOIN sales_headers_aux sha9  ON (h.id = sha9.header  AND sha9.key_name  = 'at_code')
    LEFT JOIN sales_headers_aux sha10 ON (h.id = sha10.header AND sha10.key_name = 'delivery_hour')
    LEFT JOIN sales_headers_aux sha11 ON (h.id = sha11.header AND sha11.key_name = 'load_hour')
    LEFT JOIN sales_headers_aux sha12 ON (h.id = sha12.header AND sha12.key_name = 'load_date')
    LEFT JOIN sales_headers_aux sha13 ON (h.id = sha13.header AND sha13.key_name = 'delivery_date')
    LEFT JOIN sales_headers_aux sha14 ON (h.id = sha14.header AND sha14.key_name = 'manualdoc_serie')
    LEFT JOIN sales_headers_aux sha15 ON (h.id = sha15.header AND sha15.key_name = 'manualdoc_total_w_vat')
    LEFT JOIN sales_headers_aux sha16 ON (h.id = sha16.header AND sha16.key_name = 'manualdoc_total_vat')
    LEFT JOIN sales_headers_aux sha17 ON (h.id = sha17.header AND sha17.key_name = 'manualdoc_docnum')
    LEFT JOIN sales_headers_aux sha18 ON (h.id = sha18.header AND sha18.key_name = 'manualdoc_date')
    LEFT JOIN sales_headers_aux sha19 ON (h.id = sha19.header AND sha19.key_name = 'manualdoc_type')
    LEFT JOIN sales_headers_aux sha20 ON (h.id = sha20.header AND sha20.key_name = 'lines_source_assoc')
    LEFT JOIN sales_headers_aux sha21 ON (h.id = sha21.header AND sha21.key_name = 'refunded')
    LEFT JOIN sales_headers_aux sha22 ON (h.id = sha22.header AND sha22.key_name = 'external_doc_name')
    LEFT JOIN sales_headers_aux sha23 ON (h.id = sha23.header AND sha23.key_name = 'to_external_printer')
    LEFT JOIN sales_headers_aux sha24 ON (h.id = sha23.header AND sha24.key_name = 'in_training_mode')
    LEFT JOIN sales_hashes sh ON (h.id = sh.header)
    WHERE h.id = ?
  },$id);
return unless scalar@$headers;
my$details=$db->select(qq{
    SELECT sd.*, sda1.key_value AS original_price, sde.extra_data
    FROM sales_details sd
    LEFT JOIN sales_details_extra_data sde ON (sde.detail_gbi = sd.gb_identifier)
    LEFT JOIN sales_details_aux sda1 ON (sda1.gb_identifier = sd.gb_identifier AND sda1.key_name = 'original_price')
    WHERE sd.header = ? AND sd.operation_type = 1
    ORDER BY sd.idx
  },$id);
my$footers=$db->select(qq{
    SELECT * FROM sales_payments WHERE header = ?
  },$id);
my%extra_data=();
my$extra_data_tmp=$db->select(qq{
    SELECT * FROM sales_headers_aux WHERE header = ?
  },$id);
for(@$extra_data_tmp){$extra_data{$_->{key_name}}=$_->{key_value};}$cfg->{SAFT_PRINT_TRANSPORT_DOCS}||=0;
$cfg->{LANG}||='PT';
$cfg->{STATION_PRINT_CHECKS}=exists$cfg->{STATION_PRINT_CHECKS}?$cfg->{STATION_PRINT_CHECKS}:99;
$cfg->{STATION_PRINT_CHECKS}=99 if$cfg->{STATION_PRINT_CHECKS}==$cfg->{STATION_NUMBER};
$cfg->{CHECK_GROUP_PRODUCTS_TEXT}||="";
$cfg->{WEIGHT_AS_INVOICE_ID}||=0;
$cfg->{TAX_TO_KEEP_IN_SOURCE}||=0;
$cfg->{CUSTOMER_VAT_ISENTION}||="";
$cfg->{SHOW_ZERO_PRICE_IN_INVOICE}||=0;
$cfg->{PRINT_EXTRAS_IN_DOCUMENTS}||=0;
$cfg->{MENUS_OPTIMIZED_FOR_GROUPS}||=0;
$cfg->{PRINT_GROUP_WEIGHT_QUANTITIES}||=0;
$cfg->{PRINT_CHECK_PRODUCTS_MODE}||=1;
$cfg->{INTERNET_DOCUMENTS_FORMAT}||='';
$cfg->{SHOW_ZERO_PRICE_IN_INVOICE}=1 if$headers->[0]->{doc_type}==10;
my$pt_certified_software=-e$machine->dir()."/common/skins/".$cfg->{LANG}."/sign_server"?1:0;
if($pt_certified_software){if($headers->[0]->{doc_type}eq '10'||$headers->[0]->{doc_type}eq '11'){$headers->[0]->{at_code}||=&translate($machine,"SEM CODIGO AT");}}my$clerk_closed=0;
my$h=$headers->[0];
$h->{refunded}||=0;
$h->{canceled}=1 if$h->{refunded};
$h->{meals}=$meals;
$h->{training_mode}=0;
if(exists$extra_data{in_training_mode}&&$extra_data{in_training_mode}&&scalar@$SOFTWARE_INFO){$h->{training_mode}=1;
$h->{training_software_number}=$SOFTWARE_INFO->[0];
$h->{training_software_company}=$SOFTWARE_INFO->[1];
$h->{training_software_version}=$SOFTWARE_INFO->[2];
$h->{training_software_nif}=$SOFTWARE_INFO->[3];}my$business_date=$h->{business_date};
$machine->get_customer_info($db,$h->{customer_id},$h)if$h->{customer_id};
&get_products_information($db);
$Text::Wrap::columns=38;
if(!defined$VAT_ISENTIONS){my%t=();
my$tmp=$db->select(qq{
      SELECT id, vat_isention
      FROM products
      WHERE vat_isention <> '' AND vat_isention <> '0'
    });
for(@$tmp){next if$_->{vat_isention}=~/^\s+$/;
my$l=wrap('','',$_->{vat_isention});
$t{$l}=1;
$VAT_ISENTIONS->{$_->{id}}=$l;}my$idx=1;
for(sort keys%t){$VAT_ISENTIONS_IDX->{$_}=$idx++;}$TAXFREE_VAT_ISENTION=wrap('','',$TAXFREE_VAT_ISENTION);
$VAT_ISENTIONS_IDX->{$TAXFREE_VAT_ISENTION}=$TAXFREE_VAT_ISENTION_IDX;}&get_clerks_information($db);
my$custom_invoice_id="";
if($cfg->{WEIGHT_AS_INVOICE_ID}){$custom_invoice_id=$h->{customer}||"";}$h->{custom_invoice_id}=$custom_invoice_id;
my$mult=$h->{doc_type}==2||$h->{doc_type}==13?-1:1;
return if$h->{doc_type}==3;
my$template="";
if($h->{doc_type}!=8){if(-e$machine->dir().'/common/print_templates/'.$cfg->{LANG}."/check_reprint.tmpl"){$template=$cfg->{LANG}."/check_reprint";}else{$template="check_reprint";}}else{if(-e$machine->dir().'/common/print_templates/'.$cfg->{LANG}."/customer_payment_reprint.tmpl"){$template=$cfg->{LANG}."/customer_payment_reprint";}else{$template="customer_payment_reprint";}}$clerk_closed=$h->{clerk_close};
my$total_discounts=0;
my$total_offers=0;
if($template){if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}if($cfg->{PRINT_BARCODE_IN_CHECK}){my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$file;
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,"77777".sprintf("%07s",$h->{id}),5);}&set_owner_information($machine,$h);
$h->{doc_via}=$is_dup?2:1;
$h->{clerk_open_name}=exists$CLERKS_INFO->{$h->{clerk_open}}?$CLERKS_INFO->{$h->{clerk_open}}->{login}:'UNKNOWN';
$h->{clerk_close_name}=exists$CLERKS_INFO->{$h->{clerk_close}}?$CLERKS_INFO->{$h->{clerk_close}}->{login}:'UNKNOWN';
my%vat=();
$h->{discount}||="";
my$global_discount_ratio=0;
if($h->{discount}ne ''){my@a=split/\@/,$h->{discount};
$global_discount_ratio=$a[1];}my$cur_date=&strftime("%Y-%m-%d",localtime);
my$cur_hour=&strftime("%H:%M:%S",localtime);
$h->{cur_date}=$cur_date;
$h->{cur_hour}=$cur_hour;
$h->{area_name}=exists$AREAS_INFO{$h->{area}}?$AREAS_INFO{$h->{area}}->[0]:'<UNKNOWN>';
$h->{area_tables}=exists$AREAS_INFO{$h->{area}}?$AREAS_INFO{$h->{area}}->[1]:0;
$h->{table_number}=$h->{table_num};
$h->{print_to_simplified_invoice}||=0;
$h->{value}=$h->{value}*$mult;
$h->{meals_txt}=&translate($machine,$cfg->{CHECK_GROUP_PRODUCTS_TEXT});
($h->{hour_opened})=$h->{date}=~/^.*?\s(\d\d\:\d\d)/;
($h->{hour_closed})=$h->{date_closed}=~/^.*?\s(\d\d\:\d\d)/;
$h->{station_close_name}=exists$STATION_NAMES->{$h->{station_close}}?$STATION_NAMES->{$h->{station_close}}:$h->{station_close};
$h->{station_open_name}=exists$STATION_NAMES->{$h->{station_open}}?$STATION_NAMES->{$h->{station_open}}:$h->{station_open};
my$medias=[];
foreach my $d(sort{$a->{payment_media}cmp$b->{payment_media}}@{$footers}){$d->{payment_media_orig}=$d->{payment_media};
$d->{payment_media}=&my_conv($d->{payment_media},20);
$d->{value}=$d->{value}*$mult;
push@$medias,$d;}$footers=$medias;
my$my_details=[];
my%vat_isentions=();
my$use_print_priorities=0;
$extra_data{to_customer_account}//=0;
if($h->{doc_type}==7&&$h->{customer_id}&&$extra_data{to_customer_account}==1){my$q=qq{
        SELECT SUM(CASE WHEN sha3.key_value = '1' THEN 0 ELSE sh.doc_value END) AS pending
        FROM document_history sh
        LEFT JOIN sales_headers h ON (sh.header = h.id)
        LEFT JOIN sales_headers_aux sha3 ON (sh.header = sha3.header AND sha3.key_name = 'canceled')
        WHERE sh.doc_type <> 5 AND h.customer_id = ? AND sh.id < ?
      };
my@rr=$db->select($q,$h->{customer_id},$h->{id});
if(scalar@rr){$h->{customer_pending_amount}=$rr[0]->{pending};}}$h->{lines_source_assoc}||="";
my%ft_gt_assocs=();
my$ft_gt_idx;
my%ft_gt_idx=();
if($h->{doc_type}==7&&$h->{lines_source_assoc}){my@a=split/,/,$h->{lines_source_assoc};
for(@a){my@b=split/_/,$_;
if(!exists$ft_gt_idx{$b[1]}){$ft_gt_idx++;
$ft_gt_idx{$b[1]}=$ft_gt_idx;}$ft_gt_assocs{$b[0]}={gbi=>$b[1],idx=>$ft_gt_idx{$b[1]}};}}my$check_has_menus=0;
my%t=();
foreach my $d(@{$details}){if($d->{free_text}eq 'MENU'){$d->{product_type}='MENU';}elsif($d->{free_text}eq 'EXTRA'){$d->{product_type}='EXTRA';}else{$d->{product_type}='';}$check_has_menus=1 if exists$PRODUCTS_MENUS->{$d->{product}};
next if$d->{product_name}=~/^\-\-\-\-\-\-\-\-\-\-/;
$d->{printing_priority}=$PRINTING_PRIORITIES->{$d->{product}};
$d->{is_menu}=exists$PRODUCTS_MENUS->{$d->{product}}&&$d->{product_type}ne 'MENU'?1:0;
if(!$cfg->{SHOW_ZERO_PRICE_IN_INVOICE}&&!$d->{is_menu}){next unless$d->{price}*1!=0;}$d->{discount}||=0;
$d->{offer}||=0;
$use_print_priorities=1 if$PRINTING_PRIORITIES->{$d->{product}};
my$txt=exists$d->{extra_data}&&defined$d->{extra_data}&&$d->{extra_data}?$d->{extra_data}:"";
my$key=$d->{vat}."#".$d->{product}."#".($d->{price}*1)."#".$d->{discount}."#".$txt;
if($d->{is_menu}||$d->{product_type}eq 'MENU'){$key=$d->{gb_identifier};}if($PRODUCTS_INFO->{$d->{product}}->{is_weight}&&!$cfg->{PRINT_GROUP_WEIGHT_QUANTITIES}){$key=$d->{gb_identifier};}if($d->{is_menu}||$d->{free_text}eq 'MENU'){$key=$d->{gb_identifier};}if(%ft_gt_assocs){$key.="#".$ft_gt_assocs{$d->{gb_identifier}}->{gbi};}if(exists$t{$key}){$t{$key}->{quantity}+=$d->{quantity};}else{$t{$key}=$d;}}@$details=values%t;
my$splited_check=exists$extra_data{splited_check}&&$extra_data{splited_check}?1:0;
if($check_has_menus&&!$cfg->{MENUS_OPTIMIZED_FOR_GROUPS}){my$last_key="";
my$last_menu_price=0;
my%to_ignore=();
my%grouping=();
my$count=-1;
my$idx=0;
my%t=();
my%t2=();
my%menu_group=();
my%menu_keys=();
my%menu_qttys=();
my$last_menu_gbi="";
my$last_menu_key="";
foreach my $d(sort{$a->{idx}<=>$b->{idx}}@$details){if($d->{is_menu}){$last_menu_gbi=$d->{gb_identifier};
$menu_group{$last_menu_gbi}=$d->{vat}."#".$d->{product}."#".($d->{price}*1)."#".$d->{discount}.",";
$menu_qttys{$last_menu_gbi}+=$d->{quantity};}elsif($d->{product_type}eq 'MENU'){my$key=$d->{vat}."#".$d->{product}."#".($d->{price}*1)."#".$d->{discount}.",";
$menu_group{$last_menu_gbi}.=$key;}}foreach my $k(keys%menu_group){$menu_keys{$menu_group{$k}}+=$menu_qttys{$k};}my%did_keys=();
my%did_grouped_menus_id=();
my%did_components=();
my%did_details=();
my$qty_increment=0;
my@menu_new_details=();
my$prev_menu;
my$prev_menu_key="";
my$qty_hardcoded=0;
foreach my $d(sort{$a->{idx}<=>$b->{idx}}@$details){my$gbi=$d->{gb_identifier};
if($d->{is_menu}){$prev_menu=$d;
my$key=$menu_group{$gbi};
if(defined$key){$d->{menus_grouping_key}=$key;
if(!exists$did_grouped_menus_id{$key}){$d->{quantity}=exists$menu_keys{$key}?$menu_keys{$key}:1;
$d->{menu_original_price}=$d->{menu_original_price}*$d->{quantity}if exists$d->{menu_original_price};
$did_keys{$key}=$d->{quantity};
push@menu_new_details,$d;}$did_grouped_menus_id{$key}++;}else{push@menu_new_details,$d;}}elsif($d->{product_type}eq 'MENU'){if(!exists$prev_menu->{menus_grouping_key}){push@menu_new_details,$d;}elsif(defined$prev_menu&&!exists$did_grouped_menus_id{$prev_menu->{menus_grouping_key}}){push@menu_new_details,$d;}else{if($did_grouped_menus_id{$prev_menu->{menus_grouping_key}}==1){$d->{quantity}=$prev_menu->{quantity};
push@menu_new_details,$d;}}}else{push@menu_new_details,$d;}}$details=\@menu_new_details;}$use_print_priorities=0 if$check_has_menus;
if($use_print_priorities){my@t_tmp=reverse sort{$a->{gb_identifier}cmp$b->{gb_identifier}}@$details;
my$idx=1;
for(@t_tmp){$_->{printing_priority}||=0;
$_->{printing_priority}=$_->{printing_priority}?$_->{printing_priority}:$idx++;}@$details=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@t_tmp;}else{@$details=sort{$a->{idx}<=>$b->{idx}}@$details;}my$total_wo_discount=0;
my$total_wo_discount_wo_vat=0;
my$total_wo_vat=0;
my$total_wo_vat2=0;
my$products_quantities=0;
my$total_vat=0;
my$total_discount_wo_vat=0;
my$last_menu="";
my$new_menu_scheme=0;
my$total_w_vat=0;
$Text::Wrap::columns=38;
foreach my $d(@{$details}){next if$d->{quantity}*1==0;
next if exists$PRODUCTS_MESSAGES->{$d->{product}}&&$d->{price}==0;
$last_menu=$d if$d->{is_menu};
$d->{quantity}=$d->{quantity}*$mult;
$d->{vat}||=0;
if(!$cfg->{PRINT_EXTRAS_IN_DOCUMENTS}){next if exists$PRODUCTS_EXTRAS->{$d->{product}}&&$d->{price}==0;}if($lang&&exists$PRODUCTS_TRANSLATIONS->{$d->{product}}->{$lang}){$d->{product_name}=&my_conv($PRODUCTS_TRANSLATIONS->{$d->{product}}->{$lang},26);}else{$d->{product_name}=&my_conv($d->{product_name},26);}if(exists$extra_data{customer_vat_isention}){my$vat_idx=10;
$d->{product_name}="(".$vat_idx.") ".$d->{product_name};
$vat_isentions{$vat_idx}=wrap('','',$extra_data{customer_vat_isention});}elsif(exists$VAT_ISENTIONS->{$d->{product}}&&$VAT_ISENTIONS->{$d->{product}}ne '0'&&$d->{vat}*1==0){$d->{product_name}="(".$VAT_ISENTIONS_IDX->{$VAT_ISENTIONS->{$d->{product}}}.") ".$d->{product_name};
$vat_isentions{$VAT_ISENTIONS_IDX->{$VAT_ISENTIONS->{$d->{product}}}}=$VAT_ISENTIONS->{$d->{product}};}if(exists$extra_data{taxfree_total}&&$extra_data{taxfree_total}){$d->{product_name}="(".$VAT_ISENTIONS_IDX->{$TAXFREE_VAT_ISENTION}.") ".$d->{product_name};
$vat_isentions{$TAXFREE_VAT_ISENTION_IDX}=$TAXFREE_VAT_ISENTION;}if(%ft_gt_assocs&&exists$ft_gt_assocs{$d->{gb_identifier}}){$d->{product_name}="[".$ft_gt_assocs{$d->{gb_identifier}}->{idx}."] ".$d->{product_name};}$d->{is_weight}=$PRODUCTS_INFO->{$d->{product}}->{is_weight};
$d->{is_service}=$PRODUCTS_INFO->{$d->{product}}->{is_service};
$d->{txts}=[];
if(defined$d->{extra_data}&&$d->{extra_data}){my@t=split/;/,$d->{extra_data};
for my $tok(@t){next unless$tok;
$Text::Wrap::columns=38;
my$l=wrap('','',$tok);
my@lines=split/\n/,$l;
for(@lines){push@{$d->{txts}},$_;}}}my$sem_iva=my_round($d->{quantity}*$d->{price_wo_vat},$cfg->{DECIMAL_PLACES}||2);
my$com_iva=my_round($d->{quantity}*$d->{price},$cfg->{DECIMAL_PLACES}||2);
my$desconto_com_iva=0;
my$desconto_sem_iva=0;
if($d->{price}!=0){$d->{price_wo_vat}||=1;
$desconto_com_iva=$d->{quantity}*$d->{price}*($d->{discount}/$d->{price_wo_vat});
$desconto_sem_iva=$d->{quantity}*$d->{discount};}$total_discount_wo_vat+=$desconto_sem_iva;
$total_wo_vat+=$d->{quantity}*$d->{price_wo_vat};
$com_iva=$com_iva-$desconto_com_iva;
$sem_iva=$sem_iva-$desconto_sem_iva;
$total_wo_vat2+=($d->{quantity}*$d->{price_wo_vat})-($d->{quantity}*$d->{discount});
my$valor_iva=$com_iva-$sem_iva;
$d->{line_total_wo_vat}=$d->{price_wo_vat}*$d->{quantity};
$d->{unit_price_wo_vat}=$d->{price_wo_vat};
$d->{line_total}=$d->{price}*$d->{quantity};
$d->{unit_price}=$d->{price};
$d->{discount_wo_vat}=$desconto_sem_iva;
$d->{quantity}=$d->{quantity};
$d->{line_total_w_discount}=$d->{line_total}-my_round($desconto_com_iva,$cfg->{DECIMAL_PLACES}||2);
$d->{line_total_w_discount_not_rounded}=$d->{line_total}-$desconto_com_iva;
$d->{line_total_wo_vat_w_discount}=$d->{line_total_wo_vat}-my_round($desconto_sem_iva,$cfg->{DECIMAL_PLACES}||2);
$d->{line_total_wo_vat_w_discount_not_rounded}=$d->{line_total_wo_vat}-$desconto_sem_iva;
$total_w_vat+=$d->{line_total_w_discount_not_rounded};
$d->{handy_plu}=$PRODUCTS_INFO->{$d->{product}}->{handy_plu};
if(exists$d->{menu_total_price}){$d->{menu_total_price}=$d->{menu_total_price}*$d->{quantity};}if($d->{vat}*1!=0){$vat{$d->{vat}}->{w_vat}+=$d->{line_total_w_discount};
$vat{$d->{vat}}->{wo_vat}+=$desconto_com_iva>0?my_round($sem_iva,$cfg->{DECIMAL_PLACES}||2):$sem_iva;}else{$vat{$d->{vat}}->{wo_vat}+=$sem_iva*$mult;
$vat{$d->{vat}}->{w_vat}+=$sem_iva*$mult;}$total_discounts+=$desconto_com_iva;
my$tt=my_round($d->{line_total_wo_vat},$cfg->{DECIMAL_PLACES}||2)*(1+$d->{vat}/100);
$total_wo_discount+=my_round($tt,$cfg->{DECIMAL_PLACES}||2);
$total_wo_discount_wo_vat+=my_round($d->{line_total_wo_vat},$cfg->{DECIMAL_PLACES}||2);
$products_quantities+=$d->{quantity}unless exists$PRODUCTS_EXTRAS->{$d->{product}};
if(!$d->{is_menu}&&$d->{product_type}ne 'MENU'){push@$my_details,$d;}else{if($cfg->{MENUS_VALUE_BY_COMPONENT}){my$orig_price=defined$d->{original_price}?$d->{original_price}:$d->{price};
if($d->{is_menu}){$d->{menu_total_price}+=$orig_price*$d->{quantity};}elsif($d->{product_type}eq 'MENU'){$last_menu->{menu_total_price}+=$orig_price*$d->{quantity};
$last_menu->{menu_total_price_wo_vat}+=$d->{line_total_wo_vat};}}else{if($d->{is_menu}){$d->{menu_total_price}+=$d->{line_total};}}push@$my_details,$d;}}$total_vat=0;
my$tmp_total=0;
for(keys%vat){$vat{$_}->{vat_v}=&my_round($vat{$_}->{w_vat},$cfg->{DECIMAL_PLACES}||2)-&my_round($vat{$_}->{wo_vat},$cfg->{DECIMAL_PLACES}||2);
$total_vat+=$vat{$_}->{vat_v};
$tmp_total+=$vat{$_}->{w_vat};
if($vat{$_}->{vat_v}==0&&$vat{$_}->{wo_vat}==0){delete$vat{$_};}}$tmp_total=abs($tmp_total);
my$total_diff=my_round($total_w_vat,$cfg->{DECIMAL_PLACES}||2)-my_round($tmp_total,$cfg->{DECIMAL_PLACES}||2);
if($total_diff!=0){foreach my $vat(sort keys%vat){next if$vat*1==0;
$vat{$vat}->{w_vat}+=$total_diff;
$headers->[0]->{kept_in_source}||=0;
if($headers->[0]->{kept_in_source}*1==0){$vat{$vat}->{vat_v}+=$total_diff;}last;}}foreach my $vat(sort keys%vat){if($vat>0){$vat{$vat}->{vat_v}=$vat{$vat}->{w_vat}-$vat{$vat}->{wo_vat};}}$details=$my_details;
my$customer_name=$h->{customer};
my$customer_nif=$h->{customer_nif};
if($pt_certified_software){if(!$h->{customer_nif}||$h->{customer_nif}eq '999999990'){$h->{customer_nif}='*********';}if(!$h->{customer}){if($h->{doc_type}==90&&!$customer_nif){$h->{customer}=&translate($machine,'Consumidor Final');}else{$h->{customer}='*********';}}}if($h->{customer_nif}=~/\-/){my@a=split/\-/,$h->{customer_nif};
if($a[0]=~/^\d+$/){$h->{customer_nif}=~s/\-\d+$//;}}$total_wo_vat||=1;
my$disc=100-(($total_wo_vat2/$total_wo_vat)*100);
$cfg->{MACHINE_LABEL}||="";
my%info=(check=>{machine_label=>$cfg->{MACHINE_LABEL},headers=>$h,extra_data=>\%extra_data,details=>$details,payments=>$footers,products_number=>scalar@$details,products_quantities=>$products_quantities,discount_percent=>$disc,discount_amount=>$total_discounts,total_wo_discount=>$total_wo_discount,total_wo_discount_wo_vat=>$total_wo_discount_wo_vat,total_w_discount_wo_vat=>$total_wo_discount_wo_vat-$total_discount_wo_vat,total_vat=>$total_vat,total_wo_vat=>$total_wo_vat2,total_discounts=>$total_discounts,total_discount_wo_vat=>$total_discount_wo_vat,calculated_percent=>($total_discounts/($total_wo_discount||1))*100,percent_to_keep_in_source=>$cfg->{TAX_TO_KEEP_IN_SOURCE},customer_vat_isention=>$cfg->{CUSTOMER_VAT_ISENTION},invoice_is_payed=>$extra_data{invoice_is_payed}//0,},);
$info{has_logo_a4}=-e"/opt/pos/logo_pdf.png"?1:0;
$info{vat_info}=\%vat;
$info{vat_isentions}=\%vat_isentions;
$info{pt_certified_software}=$pt_certified_software;
$info{print_products_mode}=$cfg->{PRINT_CHECK_PRODUCTS_MODE};
if(defined$AREAS_INFO{$h->{area}}->[10]&&$AREAS_INFO{$h->{area}}->[10]){$info{check}->{area_wo_vat}=1}$h->{manualdoc_docnum}||="";
if($h->{manualdoc_docnum}){$info{check}->{manualdoc_serie}=$h->{manualdoc_serie};
$info{check}->{manualdoc_total_w_vat}=$h->{manualdoc_total_w_vat};
$info{check}->{manualdoc_total_vat}=$h->{manualdoc_total_vat};
$info{check}->{manualdoc_docnum}=$h->{manualdoc_docnum};
$info{check}->{manualdoc_date}=$h->{manualdoc_date};
$info{check}->{manualdoc_type}=$h->{manualdoc_type};}$h->{check_name}||="";
if(!$h->{check_name}){$info{table_desc}=$h->{table_number};}else{if($h->{check_name}=~/^\d+$/){$info{table_desc}=$h->{check_name};}else{$info{table_desc}=$h->{table_number}." (".$h->{check_name}.")";}}$info{table_desc}=~s/<br>/ /g;
if($h->{obs}){$h->{obs}=my_conv($h->{obs});
my@obs=split/§/,$h->{obs};
$info{check}->{obs}=\@obs;}my$doc_origin=[];
my$doc_bills=[];
if($pt_certified_software){$doc_origin=_get_doc_origin($machine,$h,\%ft_gt_assocs);}if(scalar@$doc_origin){$info{check}->{obs}||=[];
push@{$info{check}->{obs}},@$doc_origin;}if($pt_certified_software){$doc_bills=_get_doc_bills($machine,$h);}if(scalar@$doc_bills){$info{check}->{obs}||=[];
push@{$info{check}->{obs}},@$doc_bills;}$h->{delivery_hour}||="";
$h->{load_hour}||="";
$h->{delivery_site}||="";
$h->{load_site}||="";
$h->{kept_in_source}||=0;
$h->{car_plate}||="";
$h->{delivery_date}||="";
$h->{load_date}||="";
if($h->{kept_in_source}>0&&($h->{doc_type}==2||$h->{doc_type}==13||$h->{doc_type}==14||$h->{doc_type}==33)){$h->{kept_in_source}=$h->{kept_in_source}*-1;}$info{menus_value_by_component}=$cfg->{MENUS_VALUE_BY_COMPONENT};
$info{check}->{delivery_date}=$h->{delivery_date};
$info{check}->{load_date}=$h->{load_date};
$info{check}->{delivery_hour}=$h->{delivery_hour};
$info{check}->{load_hour}=$h->{load_hour};
$info{check}->{delivery_site}=$h->{delivery_site};
$info{check}->{load_site}=$h->{load_site};
$info{check}->{kept_in_source}=$h->{kept_in_source};
$info{check}->{car_plate}=$h->{car_plate};
$info{check}->{delivery_site}=~s/\+/\n/g if$info{check}->{delivery_site};
$info{check}->{load_site}=~s/\+/\n/g if$info{check}->{load_site};
$info{check}->{car_plate}=~s/\+/\n/g if$info{check}->{car_plate};
my$content="";
return unless$REAL_PRINT;
$info{check}->{docs_payed}=[];
if($info{check}->{headers}->{doc_type}==8||$info{check}->{headers}->{doc_type}==35){my$dests=$db->select("SELECT source_gb_identifier FROM document_operations WHERE dest_gb_identifier = ?",$info{check}->{headers}->{gb_identifier},);
my@a=();
for(@$dests){push@a,$_->{source_gb_identifier};}if(scalar@a){my$q="";
for(@a){$q.="sh.gb_identifier = '$_' OR ";}$q=~s/ OR $//;
$info{check}->{docs_payed}=$db->select(qq{
          SELECT sh.*, dop.value AS doc_value
          FROM sales_headers sh
          LEFT JOIN document_operations dop ON (sh.gb_identifier = dop.source_gb_identifier)
          WHERE dop.dest_gb_identifier = ? AND ($q)
        },$info{check}->{headers}->{gb_identifier});}}if($info{check}->{headers}->{doc_type}==7&&$info{check}->{headers}->{customer_nif}eq '999999990'){$info{check}->{headers}->{customer_nif}='*********'}$info{print_products_mode}=$cfg->{PRINT_CHECK_PRODUCTS_MODE};
$info{check}->{headers}->{doc_type_str}=$machine->config()->doc_type_str_by_id($info{check}->{headers}->{doc_type});
my$print_destination=$machine->config()->print_destination($info{check}->{headers}->{doc_type});
if($pdf_only){if($cfg->{INTERNET_DOCUMENTS_FORMAT}){if($cfg->{INTERNET_DOCUMENTS_FORMAT}eq 'ticket'){$print_destination=1;}else{$print_destination=2;}}}if($to_screen){$print_destination=1;}my$tmpl_file=$machine->config()->document_template($machine->dir(),$info{check}->{headers}->{doc_type},$print_destination);
if($h->{print_to_simplified_invoice}){if(!$to_screen){$print_destination=$machine->config()->print_destination(90);}$tmpl_file=$machine->config()->document_template($machine->dir(),$info{check}->{headers}->{doc_type},$print_destination);}if(exists$extra_data{to_external_printer}&&$extra_data{to_external_printer}){$print_destination=2;
$tmpl_file=$machine->config()->document_template($machine->dir(),$info{check}->{headers}->{doc_type},2);}if(-e"/opt/pos/common/print_templates/$tmpl_file.custom"){$tmpl_file="/opt/pos/common/print_templates/$tmpl_file.custom";}my$invoice_to_customer_account=0;
if($info{check}->{headers}->{doc_type}==7||$info{check}->{headers}->{doc_type}==13){$invoice_to_customer_account=1;
$print_destination=$machine->config()->print_destination(7);
$tmpl_file=$machine->config()->document_template($machine->dir(),$info{check}->{headers}->{doc_type},$print_destination);}if($print_destination==2){$cfg->{EXTERNAL_INVOICE_LINES_PER_PAGE}||=30;
my$lines_per_page=$cfg->{EXTERNAL_INVOICE_LINES_PER_PAGE};
my$page=0;
my$did_lines=0;
my@details_orig=@{$info{check}->{details}};
my%details=();
foreach my $d(@details_orig){my$will_count=1;
if(scalar@{$d->{txts}}){$will_count+=(scalar@{$d->{txts}}+1);}if($did_lines+$will_count<$lines_per_page){$did_lines+=$will_count;
push@{$details{$page}},$d;}else{$page++;
$did_lines=0;
push@{$details{$page}},$d;}}my$diff=($lines_per_page-$did_lines)-3;
while($diff-- >0){push@{$details{$page}},{product=>''};}my$vias;
if($invoice_to_customer_account){$vias=exists$RECEIPT_PRINT_VIAS{7}?$RECEIPT_PRINT_VIAS{7}:1;}else{$vias=exists$RECEIPT_PRINT_VIAS{$info{check}->{headers}->{doc_type}}?$RECEIPT_PRINT_VIAS{$info{check}->{headers}->{doc_type}}:1;}if(exists$RECEIPT_PRINT_VIAS_WHERE_TO_APPLY{$info{check}->{headers}->{doc_type}}&&$RECEIPT_PRINT_VIAS_WHERE_TO_APPLY{$info{check}->{headers}->{doc_type}}!=2){$vias=1;}if($is_dup&&!$email_addr){$h->{doc_via}=2;
$vias=1;}if($email_addr){$h->{doc_via}=1;}$cfg->{INVOICE_QRCODES_TURN_OFF}//=0;
if(!$cfg->{INVOICE_QRCODES_TURN_OFF}&&$pt_certified_software&&exists$extra_data{atcud}&&$extra_data{atcud}ne ''){unlink"/tmp/qrcode.png";
require App::POS::PrinterQrCode;
my$qr_code=App::POS::PrinterQrCode->new(machine=>$machine,info=>\%info,);
$qr_code->generate_png("/tmp/qrcode.png");}if($pdf_only){my$vias_tmp=$RECEIPT_PRINT_VIAS{$info{check}->{headers}->{doc_type}}||0;
$vias=$vias_tmp if$vias_tmp;}foreach my $pg(sort keys%details){my$page_num=$pg+1;
$info{check}->{pages}=scalar(keys%details);
$info{check}->{page_number}=$page_num;
$info{check}->{details}=$details{$pg};
for(my$i=0;$i<$vias;$i++){my$via=$i+1;
my$txt=&translate($machine,$DOC_VIAS{$via});
$content="";
$info{check}->{headers}->{via}=$txt;
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($tmpl_file,\%info,\$content,)||die$TEMPLATE->error;
if($email_addr){&external_print($cfg,$content,$info{check}->{headers}->{fo_doc_number}.' / '.&translate($machine,$DOC_VIAS{$via}),0,$station,$pdf_only,$email_addr);}else{&external_print($cfg,$content,$info{check}->{headers}->{fo_doc_number}.' / '.&translate($machine,$DOC_VIAS{$via}),0,$station,$pdf_only);}}}}elsif($print_destination==1){my$vias;
if($invoice_to_customer_account){$vias=exists$RECEIPT_PRINT_VIAS{7}?$RECEIPT_PRINT_VIAS{7}:1;}else{$vias=exists$RECEIPT_PRINT_VIAS{$info{check}->{headers}->{doc_type}}?$RECEIPT_PRINT_VIAS{$info{check}->{headers}->{doc_type}}:1;}if(exists$RECEIPT_PRINT_VIAS_WHERE_TO_APPLY{$info{check}->{headers}->{doc_type}}&&$RECEIPT_PRINT_VIAS_WHERE_TO_APPLY{$info{check}->{headers}->{doc_type}}!=1){$vias=1;}if($is_dup&&!$email_addr){$h->{doc_via}=2;
$vias=1;}if($email_addr){$h->{doc_via}=1;}if($pdf_only){my$vias_tmp=$RECEIPT_PRINT_VIAS{$info{check}->{headers}->{doc_type}}||0;
$vias=$vias_tmp if$vias_tmp;}for(my$i=0;$i<$vias;$i++){my$via=$i+1;
my$txt=&translate($machine,$DOC_VIAS{$via});
$content="";
$info{check}->{headers}->{via}=$txt;
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($tmpl_file,\%info,\$content,)||die$TEMPLATE->error;
if($pt_certified_software){if($print_invoice_md5_valid){$print_invoice_md5_cur=_file_md5("/opt/pos/common/print_templates/print_invoice.tmpl")unless defined$print_invoice_md5_cur;
if($print_invoice_md5_cur ne$print_invoice_md5_valid){$content="\n\n"."---------------------------------------\n"."DOCUMENTO DE IMPRESSAO NAO E VALIDO\n"."---------------------------------------\n"."\n\n\@paper_cut\@";}}}if((!$is_dup||$email_addr)&&$via>1){my$txt=&translate($machine,$DOC_VIAS{$via});
$content=~s/Original/$txt/gmi;}if($pt_certified_software){if(-e"/opt/pos/.nif"){open(F,"< /opt/pos/.nif");
my$nif=<F>;
chop($nif);
close(F);
if($nif){my@lines=split/\n/,$content;
$lines[2].="\nContribuinte n. $nif";
$content=join("\n",@lines);}}}if($pdf_only){my$doc_name=$h->{fo_doc_number};
$doc_name=~s/\s/_/g;
$doc_name=~s/\//_/g;
my$pdf_file="/opt/pos/common/data/doc_".$doc_name.".pdf";
my$rrr=int(rand(10000));
my$fff="/tmp/print_$rrr.html";
my$content_tmp=$content;
$content_tmp=encode_entities(decode("utf8",$content_tmp));
$content_tmp=~s/\@size_double\@//g;
$content_tmp=~s/\@size_normal\@//g;
$content_tmp=~s/\@size_small\@//g;
$content_tmp=~s/\@paper_cut\@//g;
$content_tmp=~s/\@justify_.*?\@//g;
$content_tmp=~s/\@logo.*?\@//g;
my$server_cfg=$machine->is_server()?$cfg:$machine->server_config->load();
_replace_saft_tags($server_cfg,\$content_tmp);
$cfg->{INVOICE_QRCODES_TURN_OFF}//=0;
if(!$cfg->{INVOICE_QRCODES_TURN_OFF}&&$pt_certified_software&&exists$info{check}->{extra_data}->{atcud}&&$info{check}->{extra_data}->{atcud}ne ''){unlink"/tmp/qrcode.png";
require App::POS::PrinterQrCode;
my$qr_code=App::POS::PrinterQrCode->new(machine=>$machine,info=>\%info,);
$qr_code->generate_png("/tmp/qrcode.png");
$content_tmp=~s/\@qrcode\@/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src=\"\/tmp\/qrcode.png\" width=100>/g;}open(F,"> $fff");
print F"<html><body><pre>$content_tmp</pre></body></html>";
close(F);
if($email_addr){_pdf_gen($cfg,$fff,$pdf_file,$email_addr,$info{check}->{headers}->{fo_doc_number}." / ".&translate($machine,$DOC_VIAS{$via}));}else{_pdf_gen($cfg,$fff,$pdf_file);
return;}}if($to_screen){$content=~s/\@size_.*?\@//g;
$content=~s/\@justify_.*?\@//g;
$content=~s/\@paper_cut\@//g;
my$server_cfg=$machine->is_server()?$cfg:$machine->server_config->load();
_replace_saft_tags($server_cfg,\$content);
open(F,"> ".$machine->dir_tmp()."/printer_screen#$station.txt");
print F$content;
close(F);}else{$cfg->{PRINTER_OVERRIDE_BY_DOCUMENT}||="";
my$print_station=undef;
my$print_printer=undef;
if($cfg->{PRINTER_OVERRIDE_BY_DOCUMENT}&&!defined$print_station&&!defined$print_printer){if(!%DOC_TYPE_PRINTER_OVERRIDES){my@by_doc=split/\-/,$cfg->{PRINTER_OVERRIDE_BY_DOCUMENT};
foreach my $tok(@by_doc){my@toks=split/,/,$tok;
if(scalar@toks==3){$DOC_TYPE_PRINTER_OVERRIDES{$toks[0]}->{station}=$toks[1];
$DOC_TYPE_PRINTER_OVERRIDES{$toks[0]}->{printer}=$toks[2];}else{print STDERR"PRINTER_OVERRIDE_BY_DOCUMENT has an incorrect value... Check docs...\n";}}}if(exists$DOC_TYPE_PRINTER_OVERRIDES{$info{headers}->{doc_type}}){$print_station=$DOC_TYPE_PRINTER_OVERRIDES{$info{headers}->{doc_type}}->{station};
$print_printer=$DOC_TYPE_PRINTER_OVERRIDES{$info{headers}->{doc_type}}->{printer};}}my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){if(defined$print_station){$print_printer--;
$print_file=~s/print#\d+#\d+#/print#$print_station#$print_printer#/;}else{$cfg->{INVOICE_QRCODES_TURN_OFF}//=0;
if(!$cfg->{INVOICE_QRCODES_TURN_OFF}&&$pt_certified_software&&exists$extra_data{atcud}&&$extra_data{atcud}ne ''){require App::POS::PrinterQrCode;
my$qr_code=App::POS::PrinterQrCode->new(machine=>$machine,info=>\%info,);
my$qr_content=$qr_code->generate();
my$memc=memcache($machine->server_ip(),'qr_codes',\%MEMC_CONNECTIONS);
my@a=split/#/,$print_file;
$memc->set($a[4],$qr_content,5);}open(F,"> $print_file");
print F$content;
close(F);}}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}}}}sub check_finalize{my($machine,$file)=@_;
return 2 unless$machine->is_server();
my$clerk_closed=0;
my$t1=new Benchmark;
my$t2;
if(-e$machine->dir_server()."/server/data/tmp/stop_sales_integration.txt"){print STDERR"SALES INTEGRATION IS STOPED! IGNORING FILE...\n";
return 2;}my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
if(!defined$CLOSE_HOUR){my$close_hour=$cfg->{END_BUSINESS_HOUR}||"05:00";
$CLOSE_HOUR=$close_hour;
my@tmp=split/:/,$close_hour;
$CLOSE_HOUR.=":00" if scalar@tmp==2;}&get_products_information($db);
$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T1:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
if(!defined$VAT_ISENTIONS){my%t=();
my$tmp=$db->select("SELECT id, vat_isention FROM products WHERE vat_isention <> '' AND vat_isention <> '0'");
for(@$tmp){next if$_->{vat_isention}=~/^\s+$/;
$Text::Wrap::columns=38;
my$l=wrap('','',$_->{vat_isention});
$t{$l}=1;
$VAT_ISENTIONS->{$_->{id}}=$l;}my$idx=1;
for(sort keys%t){$VAT_ISENTIONS_IDX->{$_}=$idx++;}$TAXFREE_VAT_ISENTION=wrap('','',$TAXFREE_VAT_ISENTION);
$VAT_ISENTIONS_IDX->{$TAXFREE_VAT_ISENTION}=$TAXFREE_VAT_ISENTION_IDX;}&get_clerks_information($db);
$cfg->{LANG}||="PT";
$cfg->{MACHINE_LABEL}||="";
$cfg->{CHECK_GROUP_PRODUCTS_TEXT}||="";
$cfg->{TAX_TO_KEEP_IN_SOURCE}||="";
$cfg->{CUSTOMER_VAT_ISENTION}||="";
$cfg->{SAFT_PRINT_TRANSPORT_DOCS}||=0;
$cfg->{SHOW_ZERO_PRICE_IN_INVOICE}||=0;
$cfg->{MENUS_VALUE_BY_COMPONENT}||=0;
$cfg->{MENUS_OPTIMIZED_FOR_GROUPS}||=0;
$cfg->{PRINT_EXTRAS_IN_DOCUMENTS}||=0;
$cfg->{PRINT_HISTORY_SAVE}||=0;
$cfg->{INTERNET_DASHBOARD}||=0;
$cfg->{QUIZ_QUESTIONS}||=[];
$cfg->{QUIZ_TOKEN}||="";
$cfg->{PRINT_GROUP_WEIGHT_QUANTITIES}||=0;
$cfg->{PRINT_CHECK_PRODUCTS_MODE}||=1;
$cfg->{CUSTOMER_VAT_ISENTION}||="";
my$pt_certified_software=-e$machine->dir()."/common/skins/".$cfg->{LANG}."/sign_server"?1:0;
$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T2:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
my$check=App::POS::Check->new(file=>$file);
my$data=$check->data();
my$products_extra_data=$check->products_extra_data();
my$info=undef;
my$retries=5;
my$dir_error="/opt/pos/server/data/integration_errors";
my$error1=undef;
my$error2=undef;
my$station_names=_station_names($machine);
my$abort_all=0;
while($retries-- !=0){$info=undef;
$error1=undef;
$error2=undef;
eval{$info=$machine->integrate_check_in_db($file,$PRODUCTS_INFO,$CLERKS_INFO,$check);
if(defined$info&&$info==-1){$abort_all=1;
return;}};
return if$abort_all;
if($@){print STDERR"!!!!!!!!!! integrate_check_in_db ERROR !!!!!!!! $@\n";
$error1=1;
last if$@=~/DBD::Pg/;}elsif(!defined$info){$error2=1;}last unless defined$error1||defined$error2;
my$fsize=-s$file;
print STDERR"#### INTEGRATION ERROR (file: $file, fsize: $fsize)... retries: $retries\n";
my$fcontent=read_file($file);
print STDERR"#$fcontent#\n";
$check=App::POS::Check->new(file=>$file);
$data=$check->data();
$products_extra_data=$check->products_extra_data();
sleep 0.5;}if(defined$error1&&$error1){print STDERR"ERROR INTEGRATING CHECK IN DB - $@\n";
mkdir$dir_error unless-d$dir_error;
print STDERR"file: $file\n";
$machine->{__pg_conn}=undef;
&copy($file,$dir_error);
print STDERR"copied $file to $dir_error\n";
&remove($file.'*');
return;}elsif(defined$error2&&$error2){print STDERR"ERROR - integrate_check_in_db returned undef...\n";
mkdir$dir_error unless-d$dir_error;
print STDERR"file: $file\n";
&copy($file,$dir_error);
print STDERR"copied $file to $dir_error\n";
&remove($file.'*');
return;}my$ex=$check->extra_data();
$ex->{station_print_checks}//="";
$ex->{from_mobile}||=0;
$ex->{send_to_network_printer}||=0;
my$from_mobile=$ex->{from_mobile};
$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T2.1:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
$from_mobile=1 if$info->{headers}->{station_close}>80;
$info->{headers}->{station_close_name}=exists$STATION_NAMES->{$info->{headers}->{station_close}}?$STATION_NAMES->{$info->{headers}->{station_close}}:$info->{headers}->{station_close};
$info->{headers}->{station_open_name}=exists$STATION_NAMES->{$info->{headers}->{station_open}}?$STATION_NAMES->{$info->{headers}->{station_open}}:$info->{headers}->{station_open};
my$a_info=$AREAS_INFO{$info->{headers}->{area}};
if(scalar@$a_info>=5){if($a_info->[4]eq 'consumption'){my$name_f="/opt/pos/server/data/areas/".$info->{headers}->{area}."/".$info->{headers}->{table_number}.".name";
unlink$name_f if-e$name_f;}}$ex->{lines_source_assoc}||="";
my%ft_gt_assocs=();
my$ft_gt_idx;
my%ft_gt_idx=();
if($info->{headers}->{doc_type}==7&&$ex->{lines_source_assoc}){my@a=split/,/,$ex->{lines_source_assoc};
for(@a){my@b=split/_/,$_;
if(!exists$ft_gt_idx{$b[1]}){$ft_gt_idx++;
$ft_gt_idx{$b[1]}=$ft_gt_idx;}$ft_gt_assocs{$b[0]}={gbi=>$b[1],idx=>$ft_gt_idx{$b[1]}};}}$ex->{print_language}||="";
$ex->{obs}||="";
$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T2.2:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
if($ex->{print_language}){&get_products_translations($db);}if($ex->{obs}){$ex->{obs}=my_conv($ex->{obs});
my@obs=split/§/,$ex->{obs};
$info->{obs}=\@obs;}$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T2.3:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
my$doc_origin=[];
my$doc_bills=[];
if($pt_certified_software){$doc_origin=_get_doc_origin($machine,$info->{headers},\%ft_gt_assocs);}if(scalar@$doc_origin){$info->{obs}||=[];
push@{$info->{obs}},@$doc_origin;}$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T2.4:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
if($pt_certified_software){$doc_bills=_get_doc_bills($machine,$info->{headers});}if(scalar@$doc_bills){$info->{obs}||=[];
push@{$info->{obs}},@$doc_bills;}$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T3:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
my$to_print=$info->{headers}->{to_print};
my$mult=$info->{headers}->{doc_type}==2||$info->{headers}->{doc_type}==13||$info->{headers}->{doc_type}==14||$info->{headers}->{doc_type}==33?-1:1;
return if$info->{headers}->{doc_type}==3;
my$discount_percent=$info->{headers}->{discount};
$clerk_closed=$info->{headers}->{clerk_close};
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}if($pt_certified_software){if($info->{headers}->{doc_type}eq '10'||$info->{headers}->{doc_type}eq '11'){$info->{headers}->{at_code}=&translate($machine,"SEM CODIGO AT");}}$info->{headers}->{clerk_open_name}=exists$CLERKS_INFO->{$info->{headers}->{clerk_open}}?$CLERKS_INFO->{$info->{headers}->{clerk_open}}->{login}:'UNKNOWN';
$info->{headers}->{clerk_close_name}=exists$CLERKS_INFO->{$info->{headers}->{clerk_close}}?$CLERKS_INFO->{$info->{headers}->{clerk_close}}->{login}:'UNKNOWN';
my%vat=();
my$cur_date=&strftime("%Y-%m-%d",localtime);
my$cur_hour=&strftime("%H:%M:%S",localtime);
$info->{headers}->{cur_date}=$cur_date;
$info->{headers}->{cur_hour}=$cur_hour;
$info->{headers}->{doc_via}=1;
&set_owner_information($machine,$info->{headers});
$info->{headers}->{value}=$info->{headers}->{value}*$mult;
$info->{headers}->{meals_txt}=&translate($machine,$cfg->{CHECK_GROUP_PRODUCTS_TEXT});
($info->{headers}->{hour_opened})=$info->{headers}->{date}=~/^.*?\s(\d\d\:\d\d)/;
($info->{headers}->{hour_closed})=$info->{headers}->{date_closed}=~/^.*?\s(\d\d\:\d\d)/;
$info->{headers}->{area_name}=exists$AREAS_INFO{$info->{headers}->{area}}?$AREAS_INFO{$info->{headers}->{area}}->[0]:'<UNKNOWN>';
$info->{headers}->{area_tables}=exists$AREAS_INFO{$info->{headers}->{area}}?$AREAS_INFO{$info->{headers}->{area}}->[1]:0;
if($cfg->{PRINT_BARCODE_IN_CHECK}){my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$file;
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,"77777".sprintf("%07s",$info->{headers}->{id}),5);}my$medias=[];
foreach my $d(sort{$a->{payment_media}cmp$b->{payment_media}}@{$info->{payments}}){$d->{payment_media_orig}=$d->{payment_media};
$d->{payment_media}=&my_conv($d->{payment_media},20);
$d->{payment_media}=~s/\s+$//;
$d->{value}=$d->{value}*$mult;
if($d->{payment_media}eq&translate($machine,$cfg->{CHECK_MONEY_CHANGE_TEXT})){$d->{is_change}=1;}else{$d->{is_change}=0;}push@$medias,$d;}$info->{payments}=$medias;
my$details=[];
my$total_discounts=0;
my$total_offers=0;
$info->{headers}->{discount}||='';
if($info->{headers}->{training_mode}&&scalar@$SOFTWARE_INFO){$info->{headers}->{training_software_number}=$SOFTWARE_INFO->[0];
$info->{headers}->{training_software_company}=$SOFTWARE_INFO->[1];
$info->{headers}->{training_software_version}=$SOFTWARE_INFO->[2];
$info->{headers}->{training_software_nif}=$SOFTWARE_INFO->[3];}my$use_print_priorities=0;
$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T4:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
$cfg->{SHOW_ZERO_PRICE_IN_INVOICE}=1 if$info->{headers}->{doc_type}==10;
my$check_has_menus=0;
my%t=();
foreach my $d(@{$info->{details}}){$d->{product_type}||="";
next if$d->{product_name}=~/^\-\-\-\-\-\-\-\-\-\-/;
$check_has_menus=1 if exists$PRODUCTS_MENUS->{$d->{product}};
$d->{discount}||=0;
$d->{offer}||=0;
$d->{printing_priority}=$PRINTING_PRIORITIES->{$d->{product}};
$d->{is_menu}=exists$PRODUCTS_MENUS->{$d->{product}}&&$d->{product_type}ne 'MENU'?1:0;
$use_print_priorities=1 if$PRINTING_PRIORITIES->{$d->{product}};
my$txt=exists$products_extra_data->{$d->{gb_identifier}}?$products_extra_data->{$d->{gb_identifier}}:"";
my$key=$d->{vat}."#".$d->{product}."#".($d->{price}*1)."#".$d->{discount}."#".$txt;
if($PRODUCTS_INFO->{$d->{product}}->{is_weight}&&!$cfg->{PRINT_GROUP_WEIGHT_QUANTITIES}){$key=$d->{idx};}if(!$cfg->{SHOW_ZERO_PRICE_IN_INVOICE}&&!$d->{is_menu}){next unless$d->{price}*1!=0;}if($d->{is_menu}||$d->{product_type}eq 'MENU'){$key=$d->{idx};}if(%ft_gt_assocs){$key.="#".$ft_gt_assocs{$d->{gb_identifier}}->{gbi};}if(exists$t{$key}){$t{$key}->{quantity}+=$d->{quantity};}else{$t{$key}=$d;}}@{$info->{details}}=values%t;
my$splited_check=exists$ex->{splited_check}&&$ex->{splited_check}?1:0;
if($check_has_menus&&!$cfg->{MENUS_OPTIMIZED_FOR_GROUPS}){my$last_key="";
my$last_menu_price=0;
my%to_ignore=();
my%grouping=();
my$count=-1;
my$idx=0;
my%t=();
my%t2=();
my%menu_group=();
my%menu_keys=();
my%menu_qttys=();
my$last_menu_gbi="";
my$last_menu_key="";
foreach my $d(sort{$a->{idx}<=>$b->{idx}}@{$info->{details}}){if($d->{is_menu}){$last_menu_gbi=$d->{gb_identifier};
$menu_group{$last_menu_gbi}=$d->{vat}."#".$d->{product}."#".($d->{price}*1)."#".$d->{discount}.",";
$menu_qttys{$last_menu_gbi}+=$d->{quantity};}elsif($d->{product_type}eq 'MENU'){my$key=$d->{vat}."#".$d->{product}."#".($d->{price}*1)."#".$d->{discount}.",";
$menu_group{$last_menu_gbi}.=$key;}}foreach my $k(keys%menu_group){$menu_keys{$menu_group{$k}}+=$menu_qttys{$k};}my%did_keys=();
my%did_grouped_menus_id=();
my%did_components=();
my%did_details=();
my$qty_increment=0;
my@menu_new_details=();
my$prev_menu;
my$prev_menu_key="";
my$qty_hardcoded=0;
foreach my $d(sort{$a->{idx}<=>$b->{idx}}@{$info->{details}}){my$gbi=$d->{gb_identifier};
if($d->{is_menu}){$prev_menu=$d;
my$key=$menu_group{$gbi};
if(defined$key){$d->{menus_grouping_key}=$key;
if(!exists$did_grouped_menus_id{$key}){$d->{quantity}=exists$menu_keys{$key}?$menu_keys{$key}:1;
$d->{menu_original_price}=$d->{menu_original_price}*$d->{quantity}if exists$d->{menu_original_price};
$did_keys{$key}=$d->{quantity};
push@menu_new_details,$d;}$did_grouped_menus_id{$key}++;}else{push@menu_new_details,$d;}}elsif($d->{product_type}eq 'MENU'){if(!exists$prev_menu->{menus_grouping_key}){push@menu_new_details,$d;}elsif(defined$prev_menu&&!exists$did_grouped_menus_id{$prev_menu->{menus_grouping_key}}){push@menu_new_details,$d;}else{if($did_grouped_menus_id{$prev_menu->{menus_grouping_key}}==1){$d->{quantity}=$prev_menu->{quantity};
push@menu_new_details,$d;}}}else{push@menu_new_details,$d;}}$info->{details}=\@menu_new_details;}$use_print_priorities=0 if$check_has_menus;
if($use_print_priorities){my@t_tmp=reverse sort{$a->{gb_identifier}cmp$b->{gb_identifier}}@{$info->{details}};
my$idx=1;
for(@t_tmp){$_->{printing_priority}||=0;
$_->{printing_priority}=$_->{printing_priority}?$_->{printing_priority}:$idx++;}@{$info->{details}}=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@{$info->{details}};}else{@{$info->{details}}=sort{$a->{idx}<=>$b->{idx}}@{$info->{details}};}my%vat_isentions=();
my$total_wo_discount=0;
my$total_discount_wo_vat=0;
my$total_wo_discount_wo_vat=0;
my$products_quantities=0;
my$total_vat=0;
my$total_wo_vat=0;
my$total_w_vat=0;
my$last_menu="";
$Text::Wrap::columns=38;
foreach my $d(@{$info->{details}}){next if$d->{quantity}*1==0;
next if exists$PRODUCTS_MESSAGES->{$d->{product}}&&$d->{price}==0;
$last_menu=$d if$d->{is_menu};
$d->{quantity}||=1;
if(!$cfg->{PRINT_EXTRAS_IN_DOCUMENTS}){next if exists$PRODUCTS_EXTRAS->{$d->{product}}&&$d->{price}==0;}next if$PRODUCTS_INFO->{$d->{product}}->{name}=~/^\-\-\-\-\-\-\-\-\-\-/;
$d->{quantity}=$d->{quantity}*$mult;
$d->{clerk_name}=exists$CLERKS_INFO->{$d->{clerk}}?$CLERKS_INFO->{$d->{clerk}}->{login}:'UNKNOWN';
if($ex->{print_language}&&exists$PRODUCTS_TRANSLATIONS->{$d->{product}}->{$ex->{print_language}}){$d->{product_name}=$PRODUCTS_TRANSLATIONS->{$d->{product}}->{$ex->{print_language}};}if(exists$ex->{customer_vat_isention}){my$vat_idx=10;
$d->{product_name}="(".$vat_idx.") ".$d->{product_name};
$vat_isentions{$vat_idx}=wrap('','',$ex->{customer_vat_isention});}elsif(exists$VAT_ISENTIONS->{$d->{product}}&&$VAT_ISENTIONS->{$d->{product}}ne '0'&&$d->{vat}*1==0){$d->{product_name}="(".$VAT_ISENTIONS_IDX->{$VAT_ISENTIONS->{$d->{product}}}.") ".$d->{product_name};
$vat_isentions{$VAT_ISENTIONS_IDX->{$VAT_ISENTIONS->{$d->{product}}}}=$VAT_ISENTIONS->{$d->{product}};}if(exists$ex->{taxfree_total}){$d->{product_name}="(".$VAT_ISENTIONS_IDX->{$TAXFREE_VAT_ISENTION}.") ".$d->{product_name};
$vat_isentions{$TAXFREE_VAT_ISENTION_IDX}=$TAXFREE_VAT_ISENTION;}if(%ft_gt_assocs&&exists$ft_gt_assocs{$d->{gb_identifier}}){$d->{product_name}="[".$ft_gt_assocs{$d->{gb_identifier}}->{idx}."] ".$d->{product_name};}$d->{is_weight}=$PRODUCTS_INFO->{$d->{product}}->{is_weight};
$d->{is_service}=$PRODUCTS_INFO->{$d->{product}}->{is_service};
$d->{product_name}=&my_conv($d->{product_name},26);
$d->{handy_plu}=$PRODUCTS_INFO->{$d->{product}}->{handy_plu};
$d->{txts}=[];
if(defined$d->{extra_data}&&$d->{extra_data}){my@t=split/;/,$d->{extra_data};
for my $tok(@t){next unless$tok;
$Text::Wrap::columns=38;
my$l=wrap('','',$tok);
my@lines=split/\n/,$l;
for(@lines){push@{$d->{txts}},$_;}}}$d->{vat}||=0;
my$sem_iva=my_round($d->{quantity}*$d->{price_wo_vat},$cfg->{DECIMAL_PLACES}||2);
my$com_iva=my_round($d->{quantity}*$d->{price},$cfg->{DECIMAL_PLACES}||2);
my$desconto_com_iva=0;
my$desconto_sem_iva=0;
if($d->{price}!=0){$d->{price_wo_vat}||=1;
$desconto_com_iva=$d->{quantity}*$d->{price}*($d->{discount}/$d->{price_wo_vat});
$desconto_sem_iva=$d->{quantity}*$d->{discount};}$total_discount_wo_vat+=$desconto_sem_iva;
$com_iva=$com_iva-$desconto_com_iva;
$sem_iva=$sem_iva-$desconto_sem_iva;
$total_wo_vat+=$sem_iva;
my$valor_iva=$com_iva-$sem_iva;
$info->{headers}->{value_net}+=$sem_iva;
$d->{line_total_wo_vat}=$d->{price_wo_vat}*$d->{quantity};
$d->{unit_price_wo_vat}=$d->{price_wo_vat};
$d->{line_total}=$d->{price}*$d->{quantity};
$d->{unit_price}=$d->{price};
$d->{discount_wo_vat}=$desconto_sem_iva;
$d->{quantity}=$d->{quantity};
$d->{line_total_w_discount}=$d->{line_total}-my_round($desconto_com_iva,$cfg->{DECIMAL_PLACES}||2);
$d->{line_total_w_discount_not_rounded}=$d->{line_total}-$desconto_com_iva;
$d->{line_total_wo_vat_w_discount}=$d->{line_total_wo_vat}-my_round($desconto_sem_iva,$cfg->{DECIMAL_PLACES}||2);
$d->{line_total_wo_vat_w_discount_not_rounded}=$d->{line_total_wo_vat}-$desconto_sem_iva;
$total_w_vat+=$d->{line_total_w_discount_not_rounded};
if(exists$d->{menu_total_price}){$d->{menu_total_price}=$d->{menu_total_price}*$d->{quantity};}if($d->{vat}*1!=0){$vat{$d->{vat}}->{w_vat}+=$d->{line_total_w_discount}*$mult;
$vat{$d->{vat}}->{wo_vat}+=$desconto_com_iva>0?my_round($sem_iva*$mult,$cfg->{DECIMAL_PLACES}||2):$sem_iva*$mult;}else{$vat{$d->{vat}}->{wo_vat}+=$sem_iva*$mult;
$vat{$d->{vat}}->{w_vat}+=$sem_iva*$mult;}$total_discounts+=$desconto_com_iva;
$total_wo_discount+=my_round($d->{line_total},$cfg->{DECIMAL_PLACES}||2);
$total_wo_discount_wo_vat+=my_round($d->{line_total_wo_vat},$cfg->{DECIMAL_PLACES}||2);
$products_quantities+=$d->{quantity}unless exists$PRODUCTS_EXTRAS->{$d->{product}};
if(!$d->{is_menu}&&$d->{product_type}ne 'MENU'){$last_menu="";
push@$details,$d;}else{push@$details,$d;
if($cfg->{MENUS_VALUE_BY_COMPONENT}){if($d->{is_menu}&&!$d->{no_components}){$d->{menu_total_price}+=$d->{menu_original_price};}elsif($d->{product_type}eq 'MENU'){$last_menu->{menu_total_price}+=$d->{menu_original_price}*$d->{quantity};
$last_menu->{menu_total_price_wo_vat}+=$d->{line_total_wo_vat};}}else{if($d->{is_menu}&&!$d->{no_components}){$d->{menu_total_price}+=$d->{price}*$d->{quantity};}}}}$total_vat=0;
my$tmp_total=0;
for(keys%vat){$vat{$_}->{vat_v}=&my_round($vat{$_}->{w_vat},$cfg->{DECIMAL_PLACES}||2)-&my_round($vat{$_}->{wo_vat},$cfg->{DECIMAL_PLACES}||2);
$total_vat+=$vat{$_}->{vat_v};
$tmp_total+=$vat{$_}->{w_vat};
if($vat{$_}->{vat_v}==0&&$vat{$_}->{wo_vat}==0){delete$vat{$_};}}$tmp_total=abs($tmp_total);
my$total_diff=my_round($total_w_vat*$mult,$cfg->{DECIMAL_PLACES}||2)-my_round($tmp_total,$cfg->{DECIMAL_PLACES}||2);
if($total_diff!=0){foreach my $vat(sort keys%vat){next if$vat*1==0;
$vat{$vat}->{w_vat}+=$total_diff;
if(exists$ex->{kept_in_source}&&defined$ex->{kept_in_source}&&$ex->{kept_in_source}*1==0){$vat{$vat}->{vat_v}+=$total_diff;}last;}}foreach my $vat(sort keys%vat){if($vat>0){$vat{$vat}->{vat_v}=$vat{$vat}->{w_vat}-$vat{$vat}->{wo_vat};}}$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T5:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
my$discount_was_percentage=0;
if($discount_percent=~/%/){$discount_was_percentage=1;
$discount_percent=~s/%//g;}$discount_was_percentage=0 if exists$ex->{has_line_discounts}&&$ex->{has_line_discounts};
$info->{details}=$details;
$info->{products_number}=scalar@$details;
$info->{products_quantities}=$products_quantities;
$info->{total_discounts}=$total_discounts;
$info->{discount_was_percentage}=$discount_was_percentage;
$info->{discount_percent}=$discount_percent;
$info->{total_wo_discount}=$total_wo_discount;
$info->{total_wo_discount_wo_vat}=$total_wo_discount_wo_vat;
$info->{total_w_discount_wo_vat}=$total_wo_discount_wo_vat-$total_discount_wo_vat;
$info->{total_w_discount_w_vat}=$total_wo_discount-$total_discounts;
$info->{total_vat}=$total_vat;
$info->{total_discount_wo_vat}=$total_discount_wo_vat;
$info->{total_wo_vat}=$total_wo_vat;
$info->{percent_to_keep_in_source}=$cfg->{TAX_TO_KEEP_IN_SOURCE};
$info->{customer_vat_isention}=$cfg->{CUSTOMER_VAT_ISENTION};
$info->{in_happy_hour}=-e$machine->dir_server().'/server/data/tmp/in_happy_hour.txt'?1:0;
my$area_vat=exists$ex->{original_area}&&$ex->{original_area}?$ex->{original_area}:$check->area();
if(defined$AREAS_INFO{$area_vat}->[10]&&$AREAS_INFO{$area_vat}->[10]){$info->{area_wo_vat}=1}if(exists$ex->{kept_in_source}&&$ex->{kept_in_source}){$info->{kept_in_source}=$ex->{kept_in_source}*1;}else{$info->{kept_in_source}=0;}my$div=$info->{total_wo_discount};
$div||=1;
$info->{calculated_percent}=($info->{total_discounts}/$div)*100;
$info->{table_number}||=0;
if($info->{table_number}<0){$info->{table_number}=$info->{table_number}*-1;}if(exists$ex->{original_table_number}&&$ex->{original_table_number}){$info->{headers}->{table_number}=$ex->{original_table_number};}my%info=(check=>$info);
my$content="";
my$check_unique_id=$info->{headers}->{__unique_id}||undef;
$info{request_number}="";
$info{has_logo_a4}=-e"/opt/pos/logo_pdf.png"?1:0;
$info{check}->{request_number}="";
$info{check}->{machine_label}=$cfg->{MACHINE_LABEL};
$info{print_products_mode}=$cfg->{PRINT_CHECK_PRODUCTS_MODE};
$ex->{has_pzones}||=0;
if($ex->{has_pzones}&&defined$check_unique_id&&$check_unique_id){my$request_numbers_checks=App::POS::Counters->new(db=>$db,name=>'request_numbers_checks',);
$info{check}->{request_number}=$request_numbers_checks->cur_value($check_unique_id);
$info{request_number}=$info{check}->{request_number};}$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T6:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
$ex->{check_name}||="";
if(!$ex->{check_name}){$info{table_desc}=$info->{headers}->{table_number};}else{if($ex->{check_name}=~/^\d+$/){$info{table_desc}=$ex->{check_name};}else{$info{table_desc}=$info->{headers}->{table_number}." (".$ex->{check_name}.")";}}$info{table_desc}=~s/<br>/ /g;
$info{check_name}=$ex->{check_name}||"";
$info->{headers}->{check_name}=$info{check_name};
$info{check}->{headers}->{check_name}=$info{check_name};
$info{vat_info}=\%vat;
$info{vat_isentions}=\%vat_isentions;
$info{check_number}=$info->{headers}->{table_number};
$info{menus_value_by_component}=$cfg->{MENUS_VALUE_BY_COMPONENT};
$ex->{delivery_date}||="";
$ex->{load_date}||="";
$ex->{delivery_hour}||="";
$ex->{load_hour}||="";
$ex->{delivery_site}||="";
$ex->{load_site}||="";
$ex->{car_plate}||="";
$info{check}->{delivery_date}=$ex->{delivery_date};
$info{check}->{load_date}=$ex->{load_date};
$info{check}->{delivery_hour}=$ex->{delivery_hour};
$info{check}->{load_hour}=$ex->{load_hour};
$info{check}->{delivery_site}=$ex->{delivery_site};
$info{check}->{load_site}=$ex->{load_site};
$info{check}->{car_plate}=$ex->{car_plate};
$info{check}->{invoice_is_payed}=$ex->{invoice_is_payed};
$info{check}->{delivery_site}=~s/\+/\n/g if$info{check}->{delivery_site};
$info{check}->{load_site}=~s/\+/\n/g if$info{check}->{load_site};
$info{check}->{car_plate}=~s/\+/\n/g if$info{check}->{car_plate};
$ex->{manualdoc_docnum}||="";
if($ex->{manualdoc_docnum}){$info{check}->{manualdoc_serie}=$ex->{manualdoc_serie};
$info{check}->{manualdoc_total_w_vat}=$ex->{manualdoc_total_w_vat};
$info{check}->{manualdoc_total_vat}=$ex->{manualdoc_total_vat};
$info{check}->{manualdoc_docnum}=$ex->{manualdoc_docnum};
$info{check}->{manualdoc_date}=$ex->{manualdoc_date};
$info{check}->{manualdoc_type}=$ex->{manualdoc_type};}$data->{extra_data}->{delivery_info}||="";
if($data->{extra_data}->{delivery_info}){my$dinfo=&_decode_delivery_info($db,$data);
$info{delivery_info}=$dinfo;}$data->{extra_data}->{queue_name}||="";
if($data->{extra_data}->{queue_name}){$info{queue_number}=_read_queue_number($machine,$data->{extra_data}->{queue_name});}$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T7:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
return unless$REAL_PRINT;
$ex->{docs_payed}||="";
my$docs_payed=[];
if($ex->{docs_payed}){my@a=split/,/,$ex->{docs_payed};
my$q="";
for(@a){$q.="sh.gb_identifier = '$_' OR ";}$q=~s/ OR $//;
my$count=0;
while(!scalar@$docs_payed){last if$count++ ==10;
my$query=qq{
        SELECT sh.*, dop.value AS doc_value
        FROM sales_headers sh
        LEFT JOIN document_operations dop ON (sh.gb_identifier = dop.source_gb_identifier)
        WHERE dop.dest_gb_identifier = ? AND ($q)
      };
$docs_payed=$db->select($query,$info->{headers}->{gb_identifier});
foreach my $doc(@$docs_payed){$db->do("UPDATE sales_headers_aux SET key_value = '1' WHERE key_name = 'invoice_is_payed' AND header = ?",$doc->{id},);}sleep 0.5;}}$info->{docs_payed}=$docs_payed;
$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T8:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
&_add_to_document_history($db,$info,$check,$cfg);
$ex->{to_customer_account}//=0;
if($info->{headers}->{doc_type}==7&&$info->{headers}->{customer_id}&&$ex->{to_customer_account}){my$q=qq{
      SELECT SUM(CASE WHEN sha3.key_value = '1' THEN 0 ELSE sh.doc_value END) AS pending
      FROM document_history sh
      LEFT JOIN sales_headers h ON (sh.header = h.id)
      LEFT JOIN sales_headers_aux sha3 ON (sh.header = sha3.header AND sha3.key_name = 'canceled')
      WHERE sh.doc_type <> 5 AND h.customer_id = ?
    };
my@rr=$db->select($q,$info{check}->{headers}->{customer_id});
if(scalar@rr){$info{check}->{headers}->{customer_pending_amount}=$rr[0]->{pending};}}if($pt_certified_software){my$h=$info->{headers};
if(!$h->{customer}&&!$h->{customer_nif}){if($h->{doc_type}==90){$h->{customer}=&translate($machine,'Consumidor Final');}}}if($info->{headers}->{customer_nif}=~/\-/){my@a=split/\-/,$info->{headers}->{customer_nif};
if($a[0]=~/^\d+$/){$info->{headers}->{customer_nif}=~s/\-\d+$//;}}$info->{headers}->{customer_nif}='*********' if$info->{headers}->{customer_nif}eq '999999990';
if($info->{headers}->{doc_type}==7&&$info->{headers}->{customer_nif}eq '999999990'){$info->{headers}->{customer_nif}='*********';}$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T9:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
$info->{headers}->{doc_type_str}=$machine->config()->doc_type_str_by_id($info->{headers}->{doc_type});
my$print_destination=$machine->config()->print_destination($info->{headers}->{doc_type});
my$tmpl_file=$machine->config()->document_template($machine->dir(),$info->{headers}->{doc_type});
if(exists$ex->{to_external_printer}&&$ex->{to_external_printer}){$print_destination=2;
$tmpl_file=$machine->config()->document_template($machine->dir(),$info->{headers}->{doc_type},2);}$ex->{print_to_simplified_invoice}||=0;
if($ex->{print_to_simplified_invoice}){$print_destination=$machine->config()->print_destination(90);
$tmpl_file=$machine->config()->document_template($machine->dir(),$info->{headers}->{doc_type},$print_destination);}my$invoice_to_customer_account=0;
if($info->{headers}->{doc_type}==7||$info->{headers}->{doc_type}==13){$invoice_to_customer_account=1;
$print_destination=$machine->config()->print_destination(7);
$tmpl_file=$machine->config()->document_template($machine->dir(),$info->{headers}->{doc_type},$print_destination);}$info{check}->{extra_data}=$ex;
if(-e"/opt/pos/common/print_templates/".$tmpl_file.".custom"){$tmpl_file=$tmpl_file.".custom";}$info->{extra_data}=$ex;
$LAST_INTEGRATED_CHECK=$info;
if($pt_certified_software&&$cfg->{SAFT_PRINT_TRANSPORT_DOCS}==0&&($info->{headers}->{doc_type}eq '10'||$info->{headers}->{doc_type}eq '11')){}else{if($print_destination==2){$cfg->{EXTERNAL_INVOICE_LINES_PER_PAGE}||=30;
my$lines_per_page=$cfg->{EXTERNAL_INVOICE_LINES_PER_PAGE};
my$page=0;
my$did_lines=0;
my@details_orig=@{$info->{details}};
my%details=();
foreach my $d(@details_orig){my$will_count=1;
if(scalar@{$d->{txts}}){$will_count+=(scalar@{$d->{txts}}+1);}if($did_lines+$will_count<$lines_per_page){$did_lines+=$will_count;
push@{$details{$page}},$d;}else{$page++;
$did_lines=0;
push@{$details{$page}},$d;}}my$diff=($lines_per_page-$did_lines)-3;
while($diff-- >0){push@{$details{$page}},{product=>''};}foreach my $pg(sort keys%details){my$page_num=$pg+1;
$info->{pages}=scalar(keys%details);
$info->{page_number}=$page_num;
$info->{details}=$details{$pg};
my$vias;
if($invoice_to_customer_account){$vias=exists$RECEIPT_PRINT_VIAS{7}?$RECEIPT_PRINT_VIAS{7}:1;}else{$vias=exists$RECEIPT_PRINT_VIAS{$info->{headers}->{doc_type}}?$RECEIPT_PRINT_VIAS{$info->{headers}->{doc_type}}:1;}if(exists$RECEIPT_PRINT_VIAS_WHERE_TO_APPLY{$info{check}->{headers}->{doc_type}}&&$RECEIPT_PRINT_VIAS_WHERE_TO_APPLY{$info{check}->{headers}->{doc_type}}!=2){$vias=1;}for(my$i=0;$i<$vias;$i++){my$via=$i+1;
my$txt=&translate($machine,$DOC_VIAS{$via});
$content="";
$info{check}->{headers}->{via}=$txt;
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($tmpl_file,\%info,\$content,)||die$TEMPLATE->error;
$cfg->{INVOICE_QRCODES_TURN_OFF}//=0;
if(!$cfg->{INVOICE_QRCODES_TURN_OFF}&&$pt_certified_software&&exists$data->{extra_data}->{atcud}&&$data->{extra_data}->{atcud}ne ''){unlink"/tmp/qrcode.png";
require App::POS::PrinterQrCode;
my$qr_code=App::POS::PrinterQrCode->new(machine=>$machine,info=>\%info,);
$qr_code->generate_png("/tmp/qrcode.png");}&external_print($cfg,$content,$info{check}->{headers}->{fo_doc_number},0,$info{check}->{headers}->{station_close},0);}}$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T10:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;}elsif($print_destination==1){my$print_station=undef;
my$print_printer=undef;
$cfg->{MOBILE_PRINT_INVOICES}||="";
if($cfg->{MOBILE_PRINT_INVOICES}&&$from_mobile){if(!%MOBILE_PRINT_INVOICES){my@by_area=split/\-/,$cfg->{MOBILE_PRINT_INVOICES};
foreach my $tok(@by_area){my@toks=split/,/,$tok;
if(scalar@toks==3){$MOBILE_PRINT_INVOICES{$toks[0]}->{station}=$toks[1];
$MOBILE_PRINT_INVOICES{$toks[0]}->{printer}=$toks[2];}elsif(scalar@toks==2){$MOBILE_PRINT_INVOICES{ALL}->{station}=$toks[0];
$MOBILE_PRINT_INVOICES{ALL}->{printer}=$toks[1];}}}if(exists$MOBILE_PRINT_INVOICES{$info{check}->{headers}->{area_name}}){$print_station=$MOBILE_PRINT_INVOICES{$info{check}->{headers}->{area_name}}->{station};
$print_printer=$MOBILE_PRINT_INVOICES{$info{check}->{headers}->{area_name}}->{printer};}elsif(exists$MOBILE_PRINT_INVOICES{ALL}){$print_station=$MOBILE_PRINT_INVOICES{ALL}->{station};
$print_printer=$MOBILE_PRINT_INVOICES{ALL}->{printer};}}$cfg->{PRINTER_OVERRIDE_BY_DOCUMENT}||="";
if($cfg->{PRINTER_OVERRIDE_BY_DOCUMENT}&&!defined$print_station&&!defined$print_printer){if(!%DOC_TYPE_PRINTER_OVERRIDES){my@by_doc=split/\-/,$cfg->{PRINTER_OVERRIDE_BY_DOCUMENT};
foreach my $tok(@by_doc){my@toks=split/,/,$tok;
if(scalar@toks==3){$DOC_TYPE_PRINTER_OVERRIDES{$toks[0]}->{station}=$toks[1];
$DOC_TYPE_PRINTER_OVERRIDES{$toks[0]}->{printer}=$toks[2];}else{print STDERR"PRINTER_OVERRIDE_BY_DOCUMENT has an incorrect value... Check docs...\n";}}}if(exists$DOC_TYPE_PRINTER_OVERRIDES{$info->{headers}->{doc_type}}){$from_mobile=1;
$print_station=$DOC_TYPE_PRINTER_OVERRIDES{$info->{headers}->{doc_type}}->{station};
$print_printer=$DOC_TYPE_PRINTER_OVERRIDES{$info->{headers}->{doc_type}}->{printer};}}$info{check}->{headers}->{points_assigned}||=0;
$info{check}->{extra_data}->{split_simulation_persons}||=0;
if($to_print){$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($tmpl_file,\%info,\$content,)||die$TEMPLATE->error;
if($pt_certified_software){if($print_invoice_md5_valid){$print_invoice_md5_cur=_file_md5("/opt/pos/common/print_templates/print_invoice.tmpl")unless defined$print_invoice_md5_cur;
if($print_invoice_md5_cur ne$print_invoice_md5_valid){$content="\n\n"."---------------------------------------\n"."DOCUMENTO DE IMPRESSAO NAO E VALIDO\n"."---------------------------------------\n"."\n\n\@paper_cut\@";}}}if($pt_certified_software){if(-e"/opt/pos/.nif"){open(F,"< /opt/pos/.nif");
my$nif=<F>;
chop($nif);
close(F);
if($nif){my@lines=split/\n/,$content;
$lines[2].="\nContribuinte n. $nif";
$content=join("\n",@lines);}}}$t2=new Benchmark;
print STDERR"Handlers> CHECK FINALIZE T10:".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
my$station_print=$info->{headers}->{station_close};
if(exists$ex->{vnc_hardware_ignore}&&$ex->{vnc_hardware_ignore}){$station_print=0;}my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station_print,station_close=>$station_print,station_print_checks=>$ex->{station_print_checks},);
if(defined$print_file){if($from_mobile&&defined$print_station){$print_printer--;
$print_file=~s/print#\d+#\d+#/print#$print_station#$print_printer#/;}$cfg->{INVOICE_QRCODES_TURN_OFF}//=0;
if(!$cfg->{INVOICE_QRCODES_TURN_OFF}&&$pt_certified_software&&exists$data->{extra_data}->{atcud}&&$data->{extra_data}->{atcud}ne ''){require App::POS::PrinterQrCode;
my$qr_code=App::POS::PrinterQrCode->new(machine=>$machine,info=>\%info,);
my$qr_content=$qr_code->generate();
my$memc=memcache($machine->server_ip(),'qr_codes',\%MEMC_CONNECTIONS);
my@a=split/#/,$print_file;
$memc->set($a[4],$qr_content,5);}_add_custom_top_and_bottom($machine,$info->{headers}->{area},\$content);
$data->{extra_data}->{print_send_to}||="";
$data->{extra_data}->{save_to_hdd}||="";
if($data->{extra_data}->{print_send_to}){my$dir_server_tmp=$machine->server_dir_tmp();
my@a_tmp=split/#/,$print_file;
write_file("$dir_server_tmp/$a_tmp[4].print_send_to",$data->{extra_data}->{print_send_to});}if($data->{extra_data}->{save_to_hdd}){my$dir_server_tmp=$machine->server_dir_tmp();
my@a_tmp=split/#/,$print_file;
write_file("$dir_server_tmp/$a_tmp[4].save_to_hdd",$data->{extra_data}->{save_to_hdd});}my$vias;
if($invoice_to_customer_account){$vias=exists$RECEIPT_PRINT_VIAS{7}?$RECEIPT_PRINT_VIAS{7}:1;}else{$vias=exists$RECEIPT_PRINT_VIAS{$info->{headers}->{doc_type}}?$RECEIPT_PRINT_VIAS{$info->{headers}->{doc_type}}:1;}if($ex->{send_to_network_printer}ne ''){lazy_file_create("/opt/pos/common/tmp/send_to_network_printer.txt",$ex->{send_to_network_printer});}if(exists$RECEIPT_PRINT_VIAS_WHERE_TO_APPLY{$info{check}->{headers}->{doc_type}}&&$RECEIPT_PRINT_VIAS_WHERE_TO_APPLY{$info{check}->{headers}->{doc_type}}!=1){$vias=1;}if(defined$vias&&$vias){unlink"/tmp/vias.txt";
open(F,">> /tmp/vias.txt");
print F$content;
close(F);
for(my$i=1;$i<$vias;$i++){my$via=$i+1;
my$txt=&translate($machine,$DOC_VIAS{$via});
my$tmp=$content;
$tmp=~s/Original/$txt/gmi;
open(F,">> /tmp/vias.txt");
print F$tmp;
close(F);}&move("/tmp/vias.txt",$print_file);}else{open(F,"> $print_file");
print F$content;
close(F);}if(exists$data->{extra_data}->{print_in_footer}){my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$info->{headers}->{station_close},station_close=>$info->{headers}->{station_close},station_print_checks=>$ex->{station_print_checks},);
my$cnt=$data->{extra_data}->{print_in_footer};
$cnt=~s/»«/\n/g;
open(F,"> $print_file");
print F$cnt.'@paper_cut@';
close(F);}$cfg->{DELIVERY_QRCODE_PRINT}||=0;
if($cfg->{DELIVERY_QRCODE_PRINT}&&exists$DELIVERY_AREAS{$info->{headers}->{area}}){my$link="";
if($cfg->{DELIVERY_QRCODE_PRINT}==1){$link='https://www.google.com/maps/place/';}else{$link='https://waze.com/ul?q=';}$link.=url_encode($info->{headers}->{customer_address}.", ".$info->{headers}->{customer_postal_code}.", ".$info->{headers}->{customer_city}.", Portugal");
my$qr_code=App::POS::PrinterQrCode->new(machine=>$machine,info=>{},);
my$qr_content=$qr_code->generate_link($link);
my$memc=memcache($machine->server_ip(),'qr_codes',\%MEMC_CONNECTIONS);
my@a=split/#/,$print_file;
$memc->set($a[4].".delivery",$qr_content,5);}if($cfg->{PRINT_HISTORY_SAVE}){my$fname=&basename($print_file);
my($st,$prn)=$fname=~/^print#(\d+)#(\d+)#/;
my$archive_dir=$machine->dir_server().'/server/data/print_checks_archive/'.$st."/".$prn;
&make_path($archive_dir)unless-d$archive_dir;
open(F,"> $archive_dir/$fname");
print F$content;
close(F);}}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}$cfg->{VIDEO_RECORDER_PORT}||="";
if($cfg->{VIDEO_RECORDER_PORT}){system("/opt/pos/common/bin/send_to_recorder.pl ".$cfg->{VIDEO_RECORDER_PORT}." ".$check->area()." \"".$AREAS_INFO{$check->area()}->[0]."\" ".$check->table_number()." checkfinalize \"".$info{check}->{headers}->{fo_doc_number}." - ".$info{check}->{headers}->{clerk_close_name}."\"");}}}$ex->{invoice_email}//="";
if($ex->{invoice_email}){my$dir_server_tmp=$machine->server_dir_tmp();
my$dir_server_data=$machine->server_dir_data();
my$file=$dir_server_tmp."/reprint_check#".$info->{headers}->{id}."#".$info->{headers}->{meals}."#".$info->{headers}->{station_close}."#".$info->{headers}->{business_date}."#".strftime("%Y_%m_%d_%H_%M_%S",localtime)."#=".$ex->{invoice_email}."#0#1#1.txt";
write_file($file,1);}}if($cfg->{QUIZ_TOKEN}&&scalar@{$cfg->{QUIZ_QUESTIONS}}&&!exists$info{check}->{extra_data}->{used_voucher}){$cfg->{QUIZ_RGPD}||=0;
my$memc=memcache($machine->server_ip(),'pos_files',\%MEMC_CONNECTIONS);
my$hardlock=$memc->get('hardlock')||"";
$hardlock=`/opt/pos/common/bin/machine_id` unless$hardlock;
my$params={hardlock=>$hardlock,doc_number=>$info{check}->{headers}->{fo_doc_number},value=>$info{check}->{headers}->{value},discount=>$cfg->{QUIZ_DISCOUNT},rgpd_use=>$cfg->{QUIZ_RGPD},token=>$cfg->{QUIZ_TOKEN},};
my$cnt=post('http://apisv1.net-bo.com/quiz/create',$params,2);
$cnt||="";
if(defined$cnt&&$cnt){my($token,$voucher_number)=split/#/,$cnt;
my$date=strftime("%Y-%m-%d %H:%M:%S",localtime);
my$business_date=&date_based_on_closing_hour($date,$CLOSE_HOUR);
my$expires=_decode_expire('6m');
my$amount=$cfg->{QUIZ_DISCOUNT}||"";
if($amount){if($amount=~/%/){$amount=~s/%//g;
$amount=$info{check}->{headers}->{value}*($amount/100);}my$id=$db->next_id("vouchers");
$db->do(qq{
          INSERT INTO vouchers (
            id, business_date, date, date_expiration, was_used_already, value, external_id, synced
          ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        },$id,$business_date,$date,$expires,0,$amount,$voucher_number,0);}my%info=();
my$content;
my$clerk=$info->{headers}->{clerk_close};
my$station=$info->{headers}->{station_close};
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{token}=$token;
$info{discount}=$amount;
$info{discount_percentage}=$amount&&$amount=~/%/?1:0;
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('quiz.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$info->{headers}->{station_close},station_close=>$info->{headers}->{station_close},station_print_checks=>$ex->{station_print_checks},);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}else{print STDERR"Error connecting to the quiz server...\n";}}$t2=new Benchmark;
print STDERR"Handlers> check_finalize: ".timestr(timediff($t2,$t1))."\n" if$DEBUG_TIMINGS;
if(exists$ex->{taxfree_total}){_gen_taxfree_xml($machine,$check,\%info);}}sub check_finalize_fixup{my($machine,$file)=@_;
if($machine->is_server()){my@stat=stat($file);
my$last_mod=time-$stat[8];
if($last_mod>=10&&!-e$file.".total"){if($file=~/\.deleted$/){print STDERR"WILL DELETE - $file\n";
unlink($file);
open(NN,">> /root/fixup.log");
print NN"WILL DELETE - $file\n";
close(NN);}else{my$c=App::POS::Check->new(file=>$file);
my$d=$c->data();
$d->{footers}||=[];
if(!scalar@{$d->{footers}}){undef$c;
unlink($file);
open(NN,">> /root/fixup.log");
print STDERR"check_finalize_fixup ---> file: $file, last_mod: $last_mod (NO FOOTERS)\n";
print NN"check_finalize_fixup ---> file: $file, last_mod: $last_mod (NO FOOTERS)\n";
close(NN);}else{my$check_total=0;
foreach my $t(@{$d->{footers}}){next unless scalar@$t;
$check_total+=$t->[1];}open(NN,">> /root/fixup.log");
print STDERR"check_finalize_fixup ---> file: $file, last_mod: $last_mod, check_total: $check_total\n";
print NN"check_finalize_fixup ---> file: $file, last_mod: $last_mod, check_total: $check_total\n";
close(NN);
if($check_total>0){my$r=int(rand(10000));
system("mkdir /root/fixup")unless-d"/root/fixup";
&copy($file,"/root/fixup/$r");
open(II,"> ".$file.".total");
print II$check_total."\n";
close(II);}}}}}else{return 2;}}sub table_transfer{my($machine,$file)=@_;
my($station,$clerk,$a_orig,$c_orig,$a_dest,$c_dest)=$file=~/\/table_transfer#(\d+)#(\d+)#(\d+)#(\d+)#(\d+)#(\d+)\.txt$/;
my$cfg=$machine->config()->load_fixed();
$cfg->{PRINT_TABLE_TRANSFERS}||="";
my$db=$machine->database();
&get_products_information($db);
&get_clerks_information($db);
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my%products=();
my$pstr="";
{local$/=undef;
if(open(F,"< $file")){$pstr=<F>;
close(F);}if($pstr){$pstr=~s/\n//g;
my@a=split/,/,$pstr;
for(@a){my@b=split/=/;
my@c=split/\@/,$b[1];
$c[0]=$c[0]*1;
$products{$b[0]}->{$c[1]}+=$c[0];}}}my@prods=();
foreach my $id(keys%products){my$product_name=exists$PRODUCTS_INFO->{$id}?$PRODUCTS_INFO->{$id}->{name}:'UNKNOWN';
foreach my $hour(keys%{$products{$id}}){push@prods,{printing_priority=>$PRINTING_PRIORITIES->{$id},product=>$id,quantity=>$products{$id}->{$hour},product_name=>$product_name,hour_registered=>$hour,};}}my@prods_sorted=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@prods;
my$date=&strftime("%Y-%m-%d",localtime);
my$hour=&strftime("%H:%M:%S",localtime);
my$business_date=&date_based_on_closing_hour("$date $hour",$CLOSE_HOUR);
my$id=$db->next_id("transfers");
my%info=(date=>$date,hour=>$hour,datetime=>$date." ".$hour,products=>\@prods_sorted,area_from=>$a_orig,area_to=>$a_dest,area_from_name=>exists$AREAS_INFO{$a_orig}?$AREAS_INFO{$a_orig}->[0]:'<UNKNOWN>',area_to_name=>exists$AREAS_INFO{$a_dest}?$AREAS_INFO{$a_dest}->[0]:'<UNKNOWN>',table_from=>$c_orig,table_to=>$c_dest,clerk=>$clerk,clerk_name=>exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>',);
$db->do(qq{
    INSERT INTO transfers VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  },$id,$business_date,$hour,$station,$clerk,$info{clerk_name},$a_orig,$a_dest,$c_orig,$c_dest);
foreach my $p(@prods_sorted){$db->do(qq{
      INSERT INTO transfer_products VALUES (?, ?, ?, ?, ?)
    },$id,$p->{product},$p->{product_name},$p->{quantity},$p->{hour_registered});}if($cfg->{PRINT_TABLE_TRANSFERS}){my$content="";
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('products_transfer.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
$cfg->{PRINT_TABLE_TRANSFERS_PRINTER}||="";
if($cfg->{PRINT_TABLE_TRANSFERS_PRINTER}){my$str=$cfg->{PRINT_TABLE_TRANSFERS_PRINTER};
my@a=split/,/,$str;
if(scalar@a==2){my($station,$printer)=@a;
$printer-- if$printer>0;
$print_file=~s/print#\d+#\d+#/print#$station#$printer#/;}}if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}sub print_cc_report{my($machine,$file)=@_;
if($machine->is_station()){return 2;}my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}&get_clerks_information($db);
if(!defined$CLOSE_HOUR){my$close_hour=$cfg->{END_BUSINESS_HOUR}||"05:00";
$CLOSE_HOUR=$close_hour;
my@tmp=split/:/,$close_hour;
$CLOSE_HOUR.=":00" if scalar@tmp==2;}my$where_to_print=$cfg->{PRINT_CC_REPORTS};
my($pdf,$station,$clerk,$date_from,$date_to,$email_addr)=$file=~/ccreport#(.*?)#(\d+)#(\d+)#(.*?)#(.*?)#(.*)\./;
my$cur_date=&strftime("%Y-%m-%d",localtime);
my$cur_hour=&strftime("%H:%M:%S",localtime);
my%info=(total_invoiced=>0,business_date=>&date_based_on_closing_hour("$cur_date $cur_hour",$CLOSE_HOUR),date_from=>$date_from,date_to=>$date_to,cur_date=>$cur_date,cur_hour=>$cur_hour,station=>$station,clerk=>$clerk,clerk_name=>exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>',rows=>[],);
my$last_date="";
open(F,"< $file");
while(<F>){chop;
my@toks=split/#/;
next unless scalar@toks;
my$h={};
$h->{cols}=scalar@toks;
for(my$i=0;$i<scalar(@toks);$i++){my$key='col'.$i;
$h->{$key}=$toks[$i];}if($h->{cols}==5&&$h->{col2}eq ''&&$h->{col3}eq ''){$h->{is_customer}=1;}$h->{col1}||="";
$h->{col2}=0 if exists$h->{col2}&&$h->{col2}eq '';
$h->{col3}=0 if exists$h->{col3}&&$h->{col3}eq '';
if(exists$h->{col4}&&$h->{col4}*1!=0){if($h->{col3}==0&&$h->{col1}ne ''){my@rr=$db->select("SELECT doc_type FROM document_history WHERE fo_doc_number = ?",$h->{col1});
if(scalar@rr){if($rr[0]->{doc_type}!=8){$info{total_invoiced}-=$h->{col2};}}}else{$info{total_invoiced}+=$h->{col3};}}if($h->{col0}&&$h->{col0}=~/^\d\d\d\d\-\d\d\-\d\d$/){$h->{date_changed}=$h->{col0}eq$last_date?0:1;
$last_date=$h->{col0};}push@{$info{rows}},$h;}close(F);
my$content="";
if($pdf){$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_cc.external.tmpl',\%info,\$content,)||die$TEMPLATE->error;
if($pdf eq 'pdf'){&external_print($cfg,$content,'report_cc_'.$cur_date.'_'.$cur_hour,1,$station,0);}elsif($pdf eq 'email'){&external_print($cfg,$content,'report_cc_'.$cur_date.'_'.$cur_hour,0,$station,1,$email_addr);}}else{if($where_to_print==2){$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_cc.external.tmpl',\%info,\$content,)||die$TEMPLATE->error;
&external_print($cfg,$content,'report_cc_'.$cur_date.'_'.$cur_hour,0,$station,0);}else{$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_cc.tmpl',\%info,\$content,)||die$TEMPLATE->error;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,station_close=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}}sub print_cc_doc_history{my($machine,$file)=@_;
if($machine->is_station()){return 2;}my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}&get_clerks_information($db);
if(!defined$CLOSE_HOUR){my$close_hour=$cfg->{END_BUSINESS_HOUR}||"05:00";
$CLOSE_HOUR=$close_hour;
my@tmp=split/:/,$close_hour;
$CLOSE_HOUR.=":00" if scalar@tmp==2;}my$where_to_print=$cfg->{PRINT_CC_REPORTS};
my($pdf,$station,$clerk,$nif,$date_from,$date_to,$email_addr)=$file=~/doc_history#(.*?)#(\d+)#(\d+)#(.*?)#(.*?)#(.*?)#(.*)\./;
my$cust=$db->select("SELECT * FROM customers WHERE nif = ? ORDER BY deleted ASC LIMIT 1",$nif);
my$cur_date=&strftime("%Y-%m-%d",localtime);
my$cur_hour=&strftime("%H:%M:%S",localtime);
my%info=(business_date=>&date_based_on_closing_hour("$cur_date $cur_hour",$CLOSE_HOUR),date_from=>$date_from,date_to=>$date_to,cur_date=>$cur_date,cur_hour=>$cur_hour,station=>$station,clerk=>$clerk,clerk_name=>exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>',rows=>[],);
for(@$cust){foreach my $k(keys%$_){$info{"customer_".$k}=$_->{$k};}}my$last_date="";
open(F,"< $file");
while(<F>){chop;
my@toks=split/#/;
next unless scalar@toks;
my$h={};
$h->{cols}=scalar@toks;
for(my$i=0;$i<scalar(@toks);$i++){my$key='col'.$i;
$h->{$key}=$toks[$i];}if($h->{col0}&&$h->{col0}=~/^\d\d\d\d\-\d\d\-\d\d$/){$h->{date_changed}=$h->{col0}eq$last_date?0:1;
$last_date=$h->{col0};}$h->{col1}=' ' if$h->{col1}eq '';
$h->{col2}=0 if$h->{col2}eq '';
$h->{col3}=0 if defined$h->{col3}&&$h->{col3}eq '';
$h->{col4}=0 if defined$h->{col4}&&$h->{col4}eq '';
if($h->{col1}=~/\d\d\d\d\-\d\d\-\d\d/){$h->{sub}=1;
$h->{col1}="  ".$h->{col1};}else{$h->{main}=1;
$h->{col3}='';
$h->{col4}='';}push@{$info{rows}},$h;}close(F);
my$content="";
if($pdf){$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_doc_history.external.tmpl',\%info,\$content,)||die$TEMPLATE->error;
if($pdf eq 'pdf'){&external_print($cfg,$content,'report_doc_history_'.$cur_date.'_'.$cur_hour,1,$station,0);}elsif($pdf eq 'email'){&external_print($cfg,$content,'report_doc_history_'.$cur_date.'_'.$cur_hour,1,$station,1,$email_addr);}}else{if($where_to_print==2){$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_doc_history.external.tmpl',\%info,\$content,)||die$TEMPLATE->error;
&external_print($cfg,$content,'report_doc_history_'.$cur_date.'_'.$cur_hour,0,$station,0);}else{$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_doc_history.tmpl',\%info,\$content,)||die$TEMPLATE->error;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,station_close=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}}sub printer_screen{my($machine,$file)=@_;
my($station)=$file=~/\/printer_screen#(\d+)\.txt$/;
my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
if($station!=$cfg->{STATION_NUMBER}){return 2;}print STDERR"#> printer_screen - $file\n";
&move($file,"/opt/pos/common/tmp/printer_screen.txt");}sub print_report_tables{my($machine,$file)=@_;
if($machine->is_station()){return 2;}my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}&get_clerks_information($db);
my($station,$area_rec,$clerk,$all_clerks,$all_areas,$to_screen,$only_totals,$to_ignore,$show_products)=$file=~/\/report#tables#(\d+)#(\d+)#(\d+)#(\d+)#(\d+)#(\d+)#(\d+)#(.*?)#(\d+)\.txt$/;
unlink($file);
my$content="";
my%info;
$cfg->{MACHINE_LABEL}||="";
$info{machine_label}=$cfg->{MACHINE_LABEL};
$info{station}=$station;
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
my$card_sets=$machine->card_sets_information();
my%cards_opened;
my$grand_total=0;
my%areas=();
my%payed_cards=();
my%tables_from_cards=();
my%custom_tables=();
my@areas=read_dir($machine->dir_server()."/server/data/areas");
for(@areas){next unless-d$machine->dir_server()."/server/data/areas/".$_;
next unless/^\d+$/;
my$area=$_;
my$area_info=$AREAS_INFO{$area};
my$start_idx=0;
my$end_idx=0;
if(defined$area_info&&scalar@$area_info>=7){$area_info->[1]||=0;
$area_info->[6]||=0;
$start_idx=$area_info->[6]||1;
$end_idx=($start_idx+$area_info->[1])-1;}else{$area_info->[1]||=0;
$start_idx=1;
$end_idx=($start_idx+$area_info->[1])-1;}$start_idx=0 if$end_idx==0;
my$payed_cards_dir=$machine->dir_server()."/server/data/payed_cards/$area";
my$area_dir=$machine->dir_server()."/server/data/areas/$area";
my@ainfo=();
eval{@ainfo=@{$AREAS_INFO{$area}}};
if("$area" eq"$to_ignore"){next;}if($all_areas!=1){if($area!=$area_rec){next;}}my$cur_area=$area;
my$cur_area_name=$AREAS_INFO{$cur_area}->[0];
$cfg->{DISCO_PRODUCT_ID_CARD_OFFER}||="";
&find(sub{return unless-f$File::Find::name;
return unless$_=~/\.total$/;
my($number)=$_=~/^(\d+)\./;
return if$number==0;
if($start_idx!=0&&$end_idx!=0){if($number<$start_idx||$number>$end_idx){return;}}my$o=App::POS::Check->new(file=>$machine->dir_server()."/server/data/areas/$cur_area/$number");
my$i=$o->data();
my$total=$i->{header}->[19];
my$ex=$o->extra_data();
$ex->{custom_table_number}||="";
if($ex->{custom_table_number}){$tables_from_cards{$cur_area_name}->{$ex->{custom_table_number}}->{cards}->{$number}->{total}=$total;
$tables_from_cards{$cur_area_name}->{$ex->{custom_table_number}}->{total}+=$total;
$custom_tables{$cur_area_name}->{$number}=$ex->{custom_table_number};}if($all_clerks==0){return unless$i->{header}->[2]==$clerk;}$grand_total+=$total;
if(!$all_areas){return unless$cur_area eq$i->{header}->[6];}$areas{$cur_area_name}->{total}+=$total;
$areas{$cur_area_name}->{total_qttys}++;
my$custom_table="--";
if(exists$custom_tables{$cur_area_name}->{$number}){$custom_table=$custom_tables{$cur_area_name}->{$number};}if(!exists$areas{$cur_area_name}->{clerks}->{$CLERKS_INFO->{$i->{header}->[2]}->{login}}->{tables}){$areas{$cur_area_name}->{clerks}->{$CLERKS_INFO->{$i->{header}->[2]}->{login}}->{tables}=[];}my$has_offer=0;
if($show_products){my$global_discount_tmp=$i->{header}->[10];
my$global_discount_percent=0;
my$global_discount=0;
if($global_discount_tmp){if($global_discount_tmp=~/^(\d+)%$/){$global_discount_percent=1;
$global_discount=$1;}else{$global_discount=$global_discount_tmp;}}my$det=$i->{details}||[];
foreach my $d(@$det){next unless scalar@$d;
my$pci=$d->[4]||0;
my$q=$d->[3]||0;
my$perciva=$d->[6]||0;
my$psi=$pci/(1+($perciva/100));
my$clerk=$d->[7];
my$product_name=exists$PRODUCTS_INFO->{$d->[1]}?$PRODUCTS_INFO->{$d->[1]}->{name}:'UNKNOWN';
my$prod_name=&my_conv($product_name);
my$desc=0;
$has_offer=1 if!$has_offer&&$d->[1]eq$cfg->{DISCO_PRODUCT_ID_CARD_OFFER};
if($global_discount>0){if($global_discount_percent){$desc=$psi*($global_discount/100);}else{my$div=$i->{header}->[19]+$global_discount||1;
my$disc=($global_discount/$div)*100;
$desc=$psi*($disc/100);}}$pci=&my_round((($psi-($desc+$d->[5])))*(1+$perciva/100),$cfg->{DECIMAL_PLACES}||2);
$psi=&my_round((($psi-($desc+$d->[5]))),$cfg->{DECIMAL_PLACES}||2);
$info{products}->{$cur_area_name}->{by_table}->{$number}->{products}->{$prod_name}->{total}+=$pci*$q;
$info{products}->{$cur_area_name}->{by_table}->{$number}->{products}->{$prod_name}->{total_wo_vat}+=$psi*$q;
$info{products}->{$cur_area_name}->{by_table}->{$number}->{products}->{$prod_name}->{total_qttys}+=$q;
$info{products}->{$cur_area_name}->{by_table}->{$number}->{total}+=$pci*$q;
$info{products}->{$cur_area_name}->{by_table}->{$number}->{total_qttys}+=$q;
$info{products}->{$cur_area_name}->{by_product}->{$prod_name}->{total}+=$pci*$q;
$info{products}->{$cur_area_name}->{by_product}->{$prod_name}->{total_wo_vat}+=$psi*$q;
$info{products}->{$cur_area_name}->{by_product}->{$prod_name}->{total_qttys}+=$q;
$info{products}->{$cur_area_name}->{total}+=$pci*$q;
$info{products}->{$cur_area_name}->{total_qttys}+=$q;}}if($only_totals==0){push@{$areas{$cur_area_name}->{clerks}->{$CLERKS_INFO->{$i->{header}->[2]}->{login}}->{tables}},{number=>$has_offer?$number."*":$number,name=>$o->name(),total=>$total,custom_table=>$custom_table,};
$areas{$cur_area_name}->{clerks}->{$CLERKS_INFO->{$i->{header}->[2]}->{login}}->{total}+=$total;}},($area_dir));
&find(sub{return unless-f$File::Find::name;
my$number=$_;
if($start_idx!=0&&$end_idx!=0){if($number<$start_idx||$number>$end_idx){return;}}my$total=read_file($File::Find::name);
$total=~s/\n//g;
$total=~s/\r//g;
push@{$payed_cards{areas}->{$cur_area_name}->{cards}},{number=>$number,total=>$total,};
$payed_cards{areas}->{$cur_area_name}->{total_qttys}++;
$payed_cards{areas}->{$cur_area_name}->{total}+=$total;
$payed_cards{total_qttys}++;
$payed_cards{total}+=$total;},($payed_cards_dir));}foreach my $area(keys%areas){foreach my $clerk(keys%{$areas{$area}->{clerks}}){my@tables=@{$areas{$area}->{clerks}->{$clerk}->{tables}};
@tables=sort{$a->{number}<=>$b->{number}}@tables;
$areas{$area}->{clerks}->{$clerk}->{tables}=\@tables;}}$info{all_areas}=$all_areas;
$info{all_clerks}=$all_clerks;
$info{only_totals}=$only_totals;
$info{total}=$grand_total;
$info{areas}=\%areas;
$info{payed_cards}=\%payed_cards;
$info{cards_opened}=\%cards_opened;
$info{tables_from_cards}=\%tables_from_cards;
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('report_tables.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
if($to_screen){my$dir_tmp=$machine->server_dir_tmp();
my$cnt_tmp=$content;
$cnt_tmp=~s/\@.*?\@//g;
open(F,">> $dir_tmp/printer_screen#$station.txt");
print F$cnt_tmp;
close(F);}else{my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}sub _data_for_report{my($machine,$clerk,$station,$business_date,$date_to,$clerk_filter,$sales_details,$selections,$report_type)=@_;
my%selections_filters=();
foreach my $k(keys%$selections){if(-e"/opt/pos/server/data/tmp/$k.filter"){my$sel=read_file("/opt/pos/server/data/tmp/$k.filter");
if($sel){chop($sel);
$selections_filters{$k}=$sel;
unlink"/opt/pos/server/data/tmp/$k.filter";}}}$sales_details||=0;
my$biz_date=$business_date;
my$where_str="";
my$dstart=$business_date;
my$dend=$date_to;
$biz_date=~s/\-//g;
$dstart=~s/\-//g;
$dend=~s/\-//g;
my$cfg=$machine->config()->load_fixed();
$cfg->{STATION_NAME}||="";
$cfg->{LOYALTY}||=0;
$cfg->{POINTS_USAGE}||=0;
$cfg->{POINTS_VALUE_PER_POINT_ON_SALE}||=1;
if($cfg->{LOYALTY}){$cfg->{POINTS_USAGE}=0;}my$now_date=&date_based_on_closing_hour(strftime("%Y-%m-%d",localtime)." ".strftime("%H:%M:%S",localtime),$CLOSE_HOUR);
my$today_included=date_is_bigger_or_equal($date_to,$now_date)?1:0;
my$db=$machine->database();
my$station_names=_station_names($machine,$cfg);
my$station_name="";
$db->do("SET enable_nestloop TO off");
my%info=();
my$product_lists_info=&_get_report_lists_info($db);
my%waste_reasons=();
$cfg->{WASTE_JUSTIFICATIONS}||="";
$cfg->{CLERK_FOR_SALES_DATA}||=0;
if($cfg->{WASTE_JUSTIFICATIONS}){my@r=split/,/,$cfg->{WASTE_JUSTIFICATIONS};
my$i=1;
map{$waste_reasons{$i}=$_;$i++;}@r;}&get_clerks_information($db);
&get_products_information($db);
my$customers_info=undef;
my$sth=$db->dbh()->prepare(qq{
    SELECT p1.id, p1.parent_product, p1.qtty_factor AS factor, p2.qtty_factor AS factor_parent
    FROM products p1
    LEFT JOIN products p2 ON (p1.parent_product = p2.id)
    WHERE p1.parent_product <> 0
  });
$sth->execute();
my%sales_by_factor_tmp=();
my%son_products=();
while(my$rec=$sth->fetchrow_hashref){$son_products{$rec->{id}}=1;
$sales_by_factor_tmp{$rec->{id}}->{factor}=$rec->{factor};
$sales_by_factor_tmp{$rec->{id}}->{parent}=$rec->{parent_product};
if($rec->{parent_product}!=0){$son_products{$rec->{parent_product}}=1;
$sales_by_factor_tmp{$rec->{parent_product}}->{factor}=$rec->{factor_parent};
$sales_by_factor_tmp{$rec->{parent_product}}->{parent}=0;}}my$recs=[];
my$total_tips=0;
my$total_service_charges=0;
my%done_tips=();
my%done_persons=();
my%done_service_charges=();
my%service_charges_removes=();
my%transfers=();
my%consumptions=();
my%voids=();
my%voids_detailed=();
my%voids_by_reason=();
my%discounts_family=();
my%persons_number=();
my%sales_family=();
my%sales_vat=();
my%sales_vat2=();
my%sales_vat3=();
my%sales_vat_by_date=();
my%sales_by_media=();
my%receipts_by_media=();
my%sales_by_doctype=();
my%sales_by_doctype_fininfo=();
my%sales_by_product=();
my%sales_by_station_product=();
my%sales_by_clerk_product=();
my%sales_by_product_extra_data=();
my%sales_by_product_no_sons=();
my%returns_by_product=();
my%sales_by_station=();
my%sales_by_clerk=();
my%sales_by_clerk_area_product=();
my%bills_by_clerk=();
my%sales_by_serie=();
my%sales_by_customer=();
my%sales_by_customer_product=();
my%sales_by_customer_vat=();
my%sales_by_pricelevel=();
my%sales_by_menus=();
my%sales_by_cashier=();
my%sales_by_area=();
my%sales_by_parent=();
my%sales_by_hour=();
my%sales_by_cost=();
my%stock_by_product=();
my%sales_by_list=();
my%counters_by_doctype=();
my%clocks_by_clerk=();
my%docs_canceled=();
my%drawer_openings=();
my%kept_in_source=();
my%sales_detailed=();
my%sales_by_cardset=();
my%points_by_customer=();
my%manual_docs=();
my%products_to_deliver=();
my%scale_tickets=();
my%training_sales=();
my%money_counts=();
my%refunds=();
my%settlements_by_customer=();
my%sales_for_comissions=();
my%rfid_usage=();
my%sap_by_clerk=();
my$saft_from=$business_date;
my$saft_to=$date_to;
$saft_from=~s/\-//g;
$saft_to=~s/\-//g;
my$clerk_field="sh.clerk_close";
if($cfg->{CLERK_FOR_SALES_DATA}==0){$clerk_field="sd.clerk";}elsif($cfg->{CLERK_FOR_SALES_DATA}==2){$clerk_field="sh.clerk_open";}my$sql_clerk_query1="";
my$sql_clerk_query2="";
my$sql_clerk_query3="";
my$sql_clerk_query4="";
my@sql_params_two=($business_date,$date_to);
my@sql_params_four=($business_date,$date_to,$business_date,$date_to);
if(defined$clerk_filter){$sql_clerk_query1=" AND clerk = ?";
$sql_clerk_query2=" AND clerk_close = ?";
$sql_clerk_query3=" AND $clerk_field = ?";
$sql_clerk_query4=" AND t.clerk = ?";
@sql_params_two=($business_date,$date_to,$clerk);
@sql_params_four=($business_date,$date_to,$clerk,$business_date,$date_to);}my$date_from_clean=$business_date;
$date_from_clean=~s/\-//g;
my$date_to_clean=$date_to;
$date_to_clean=~s/\-//g;
eval{$sth=$db->dbh()->prepare(qq{
      SELECT id, station_number, clerk, clerk_name, business_date,
        to_char(date, 'HH:MI:SS') AS hour, to_char(date, 'YYYY-MM-DD') AS date
      FROM cashdrawer_opens
      WHERE business_date IS NOT NULL AND business_date >= ? AND business_date <= ?$sql_clerk_query1
      ORDER BY id
    });
$sth->execute(@sql_params_two);
while(my$rec=$sth->fetchrow_hashref){push@{$drawer_openings{clerks}->{$rec->{clerk_name}}},$rec;}};
if(scalar keys%DELIVERY_AREAS&&exists$selections->{"50_products_to_deliver.txt"}){$sth=$db->dbh()->prepare(qq{
      SELECT sh.business_date, sd.price, sd.price_wo_vat,
        sh.fo_doc_number,
        sd.quantity, sd.discount, sd.offer, sd.vat, sh.doc_type,
        sh.station_close, sh.clerk_close_name, sh.clerk_open_name,
        sh.clerk_open_name, sd.price_used, sh.cashier, sh.area, sh.tip,
        sh.service_charge, sd.header, p.id AS product_id, p.cost_price,
        extract(dow from sh.business_date) AS dow, sh.hour AS sale_hour,
        sh.persons_number, sd.clerk_name, sha3.key_value AS doc_canceled,
        sh.value AS doc_value, sh.fo_doc_number, sd.inside_menu, p.is_menu,
        sh.id AS doc_id, sd.gb_identifier, sh.discount AS doc_discount,
        sh.table_num, sh.customer_nif, sd.product_name, sd.free_text,
        sd.department, sh.customer_id, sd.station, sh.serie,
        sh.date_closed, sh.customer,
        sha1.key_value AS delivery_date,
        sha2.key_value AS delivery_period
      FROM sales_details sd
      LEFT JOIN sales_headers sh ON (sh.id = sd.header)
      LEFT JOIN sales_headers_aux sha1 ON (sh.id = sha1.header AND sha1.key_name = 'delivery_date')
      LEFT JOIN sales_headers_aux sha2 ON (sh.id = sha2.header AND sha2.key_name = 'delivery_period')
      LEFT JOIN sales_headers_aux sha3 ON (sh.id = sha3.header AND sha3.key_name = 'canceled')
      LEFT JOIN products p ON (sd.product = p.id)
      WHERE sd.operation_type = 1
      AND sha1.key_value >= ? AND sha1.key_value <= ?$sql_clerk_query3
      ORDER BY sh.id, sd.gb_identifier
    });
$sth->execute(@sql_params_two);
my@fields_tmp=@{$sth->{NAME}};
my%rec_tmp=();
$sth->bind_columns(map{\$rec_tmp{$_}}@fields_tmp);
while($sth->fetch){my$rec=\%rec_tmp;
next if$rec->{doc_canceled};
next unless$rec->{doc_type}==1||$rec->{doc_type}==2||$rec->{doc_type}==5||$rec->{doc_type}==7||$rec->{doc_type}==90||$rec->{doc_type}==13||$rec->{doc_type}==99||$rec->{doc_type}==12;
$rec->{price}||=0;
$rec->{discount}||=0;
my$discount_w_vat=$rec->{discount}*(1+$rec->{vat}/100);
my$price=$rec->{price}-$discount_w_vat;
my$line_total=sprintf("%.2f",$rec->{quantity}*$price);
my$line_total_wo_vat=($rec->{quantity}*$price)/(1+$rec->{vat}/100);
my$fam_name=&my_conv($rec->{family_name});
my$prod_name=&my_conv($rec->{product_name});
my$sub_fam_name=&my_conv($rec->{subfamily_name});
my$line_dept=$rec->{department};
my$serie=$rec->{serie};
my$date=$rec->{delivery_date};
my$period=$rec->{delivery_period};
my$customer_str=$rec->{customer};
my$doc_str=$rec->{fo_doc_number};
$doc_str.=" | ".$period if$period;
$products_to_deliver{dates}->{$date}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$products_to_deliver{dates}->{$date}->{products}->{$prod_name}->{total}+=$line_total;
$products_to_deliver{dates}->{$date}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$products_to_deliver{dates}->{$date}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$products_to_deliver{dates}->{$date}->{customers}->{$customer_str}->{docs}->{$doc_str}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$products_to_deliver{dates}->{$date}->{customers}->{$customer_str}->{docs}->{$doc_str}->{products}->{$prod_name}->{total}+=$line_total;
$products_to_deliver{dates}->{$date}->{customers}->{$customer_str}->{docs}->{$doc_str}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$products_to_deliver{dates}->{$date}->{customers}->{$customer_str}->{docs}->{$doc_str}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$products_to_deliver{dates}->{$date}->{customers}->{$customer_str}->{total}+=$line_total;
$products_to_deliver{dates}->{$date}->{customers}->{$customer_str}->{total_wo_vat}+=$line_total_wo_vat;
$products_to_deliver{dates}->{$date}->{customers}->{$customer_str}->{total_qttys}+=$rec->{quantity};
$products_to_deliver{dates}->{$date}->{total}+=$line_total;
$products_to_deliver{dates}->{$date}->{total_wo_vat}+=$line_total_wo_vat;
$products_to_deliver{dates}->{$date}->{total_qttys}+=$rec->{quantity};
$products_to_deliver{total}+=$line_total;
$products_to_deliver{total_qttys}+=$rec->{quantity};}}$sth=$db->dbh()->prepare(qq{
    SELECT sh.business_date, COUNT(1) AS tickets, SUM(sh.num_products) AS num_products,
      SUM(sh.amount) AS amount
    FROM scale_tickets_read sh
    -- LEFT JOIN clerks c ON (sh.clerk = c.id)
    WHERE sh.business_date >= ? AND sh.business_date <= ? $sql_clerk_query1
    GROUP BY sh.business_date
  });
$sth->execute(@sql_params_two);
while(my$rec=$sth->fetchrow_hashref){$scale_tickets{$rec->{business_date}}=$rec;}print STDERR"QUERY2\n";
$sth=$db->dbh()->prepare(qq{
    SELECT sha.business_date, SUM(sha.value) AS value, SUM(sha.value_net) AS value_net, COUNT(1) AS nrecs
    FROM sales_hashes sha
    LEFT JOIN sales_headers_aux sha8 ON (sha.header = sha8.header AND sha8.key_name = 'in_training_mode')
    WHERE sha.business_date >= ? AND sha.business_date <= ?$sql_clerk_query2 AND
      sha.doc_type = 99
      AND sha8.key_value IS NOT NULL
    GROUP BY sha.business_date
  });
$sth->execute(@sql_params_two);
while(my$rec=$sth->fetchrow_hashref){$training_sales{dates}->{$rec->{business_date}}->{total}+=$rec->{value};
$training_sales{dates}->{$rec->{business_date}}->{total_wo_vat}+=$rec->{value_net};
$training_sales{dates}->{$rec->{business_date}}->{total_recs}+=$rec->{nrecs};
$training_sales{total}+=$rec->{value};
$training_sales{total_wo_vat}+=$rec->{value_net};
$training_sales{total_recs}+=$rec->{nrecs};}print STDERR"QUERY2.1\n";
$sth=$db->dbh()->prepare(qq{
    SELECT sh.fo_doc_number, sh.doc_type, sha3.key_value AS doc_canceled
    FROM sales_headers sh
    LEFT JOIN sales_headers_aux sha3 ON (sh.id = sha3.header AND sha3.key_name = 'canceled')
    LEFT JOIN sales_headers_aux sha8 ON (sh.id = sha8.header AND sha8.key_name = 'manualdoc_serie')
    WHERE sh.business_date >= ? AND sh.business_date <= ?$sql_clerk_query2 AND
      sh.doc_type <> 99 AND sh.doc_type <> 3
      AND sha8.key_value IS NULL
    ORDER BY sh.id
  });
$sth->execute(@sql_params_two);
while(my$rec=$sth->fetchrow_hashref){$rec->{doc_canceled}||=0;
next if$rec->{doc_canceled};
my($doc,$num)=$rec->{fo_doc_number}=~/^(.*?)\/(\d+)$/;
if(!exists$counters_by_doctype{$doc}->{min}){$counters_by_doctype{$doc}->{min}=$rec->{fo_doc_number};
$counters_by_doctype{$doc}->{min_num}=$num;}$counters_by_doctype{$doc}->{max}=$rec->{fo_doc_number};
$counters_by_doctype{$doc}->{max_num}=$num;}my$total_docs=0;
foreach my $k(keys%counters_by_doctype){$total_docs+=($counters_by_doctype{$k}->{max_num}-$counters_by_doctype{$k}->{min_num})+1;}if(exists$selections->{"22_timeclock.txt"}){$sth=$db->dbh()->prepare(qq{
      SELECT * FROM time_clock WHERE date >= ? AND date <= ?$sql_clerk_query1 ORDER BY id
    });
$sth->execute(@sql_params_two);
while(my$rec=$sth->fetchrow_hashref){$rec->{operation}=~s/^clerk_//;
push@{$clocks_by_clerk{clerks}->{$rec->{clerk_name}}->{records}},$rec;}if(scalar keys%clocks_by_clerk){my%control=();
foreach my $clerk(sort{uc($a)cmp uc($b)}keys%{$clocks_by_clerk{clerks}}){my$recs=$clocks_by_clerk{clerks}->{$clerk}->{records}||[];
my$in="";
my$out="";
foreach my $r(@$recs){if($r->{operation}eq 'in'){$control{$clerk}->{in}=$r->{created};
$control{$clerk}->{out}='';}elsif($r->{operation}eq 'out'){$control{$clerk}->{out}=$r->{created};
if($control{$clerk}->{in}&&$control{$clerk}->{out}){my($y_in,$m_in,$d_in,$h_in,$mi_in,$s_in)=$control{$clerk}->{in}=~/^(\d\d\d\d)\-(\d\d)\-(\d\d) (\d\d)\:(\d\d)\:(\d\d)/;
my($y_ou,$m_ou,$d_ou,$h_ou,$mi_ou,$s_ou)=$control{$clerk}->{out}=~/^(\d\d\d\d)\-(\d\d)\-(\d\d) (\d\d)\:(\d\d)\:(\d\d)/;
my$dt_in=DateTime->new(year=>$y_in,month=>$m_in,day=>$d_in,hour=>$h_in,minute=>$mi_in,second=>$s_in,);
my$dt_out=DateTime->new(year=>$y_ou,month=>$m_ou,day=>$d_ou,hour=>$h_ou,minute=>$mi_ou,second=>$s_ou,);
my$duration=$dt_out-$dt_in;
my$minutes=$duration->in_units('minutes');
$clocks_by_clerk{clerks}->{$clerk}->{minutes_worked}+=$minutes;}}}}my$error_msg="";
foreach my $clerk(sort{uc($a)cmp uc($b)}keys%control){if($control{$clerk}->{in}&&!$control{$clerk}->{out}){$error_msg.=$clerk." - ".&translate($machine,"NÃO REGISTOU A SAÍDA")."<br>";}}if($error_msg){$machine->send_alert_to_ui($error_msg);}foreach my $clerk(keys%{$clocks_by_clerk{clerks}}){my$mins_worked=$clocks_by_clerk{clerks}->{$clerk}->{minutes_worked}||0;
my$hours_worked=$mins_worked>0?sprintf("%.2f",$mins_worked/60):0;
$hours_worked=~s/\.00$// if$hours_worked;
$clocks_by_clerk{clerks}->{$clerk}->{hours_worked}=$hours_worked;}}}$sth=$db->dbh()->prepare(qq{
    SELECT id, name, stock_current, stock_min FROM products WHERE stock_use = 1 AND deleted = 0
  });
$sth->execute();
while(my$rec=$sth->fetchrow_hashref){my$below_min=$rec->{stock_current}<$rec->{stock_min}?1:0;
my$prod_name=&my_conv($rec->{name});
$stock_by_product{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{id}};
$stock_by_product{products}->{$prod_name}->{min}=$rec->{stock_min};
$stock_by_product{products}->{$prod_name}->{current}=$rec->{stock_current};
$stock_by_product{products}->{$prod_name}->{diff}=$rec->{stock_current}-$rec->{stock_min};}my$card_sets=$machine->card_sets_information();
print STDERR"QUERY3\n";
my$doc_types=$machine->config()->doc_types_by_id();
my$doc_types_by_name=$machine->config()->doc_types_by_name();
my%did_fo_doc_nums=();
if(exists$selections->{"06_sales_by_group_department.txt"}&&exists$selections_filters{"06_sales_by_group_department.txt"}){$where_str.=" AND sf.id IN (".$selections_filters{"06_sales_by_group_department.txt"}.") ";}my$sales_query=qq{
    SELECT sh.business_date, sd.price, sd.price_wo_vat,
      sd.quantity, sd.discount, sd.offer, sd.vat, sh.doc_type,
      sh.station_close, sh.clerk_close_name, sh.clerk_open_name, sh.clerk_open_name,
      sd.price_used, sh.cashier, sh.area, sh.tip, sh.service_charge,
      sd.header, p.id AS product_id, p.cost_price, sh.customer,
      extract(dow from sh.business_date) AS dow, sh.hour AS sale_hour,
      sh.persons_number, sd.clerk_name, sha3.key_value AS doc_canceled,
      sh.value AS doc_value, sh.fo_doc_number, sd.inside_menu, p.is_menu,
      sha4.key_value AS kept_in_source, sha5.key_value AS is_consumption,
      sh.id AS doc_id, sd.gb_identifier, sh.discount AS doc_discount,
      sh.table_num, sh.customer_nif, sd.product_name, sd.free_text,
      sd.department, sha6.key_value AS invoice_is_payed, sh.customer_id,
      sd.station, sha7.key_value AS override_customer_id, sh.serie,
      sh.date_closed, sh.date AS date_opened, sha10.key_value AS to_customer_account,
      sha9.key_value AS service_charge_remove, p.stock_use, p.stock_min,
      p.stock_current, sf.id AS subfamily_id, sde.extra_data,
      sha11.key_value AS clerk_who_refunded,
    CASE
      WHEN f.id IS NOT NULL THEN COALESCE(f.name,'----------')
      ELSE COALESCE(sf.name,'----------')
    END as family_name,
    CASE
      WHEN sf.id IS NOT NULL THEN COALESCE(sf.name,'----------')
      ELSE '----------'
    END as subfamily_name
    FROM sales_details sd
    LEFT JOIN sales_details_extra_data sde ON (sde.detail_gbi = sd.gb_identifier)
    LEFT JOIN sales_headers sh ON (sh.id = sd.header)
    LEFT JOIN sales_headers_aux sha3 ON (sh.id = sha3.header AND sha3.key_name = 'canceled')
    LEFT JOIN sales_headers_aux sha4 ON (sh.id = sha4.header AND sha4.key_name = 'kept_in_source')
    LEFT JOIN sales_headers_aux sha5 ON (sh.id = sha5.header AND sha5.key_name = 'is_consumption')
    LEFT JOIN sales_headers_aux sha6 ON (sh.id = sha6.header AND sha6.key_name = 'invoice_is_payed')
    LEFT JOIN sales_headers_aux sha7 ON (sh.id = sha7.header AND sha7.key_name = 'override_customer_id')
    LEFT JOIN sales_headers_aux sha8 ON (sh.id = sha8.header AND sha8.key_name = 'manualdoc_serie')
    LEFT JOIN sales_headers_aux sha9 ON (sh.id = sha9.header AND sha9.key_name = 'service_charge_remove')
    LEFT JOIN sales_headers_aux sha10 ON (sh.id = sha10.header AND sha10.key_name = 'to_customer_account')
    LEFT JOIN sales_headers_aux sha11 ON (sh.id = sha11.header AND sha11.key_name = 'clerk_who_refunded')
    LEFT JOIN products p ON (sd.product = p.id)
    LEFT JOIN (
      SELECT product_id, MAX(group_id) as group_id
      FROM product_groups
      GROUP BY product_id
    ) pg ON (p.id = pg.product_id)
    LEFT JOIN groups sf ON (pg.group_id = sf.id)
    LEFT JOIN groups f ON (sf.parent = f.id)
    WHERE sd.operation_type = 1 $where_str
    AND sh.business_date >= ? AND sh.business_date <= ?$sql_clerk_query3
    AND sd.business_date >= ? AND sd.business_date <= ?
    AND sha8.key_value IS NULL
    ORDER BY sh.id, sd.gb_identifier
  };
$sth=$db->dbh()->prepare($sales_query);
$sth->execute(@sql_params_four);
my%did_detail=();
my%did_header=();
my@fields_tmp=@{$sth->{NAME}};
my%rec_tmp=();
$sth->bind_columns(map{\$rec_tmp{$_}}@fields_tmp);
my%sales_clerk_trans=();
while($sth->fetch){my$rec=\%rec_tmp;
$rec->{invoice_is_payed}||=0;
$rec->{to_customer_account}||=0;
if($rec->{doc_type}==1||$rec->{doc_type}==5||$rec->{doc_type}==90||$rec->{doc_type}==7){if($rec->{doc_type}!=7){$rec->{invoice_is_payed}=1;}}$did_header{$rec->{doc_id}}++;
$rec->{doc_canceled}||=0;
$rec->{kept_in_source}||=0;
$rec->{is_consumption}||=0;
if($rec->{doc_type}==7){$rec->{invoice_is_payed}||=0;}elsif($rec->{doc_type}==13&&$rec->{to_customer_account}){$rec->{invoice_is_payed}=0;}else{$rec->{invoice_is_payed}=1;}if($rec->{doc_canceled}){if(!exists$docs_canceled{$rec->{header}}){$docs_canceled{total}+=$rec->{doc_value};}$docs_canceled{$rec->{header}}={business_date=>$rec->{business_date},fo_doc_number=>$rec->{fo_doc_number},value=>$rec->{doc_value},};
next;}$rec->{price}||=0;
$rec->{discount}||=0;
my$discount_w_vat=0;
if($rec->{vat}>0){$discount_w_vat=$rec->{discount}*(1+$rec->{vat}/100);}else{$discount_w_vat=$rec->{discount};}my$price=$rec->{price}-$discount_w_vat;
my$line_total=$rec->{quantity}*$price;
my$line_total_wo_vat=$rec->{vat}>0?sprintf("%.2f",($rec->{quantity}*$price)/(1+$rec->{vat}/100)):$line_total;
my$line_total_wo_vat_net=$rec->{quantity}*($rec->{price_wo_vat}-$rec->{discount});
if($rec->{vat}>0){$line_total_wo_vat=sprintf("%.2f",$line_total_wo_vat);}my$fam_name=&my_conv($rec->{family_name});
my$prod_name=&my_conv($rec->{product_name});
my$sub_fam_name=&my_conv($rec->{subfamily_name});
my$line_dept=$rec->{department};
$line_dept=my_conv($line_dept);
my$serie=$rec->{serie};
my($saft_date)=$rec->{date_closed}=~/^(.*?)\s/;
$saft_date=~s/\-//g;
if($line_dept=~/§/){($fam_name,$sub_fam_name)=split/§/,$line_dept;
$fam_name=&my_conv($fam_name);
$sub_fam_name=&my_conv($sub_fam_name);}$rec->{override_customer_id}||=0;
my$customer_name="";
my$customer_nif="";
my$orig_customer_name="";
my$orig_customer_nif="";
if($rec->{override_customer_id}){if(!defined$customers_info){$customers_info=get_customers_information($db);}$orig_customer_name=$rec->{customer}||"";
$orig_customer_nif=$rec->{customer_nif}||'999999990';
$customer_name=$customers_info->{$rec->{override_customer_id}}->{name}||"";
$customer_nif=$customers_info->{$rec->{override_customer_id}}->{nif}||'999999990';}else{$customer_name=$rec->{customer}||"";
$customer_nif=$rec->{customer_nif}||"";
$orig_customer_name=$customer_name;
$orig_customer_nif=$customer_nif;
if(!$customer_nif){$customer_nif='999999990';
$orig_customer_name='999999990';}if(!$customer_name){$customer_name='999999990';}}$customer_name=~s/^\s+$// if$customer_name;
$orig_customer_name=~s/^\s+$// if$orig_customer_name;
if($orig_customer_nif eq '999999990'){$orig_customer_name='';}my$clerk_name=&my_conv($rec->{clerk_close_name});
my$consumption_clerk_name=&my_conv($rec->{clerk_name});
if($cfg->{CLERK_FOR_SALES_DATA}==0){$clerk_name=&my_conv($rec->{clerk_name});}elsif($cfg->{CLERK_FOR_SALES_DATA}==2){$clerk_name=&my_conv($rec->{clerk_open_name});}$sales_for_comissions{total}+=$line_total;
$sales_for_comissions{total_wo_vat}+=$line_total_wo_vat;
$sales_for_comissions{total_qttys}+=$rec->{quantity};
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{total}+=$line_total;
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{total_qttys}+=$rec->{quantity};
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{total}+=$line_total;
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{total_qttys}+=$rec->{quantity};
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{groups}->{$fam_name}->{total}+=$line_total;
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{groups}->{$fam_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{groups}->{$fam_name}->{total_qttys}+=$rec->{quantity};
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{groups}->{$fam_name}->{depts}->{$sub_fam_name}->{total}+=$line_total;
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{groups}->{$fam_name}->{depts}->{$sub_fam_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_for_comissions{dates}->{$rec->{business_date}}->{clerks}->{$clerk_name}->{groups}->{$fam_name}->{depts}->{$sub_fam_name}->{total_qttys}+=$rec->{quantity};
my$doc_type_str=$doc_types->{$rec->{doc_type}};
if($sales_details){if(!exists$did_detail{$rec->{doc_id}}){my$cur_area_name=$AREAS_INFO{$rec->{area}}->[0];
push@{$sales_detailed{docs}->{$rec->{business_date}}->{headers}},{fo_doc_number=>$rec->{fo_doc_number},doc_id=>$rec->{doc_id},doc_value=>$rec->{doc_value},doc_discount=>$rec->{doc_discount},customer_name=>$rec->{customer}||" ",customer_nif=>$rec->{customer_nif},area=>$rec->{area},area_name=>$cur_area_name,table_num=>$rec->{table_num},clerk=>$rec->{clerk_close_name},hour=>$rec->{sale_hour},};
$did_detail{$rec->{doc_id}}=1;}push@{$sales_detailed{products}->{$rec->{doc_id}}},{product=>$PRODUCTS_INFO->{$rec->{product_id}},product_name=>$prod_name,quantity=>$rec->{quantity},total=>$line_total,vat=>$rec->{vat},};
$sales_detailed{total}+=$line_total;
$sales_detailed{total_wo_vat}+=$line_total_wo_vat;}my$doc_type_str_tmp=$rec->{to_customer_account}?$doc_type_str."_CREDIT":$doc_type_str;
$sales_by_doctype{doc_types}->{$doc_type_str_tmp}->{doc_type}=$rec->{doc_type};
$sales_by_doctype{doc_types}->{$doc_type_str_tmp}->{is_payed}=$rec->{invoice_is_payed};
$sales_by_doctype{doc_types}->{$doc_type_str_tmp}->{total}+=$line_total;
$sales_by_doctype{doc_types}->{$doc_type_str_tmp}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_doctype{doc_types}->{$doc_type_str_tmp}->{total_qttys}+=$rec->{quantity};
if(!exists$did_fo_doc_nums{$rec->{fo_doc_number}}){$did_fo_doc_nums{$rec->{fo_doc_number}}=1;
$sales_by_doctype{doc_types}->{$doc_type_str_tmp}->{num_docs}++;}$sales_by_doctype{doc_types}->{$doc_type_str_tmp}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_doctype{doc_types}->{$doc_type_str_tmp}->{products}->{$prod_name}->{total}+=$line_total;
$sales_by_doctype{doc_types}->{$doc_type_str_tmp}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_doctype{doc_types}->{$doc_type_str_tmp}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
my$tmp_is_payed=1;
$tmp_is_payed=0 if$rec->{to_customer_account}&&!$rec->{invoice_is_payed};
$rec->{to_customer_account}||=0;
$sales_by_doctype_fininfo{doc_types}->{$doc_type_str}->{$rec->{to_customer_account}}->{$tmp_is_payed}->{doc_type}=$rec->{doc_type};
$sales_by_doctype_fininfo{doc_types}->{$doc_type_str}->{$rec->{to_customer_account}}->{$tmp_is_payed}->{total}+=$line_total;
$sales_by_doctype_fininfo{doc_types}->{$doc_type_str}->{$rec->{to_customer_account}}->{$tmp_is_payed}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_doctype_fininfo{doc_types}->{$doc_type_str}->{$rec->{to_customer_account}}->{$tmp_is_payed}->{total_qttys}+=$rec->{quantity};
if(defined$rec->{extra_data}&&$rec->{extra_data}){my@toks1=split/;/,$rec->{extra_data};
for(@toks1){my@toks2=split/:/;
next unless scalar@toks2>1;
$toks2[1]=~s/^\s+//;
$sales_by_product_extra_data{products}->{$prod_name}->{$toks2[0]}->{$toks2[1]}++;}}if($rec->{is_consumption}){$consumptions{logins}->{$consumption_clerk_name}->{total}+=$line_total;
$consumptions{logins}->{$consumption_clerk_name}->{total_wo_vat}+=$line_total_wo_vat;
$consumptions{logins}->{$consumption_clerk_name}->{total_qttys}+=$rec->{quantity};
$consumptions{logins}->{$consumption_clerk_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$consumptions{logins}->{$consumption_clerk_name}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$consumptions{logins}->{$consumption_clerk_name}->{products}->{$prod_name}->{total}+=$line_total;
$consumptions{logins}->{$consumption_clerk_name}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$consumptions{total}+=$line_total;
$consumptions{total_wo_vat}+=$line_total_wo_vat;
$consumptions{total_qttys}+=$rec->{quantity};}if($rec->{doc_type}==1||$rec->{doc_type}==5||$rec->{doc_type}==90||$rec->{doc_type}==12||$rec->{doc_type}==7){$sales_vat{received}->{vats}->{$rec->{vat}*1}->{total}+=$line_total;
$sales_vat{received}->{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat{received}->{vats}->{$rec->{vat}*1}->{total_vat}+=$line_total-$line_total_wo_vat;
$sales_vat{received}->{total}+=$line_total;
$sales_vat{received}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat{received}->{total_vat}+=$line_total-$line_total_wo_vat;
$sales_vat2{received}->{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat2{received}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat3{received}->{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat3{received}->{vats}->{$rec->{vat}*1}->{total}+=$line_total;
if($did_header{$rec->{doc_id}}==1&&$rec->{invoice_is_payed}&&$rec->{kept_in_source}*1!=0){$kept_in_source{by_customer}->{$customer_name}+=$rec->{kept_in_source};
$kept_in_source{total}+=$rec->{kept_in_source};}}if($rec->{doc_type}==2||$rec->{doc_type}==13){$sales_vat{payed}->{vats}->{$rec->{vat}*1}->{total}+=$line_total*-1;
$sales_vat{payed}->{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat*-1;
$sales_vat{payed}->{vats}->{$rec->{vat}*1}->{total_vat}+=($line_total-$line_total_wo_vat)*-1;
$sales_vat{payed}->{total}+=$line_total*-1;
$sales_vat{payed}->{total_wo_vat}+=$line_total_wo_vat*-1;
$sales_vat{payed}->{total_vat}+=($line_total-$line_total_wo_vat)*-1;
$sales_vat2{payed}->{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat*-1;
$sales_vat2{payed}->{total_wo_vat}+=$line_total_wo_vat*-1;
$sales_vat3{payed}->{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat*-1;
$sales_vat3{payed}->{vats}->{$rec->{vat}*1}->{total}+=$line_total*-1;
if($did_header{$rec->{doc_id}}==1&&$rec->{invoice_is_payed}&&$rec->{kept_in_source}*1!=0){$kept_in_source{by_customer}->{$customer_name}+=$rec->{kept_in_source}*-1;
$kept_in_source{total}+=$rec->{kept_in_source}*-1;}$rec->{clerk_who_refunded}//="";
$refunds{by_date}->{$rec->{business_date}}->{docs}->{$rec->{fo_doc_number}}->{clerk_who_refunded}=$rec->{clerk_who_refunded}&&exists$CLERKS_INFO->{$rec->{clerk_who_refunded}}?$CLERKS_INFO->{$rec->{clerk_who_refunded}}->{login}:"";
$refunds{by_date}->{$rec->{business_date}}->{docs}->{$rec->{fo_doc_number}}->{clerk}=$rec->{clerk_close_name};
$refunds{by_date}->{$rec->{business_date}}->{docs}->{$rec->{fo_doc_number}}->{customer}=$rec->{customer}||"";
$refunds{by_date}->{$rec->{business_date}}->{docs}->{$rec->{fo_doc_number}}->{customer_nif}=$rec->{customer_nif}||"";
$refunds{by_date}->{$rec->{business_date}}->{docs}->{$rec->{fo_doc_number}}->{total}+=$line_total*-1;
$refunds{by_date}->{$rec->{business_date}}->{docs}->{$rec->{fo_doc_number}}->{total_wo_vat}+=$line_total_wo_vat*-1;
$refunds{by_date}->{$rec->{business_date}}->{docs}->{$rec->{fo_doc_number}}->{total_vat}+=($line_total-$line_total_wo_vat)*-1;
$refunds{by_date}->{$rec->{business_date}}->{docs}->{$rec->{fo_doc_number}}->{vats}->{$rec->{vat}}->{total}+=$line_total*-1;
$refunds{by_date}->{$rec->{business_date}}->{docs}->{$rec->{fo_doc_number}}->{vats}->{$rec->{vat}}->{total_wo_vat}+=$line_total_wo_vat*-1;
$refunds{by_date}->{$rec->{business_date}}->{docs}->{$rec->{fo_doc_number}}->{vats}->{$rec->{vat}}->{total_vat}+=($line_total-$line_total_wo_vat)*-1;
$refunds{by_date}->{$rec->{business_date}}->{families}->{$fam_name}->{total}+=$line_total*-1;
$refunds{by_date}->{$rec->{business_date}}->{families}->{$fam_name}->{total_wo_vat}+=$line_total_wo_vat*-1;
$refunds{by_date}->{$rec->{business_date}}->{families}->{$fam_name}->{total_vat}+=($line_total-$line_total_wo_vat)*-1;
$refunds{by_date}->{$rec->{business_date}}->{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{total}+=$line_total*-1;
$refunds{by_date}->{$rec->{business_date}}->{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{total_wo_vat}+=$line_total_wo_vat*-1;
$refunds{by_date}->{$rec->{business_date}}->{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{total_vat}+=($line_total-$line_total_wo_vat)*-1;
$refunds{by_date}->{$rec->{business_date}}->{total}+=$line_total*-1;
$refunds{by_date}->{$rec->{business_date}}->{total_wo_vat}+=$line_total_wo_vat*-1;
$refunds{by_date}->{$rec->{business_date}}->{total_vat}+=($line_total-$line_total_wo_vat)*-1;
$refunds{by_product}->{products}->{$prod_name}->{total}+=$line_total*-1;
$refunds{by_product}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat*-1;
$refunds{by_product}->{products}->{$prod_name}->{total_vat}+=($line_total-$line_total_wo_vat)*-1;
$refunds{by_product}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity}*-1;
$refunds{by_product}->{total}+=$line_total*-1;
$refunds{by_product}->{total_wo_vat}+=$line_total_wo_vat*-1;
$refunds{by_product}->{total_vat}+=($line_total-$line_total_wo_vat)*-1;
$refunds{by_product}->{total_qttys}+=$rec->{quantity}*-1;}if($rec->{doc_type}==1||$rec->{doc_type}==2||$rec->{doc_type}==5||$rec->{doc_type}==90||$rec->{doc_type}==12||$rec->{doc_type}==13||$rec->{doc_type}==7){$sales_vat{vats}->{$rec->{vat}*1}->{total}+=$line_total;
$sales_vat{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat{vats}->{$rec->{vat}*1}->{total_vat}+=$line_total-$line_total_wo_vat;
$sales_vat{total}+=$line_total;
$sales_vat{total_wo_vat}+=$line_total_wo_vat;
$sales_vat2{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat2{total_wo_vat}+=$line_total_wo_vat;
$sales_vat3{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat3{vats}->{$rec->{vat}*1}->{total}+=$line_total;
$sales_vat3{total_wo_vat}+=$line_total_wo_vat;
$sales_vat3{total}+=$line_total;
$sales_vat_by_date{dates}->{$rec->{business_date}}->{vats}->{$rec->{vat}*1}->{total}+=$line_total;
$sales_vat_by_date{dates}->{$rec->{business_date}}->{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat_by_date{dates}->{$rec->{business_date}}->{vats}->{$rec->{vat}*1}->{total_vat}+=$line_total-$line_total_wo_vat;
$sales_vat_by_date{dates}->{$rec->{business_date}}->{total}+=$line_total;
$sales_vat_by_date{dates}->{$rec->{business_date}}->{total_vat}+=$line_total-$line_total_wo_vat;
$sales_vat_by_date{dates}->{$rec->{business_date}}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat_by_date{vats}->{$rec->{vat}*1}->{total}+=$line_total;
$sales_vat_by_date{vats}->{$rec->{vat}*1}->{total_wo_vat}+=$line_total_wo_vat;
$sales_vat_by_date{vats}->{$rec->{vat}*1}->{total_vat}+=$line_total-$line_total_wo_vat;
$sales_vat_by_date{total}+=$line_total;
$sales_vat_by_date{total_wo_vat}+=$line_total_wo_vat;}if($rec->{doc_type}==1||$rec->{doc_type}==2||$rec->{doc_type}==5||$rec->{doc_type}==90||$rec->{doc_type}==8||($rec->{doc_type}==13&&!$rec->{to_customer_account})||($rec->{doc_type}==7&&$rec->{invoice_is_payed}&&!$rec->{to_customer_account})){$sales_by_cashier{cashiers}->{$rec->{cashier}}->{name}=exists$CASHIER_NAMES{$rec->{cashier}}?$CASHIER_NAMES{$rec->{cashier}}:$rec->{cashier};
$sales_by_cashier{cashiers}->{$rec->{cashier}}->{total}+=$line_total;
$sales_by_cashier{cashiers}->{$rec->{cashier}}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cashier{cashiers}->{$rec->{cashier}}->{total_qttys}+=$rec->{quantity};
$sales_by_cashier{total}+=$line_total;
$sales_by_cashier{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cashier{total_qttys}+=$rec->{quantity};}if($rec->{doc_type}==2||$rec->{doc_type}==13){$returns_by_product{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$returns_by_product{products}->{$prod_name}->{total}+=$line_total;
$returns_by_product{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$returns_by_product{products}->{$prod_name}->{total_qttys}+=$rec->{quantity}*-1;
$returns_by_product{total}+=$line_total;
$returns_by_product{total_qttys}+=$rec->{quantity}*-1;}next unless$rec->{doc_type}==1||$rec->{doc_type}==2||$rec->{doc_type}==5||$rec->{doc_type}==7||$rec->{doc_type}==90||$rec->{doc_type}==13||$rec->{doc_type}==12;
my$doc_type_tmp=$rec->{doc_type};
$doc_type_tmp=90 if$rec->{doc_type}==7&&$rec->{invoice_is_payed};
$sales_by_serie{series}->{$serie}->{total}+=$line_total;
$sales_by_serie{series}->{$serie}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_serie{series}->{$serie}->{total_qttys}+=$rec->{quantity};
$sales_by_serie{total}+=$line_total;
$sales_by_serie{total_wo_vat}+=$line_total_wo_vat;
$sales_by_serie{total_qttys}+=$rec->{quantity};
if($rec->{doc_type}!=4){$sales_by_clerk{clerks}->{$clerk_name}->{doc_types}->{$doc_type_tmp}->{total}+=$line_total;
$sales_by_clerk{clerks}->{$clerk_name}->{doc_types}->{$doc_type_tmp}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk{clerks}->{$clerk_name}->{doc_types}->{$doc_type_tmp}->{total_qttys}+=$rec->{quantity};
$sales_by_clerk{total}+=$line_total;
$sales_by_clerk{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk{total_qttys}+=$rec->{quantity};
if($PRODUCTS_INFO->{$rec->{product_id}}->{upsale}){$sales_by_clerk{clerks}->{$clerk_name}->{upsale_qtty}+=$rec->{quantity};
$sales_by_clerk{clerks}->{$clerk_name}->{upsale_total}+=$line_total;
$sales_by_clerk{clerks}->{$clerk_name}->{upsale_total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk{upsale_qtty}+=$rec->{quantity};
$sales_by_clerk{upsale_total}+=$line_total;
$sales_by_clerk{upsale_total_wo_vat}+=$line_total_wo_vat;}if(!exists$sales_clerk_trans{$clerk_name."#".$rec->{doc_id}}){$sales_clerk_trans{$clerk_name."#".$rec->{doc_id}}=1;
$sales_by_clerk{clerks}->{$clerk_name}->{trans}++;}}if(!exists$done_tips{$rec->{header}}){$total_tips+=$rec->{tip};
$done_tips{$rec->{header}}=1;}if(!exists$done_service_charges{$rec->{header}}){$total_service_charges+=$rec->{service_charge};
$done_service_charges{$rec->{header}}=1;
if(exists$rec->{service_charge_remove}&&$rec->{service_charge_remove}){my($clerk,$value,$datetime,$reason)=split/#/,$rec->{service_charge_remove};
if(defined$clerk&&defined$value&&defined$datetime){my($date,$time)=$datetime=~/^(.*?)\s(.*?)$/;
my$clerk_name=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$service_charges_removes{clerks}->{$clerk_name}->{dates}->{$date}->{hours}->{$time}->{date_opened}=$rec->{date_opened};
$service_charges_removes{clerks}->{$clerk_name}->{dates}->{$date}->{hours}->{$time}->{date_closed}=$rec->{date_closed};
$service_charges_removes{clerks}->{$clerk_name}->{dates}->{$date}->{hours}->{$time}->{total}=$value;
$service_charges_removes{clerks}->{$clerk_name}->{dates}->{$date}->{hours}->{$time}->{reason}=$reason;
$service_charges_removes{clerks}->{$clerk_name}->{dates}->{$date}->{trans}++;
$service_charges_removes{clerks}->{$clerk_name}->{dates}->{$date}->{total}+=$value;
$service_charges_removes{clerks}->{$clerk_name}->{trans}++;
$service_charges_removes{clerks}->{$clerk_name}->{total}+=$value;
$service_charges_removes{total}+=$value;
$service_charges_removes{trans}++;}}}if($rec->{is_menu}){$sales_by_menus{menus}->{products}->{$prod_name}->{total}+=$line_total;
$sales_by_menus{menus}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_menus{menus}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_menus{menus}->{total}+=$line_total;
$sales_by_menus{menus}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_menus{menus}->{total_qttys}+=$rec->{quantity};
$sales_by_menus{by_product}->{products}->{1}->{$prod_name}->{total}+=$line_total;
$sales_by_menus{by_product}->{products}->{1}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_menus{by_product}->{products}->{1}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_menus{by_product}->{total}+=$line_total;
$sales_by_menus{by_product}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_menus{by_product}->{total_qttys}+=$rec->{quantity};}elsif($rec->{inside_menu}>0){my$menu_product=$rec->{inside_menu};
my$menu_name=&my_conv($PRODUCTS_INFO->{$rec->{inside_menu}}->{name});
$sales_by_menus{menus}->{products}->{$menu_name}->{total_wo_vat}+=$line_total;
$sales_by_menus{menus}->{total}+=$line_total;
$sales_by_menus{menus}->{total_wo_vat}+=$line_total;
$sales_by_menus{menus}->{products}->{$menu_name}->{products}->{$prod_name}->{total}+=$line_total;
$sales_by_menus{menus}->{products}->{$menu_name}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_menus{menus}->{products}->{$menu_name}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_menus{by_product}->{products}->{0}->{$prod_name}->{total}+=$line_total;
$sales_by_menus{by_product}->{products}->{0}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_menus{by_product}->{products}->{0}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_menus{by_product}->{total}+=$line_total;
$sales_by_menus{by_product}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_menus{by_product}->{total_qttys}+=$rec->{quantity};}$rec->{cost_price}||=0;
if($rec->{cost_price}>0){my$v1=$rec->{quantity}*$rec->{cost_price};
my$v2=$line_total_wo_vat;
$sales_by_cost{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_cost{products}->{$prod_name}->{value_by_cost}+=$v1;
$sales_by_cost{products}->{$prod_name}->{value_by_sale}+=$v2;
$sales_by_cost{products}->{$prod_name}->{value_diff}+=$v2-$v1;
$sales_by_cost{total_diff}+=$v2-$v1;
$sales_by_cost{total_qttys}+=$rec->{quantity};
$sales_by_cost{total_costs}+=$v1;
$sales_by_cost{total_sales}+=$v2;}if(defined$card_sets){my$card_set_info=$card_sets->{by_table}->{$rec->{table_num}};
if(defined$card_set_info){for(@$card_set_info){$sales_by_cardset{"set"}->{sets}->{$_}->{cards}->{$rec->{table_num}}->{total}+=$line_total;
$sales_by_cardset{"set"}->{sets}->{$_}->{cards}->{$rec->{table_num}}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cardset{"set"}->{sets}->{$_}->{cards}->{$rec->{table_num}}->{total_qttys}+=$rec->{quantity};
$sales_by_cardset{"set"}->{sets}->{$_}->{name}=$card_sets->{sets}->{$_}->[0];
$sales_by_cardset{"set"}->{sets}->{$_}->{total}+=$line_total;
$sales_by_cardset{"set"}->{sets}->{$_}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cardset{"set"}->{sets}->{$_}->{total_qttys}+=$rec->{quantity};
$sales_by_cardset{"set"}->{total}+=$line_total;
$sales_by_cardset{"set"}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cardset{"set"}->{total_qttys}+=$rec->{quantity};}}else{$sales_by_cardset{"without_set"}->{total}+=$line_total;
$sales_by_cardset{"without_set"}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cardset{"without_set"}->{total_qttys}+=$rec->{quantity};
$sales_by_cardset{"without_set"}->{cards}->{$rec->{table_num}}->{total}+=$line_total;
$sales_by_cardset{"without_set"}->{cards}->{$rec->{table_num}}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cardset{"without_set"}->{cards}->{$rec->{table_num}}->{total_qttys}+=$rec->{quantity};}}$sales_by_pricelevel{prices}->{$rec->{price_used}}->{total}+=$line_total;
$sales_by_pricelevel{prices}->{$rec->{price_used}}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_pricelevel{prices}->{$rec->{price_used}}->{total_qttys}+=$rec->{quantity};
$sales_by_pricelevel{prices}->{$rec->{price_used}}->{products}->{$prod_name}->{product}+=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_pricelevel{prices}->{$rec->{price_used}}->{products}->{$prod_name}->{total}+=$line_total;
$sales_by_pricelevel{prices}->{$rec->{price_used}}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_pricelevel{prices}->{$rec->{price_used}}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_pricelevel{total}+=$line_total;
$sales_by_pricelevel{total_wo_vat}+=$line_total_wo_vat;
$sales_by_pricelevel{total_qttys}+=$rec->{quantity};
$orig_customer_nif='999999990' if!$orig_customer_name&&!$orig_customer_nif;
$orig_customer_name=$orig_customer_nif if!$orig_customer_name&&$orig_customer_nif;
$sales_by_customer_vat{customers}->{$orig_customer_name}->{vats}->{$rec->{vat}}->{total}+=$line_total;
$sales_by_customer_vat{customers}->{$orig_customer_name}->{vats}->{$rec->{vat}}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_customer_vat{customers}->{$orig_customer_name}->{vats}->{$rec->{vat}}->{total_qttys}+=$rec->{quantity};
$sales_by_customer_vat{customers}->{$orig_customer_name}->{total}+=$line_total;
$sales_by_customer_vat{customers}->{$orig_customer_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_customer_vat{customers}->{$orig_customer_name}->{total_qttys}+=$rec->{quantity};
$sales_by_customer_vat{customers}->{$orig_customer_name}->{nif}=$orig_customer_nif?$orig_customer_nif:$orig_customer_name;
$sales_by_customer_vat{total}+=$line_total;
$sales_by_customer_vat{total_wo_vat}+=$line_total_wo_vat;
$sales_by_customer_vat{total_qttys}+=$rec->{quantity};
$sales_by_customer_product{customers}->{$customer_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_customer_product{customers}->{$customer_name}->{products}->{$prod_name}->{total}+=$line_total;
$sales_by_customer_product{customers}->{$customer_name}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_customer_product{customers}->{$customer_name}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_customer_product{customers}->{$customer_name}->{total}+=$line_total;
$sales_by_customer_product{customers}->{$customer_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_customer_product{customers}->{$customer_name}->{total_qttys}+=$rec->{quantity};
$sales_by_customer_product{customers}->{$customer_name}->{nif}=$rec->{customer_nif}?$rec->{customer_nif}:$customer_name;
$sales_by_customer_product{total}+=$line_total;
$sales_by_customer_product{total_wo_vat}+=$line_total_wo_vat;
$sales_by_customer_product{total_qttys}+=$rec->{quantity};
$sales_by_customer{customers}->{$customer_name||'999999990'}->{total}+=$line_total;
$sales_by_customer{customers}->{$customer_name||'999999990'}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_customer{customers}->{$customer_name||'999999990'}->{total_qttys}+=$rec->{quantity};
$sales_by_customer{total}+=$line_total;
$sales_by_customer{total_wo_vat}+=$line_total_wo_vat;
$sales_by_customer{total_qttys}+=$rec->{quantity};
$station_name=exists$station_names->{$rec->{station_close}}?$station_names->{$rec->{station_close}}:$rec->{station_close};
$sales_by_station{stations}->{$station_name}->{total}+=$line_total;
$sales_by_station{stations}->{$station_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_station{stations}->{$station_name}->{total_qttys}+=$rec->{quantity};
$sales_by_station{total}+=$line_total;
$sales_by_station{total_wo_vat}+=$line_total_wo_vat;
$sales_by_station{total_qttys}+=$rec->{quantity};
if(exists$sales_by_factor_tmp{$rec->{product_id}}){my$name_key="";
my$factor=1;
$factor=$sales_by_factor_tmp{$rec->{product_id}}->{factor}if$sales_by_factor_tmp{$rec->{product_id}}->{factor};
if($sales_by_factor_tmp{$rec->{product_id}}->{parent}==0){$name_key=$prod_name;}else{my$pid=$sales_by_factor_tmp{$rec->{product_id}}->{parent};
my$product_name=exists$PRODUCTS_INFO->{$pid}?$PRODUCTS_INFO->{$pid}->{name}:'UNKNOWN';
$name_key=&my_conv($product_name);}$sales_by_parent{products}->{$name_key}->{total}+=$line_total;
$sales_by_parent{products}->{$name_key}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_parent{products}->{$name_key}->{total_qttys}+=$rec->{quantity}*$factor;
$sales_by_parent{total}+=$line_total;
$sales_by_parent{total_qttys}+=$rec->{quantity};}my$area_name=exists$AREAS_INFO{$rec->{area}}?$AREAS_INFO{$rec->{area}}->[0]:'INVALID - '.$rec->{area};
$sales_by_area{areas}->{$area_name}->{total}+=$line_total;
$sales_by_area{areas}->{$area_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_area{areas}->{$area_name}->{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_by_area{areas}->{$area_name}->{total_qttys}+=$rec->{quantity};
$sales_by_area{areas}->{$area_name}->{vats}->{$rec->{vat}}->{total}+=$line_total;
$sales_by_area{areas}->{$area_name}->{vats}->{$rec->{vat}}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_area{areas}->{$area_name}->{vats}->{$rec->{vat}}->{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_by_area{areas}->{$area_name}->{vats}->{$rec->{vat}}->{total_qttys}+=$rec->{quantity};
$sales_by_area{total}+=$line_total;
$sales_by_area{total_wo_vat}+=$line_total_wo_vat;
$sales_by_area{total_qttys}+=$rec->{quantity};
$sales_by_area{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{areas}->{$area_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{areas}->{$area_name}->{products}->{$prod_name}->{total}+=$line_total;
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{areas}->{$area_name}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{areas}->{$area_name}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{areas}->{$area_name}->{total}+=$line_total;
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{areas}->{$area_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{areas}->{$area_name}->{total_qttys}+=$rec->{quantity};
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{totals}->{total}+=$line_total;
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{totals}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{totals}->{total_qttys}+=$rec->{quantity};
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{areas}->{$area_name}->{totals}->{total}+=$line_total;
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{areas}->{$area_name}->{totals}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk_area_product{clerks}->{$clerk_name}->{areas}->{$area_name}->{totals}->{total_qttys}+=$rec->{quantity};
$sales_by_clerk_area_product{totals}->{total}+=$line_total;
$sales_by_clerk_area_product{totals}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk_area_product{totals}->{total_qttys}+=$rec->{quantity};
my($hour)=$rec->{sale_hour}=~/^(\d+)\:/;
$sales_by_hour{hours}->{$hour}->{total}+=$line_total;
$sales_by_hour{hours}->{$hour}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_hour{hours}->{$hour}->{total_qttys}+=$rec->{quantity};
$sales_by_hour{total}+=$line_total;
$sales_by_hour{total_wo_vat}+=$line_total_wo_vat;
$sales_by_hour{total_qttys}+=$rec->{quantity};
$sales_by_clerk{clerks}->{$clerk_name}->{total}+=$line_total;
$sales_by_clerk{clerks}->{$clerk_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk{clerks}->{$clerk_name}->{total_qttys}+=$rec->{quantity};
if(!exists$son_products{$rec->{product_id}}){$sales_by_product_no_sons{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_product_no_sons{products}->{$prod_name}->{total}+=$line_total;
$sales_by_product_no_sons{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_product_no_sons{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_product_no_sons{total}+=$line_total;
$sales_by_product_no_sons{total_qttys}+=$rec->{quantity};}$sales_by_product{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_product{products}->{$prod_name}->{total}+=$line_total;
$sales_by_product{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_product{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_product{total}+=$line_total;
$sales_by_product{total_qttys}+=$rec->{quantity};
$station_name=exists$station_names->{$rec->{station}}?$station_names->{$rec->{station}}:$rec->{station};
$sales_by_station_product{stations}->{$station_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_station_product{stations}->{$station_name}->{products}->{$prod_name}->{total}+=$line_total;
$sales_by_station_product{stations}->{$station_name}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_station_product{stations}->{$station_name}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_station_product{stations}->{$station_name}->{total}+=$line_total;
$sales_by_station_product{stations}->{$station_name}->{total_qttys}+=$rec->{quantity};
$sales_by_station_product{total}+=$line_total;
$sales_by_station_product{total_qttys}+=$rec->{quantity};
$sales_by_clerk_product{clerks}->{$clerk_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_clerk_product{clerks}->{$clerk_name}->{products}->{$prod_name}->{total}+=$line_total;
$sales_by_clerk_product{clerks}->{$clerk_name}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk_product{clerks}->{$clerk_name}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_by_clerk_product{clerks}->{$clerk_name}->{total}+=$line_total;
$sales_by_clerk_product{clerks}->{$clerk_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk_product{clerks}->{$clerk_name}->{total_qttys}+=$rec->{quantity};
$sales_by_clerk_product{total}+=$line_total;
$sales_by_clerk_product{total_wo_vat}+=$line_total_wo_vat;
$sales_by_clerk_product{total_qttys}+=$rec->{quantity};
if(exists$product_lists_info->{lists_by_products}->{$rec->{product_id}}){my$i=$product_lists_info->{lists_by_products}->{$rec->{product_id}};
my@lists=keys%$i;
foreach my $list(@lists){my$linfo=$product_lists_info->{lists}->{$list};
my$wd=$linfo->{weekdays_as_array};
if($wd->[$rec->{dow}]){my$list_name=$linfo->{name};
if($linfo->{end_hour}ne '00:00'){my$lsh=$rec->{business_date}." ".$linfo->{start_hour};
my$leh=$rec->{business_date}." ".$linfo->{end_hour};
my$sh=$rec->{business_date}." ".$rec->{sale_hour};
my@a=$lsh=~/^(\d+)\-(\d+)\-(\d+)\s(\d+)\:(\d+)/;
push@a,"00";
$lsh=Date_to_Time(@a);
@a=$leh=~/^(\d+)\-(\d+)\-(\d+)\s(\d+)\:(\d+)/;
push@a,"00";
$leh=Date_to_Time(@a);
@a=$sh=~/^(\d+)\-(\d+)\-(\d+)\s(\d+)\:(\d+)/;
push@a,"00";
$sh=Date_to_Time(@a);
if($sh>=$lsh&&$sh<=$leh){$sales_by_list{lists}->{$list_name}->{total}+=$line_total;
$sales_by_list{lists}->{$list_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_list{lists}->{$list_name}->{total_qttys}+=$rec->{quantity};
$sales_by_list{total}+=$line_total;
$sales_by_list{total_qttys}+=$rec->{quantity};
$sales_by_list{lists}->{$list_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_list{lists}->{$list_name}->{products}->{$prod_name}->{total}+=$line_total;
$sales_by_list{lists}->{$list_name}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_list{lists}->{$list_name}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};}}else{$sales_by_list{lists}->{$list_name}->{total}+=$line_total;
$sales_by_list{lists}->{$list_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_list{lists}->{$list_name}->{total_qttys}+=$rec->{quantity};
$sales_by_list{total}+=$line_total;
$sales_by_list{total_qttys}+=$rec->{quantity};
$sales_by_list{lists}->{$list_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_by_list{lists}->{$list_name}->{products}->{$prod_name}->{total}+=$line_total;
$sales_by_list{lists}->{$list_name}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_list{lists}->{$list_name}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};}}}}$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product_id}};
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{products}->{$prod_name}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{products}->{$prod_name}->{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{total_qttys}+=$rec->{quantity};
$sales_family{families}->{$fam_name}->{vats}->{$rec->{vat}}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{vats}->{$rec->{vat}}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{vats}->{$rec->{vat}}->{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_family{families}->{$fam_name}->{vats}->{$rec->{vat}}->{total_qttys}+=$rec->{quantity};
if($rec->{doc_type}==1||$rec->{doc_type}==5||$rec->{doc_type}==90||$rec->{doc_type}==12||$rec->{doc_type}==7){$sales_family{families}->{$fam_name}->{vats_received}->{vats}{$rec->{vat}}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{vats_received}->{vats}{$rec->{vat}}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{vats_received}->{vats}{$rec->{vat}}->{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_family{families}->{$fam_name}->{vats_received}->{vats}{$rec->{vat}}->{total_qttys}+=$rec->{quantity};
$sales_family{families}->{$fam_name}->{vats_received}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{vats_received}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{vats_received}->{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_family{families}->{$fam_name}->{vats_received}->{total_qttys}+=$rec->{quantity};}if($rec->{doc_type}==2||$rec->{doc_type}==13){$sales_family{families}->{$fam_name}->{vats_payed}->{vats}{$rec->{vat}}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{vats_payed}->{vats}{$rec->{vat}}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{vats_payed}->{vats}{$rec->{vat}}->{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_family{families}->{$fam_name}->{vats_payed}->{vats}{$rec->{vat}}->{total_qttys}+=$rec->{quantity};
$sales_family{families}->{$fam_name}->{vats_payed}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{vats_payed}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{vats_payed}->{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_family{families}->{$fam_name}->{vats_payed}->{total_qttys}+=$rec->{quantity};}$sales_family{families}->{$fam_name}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_family{families}->{$fam_name}->{total_qttys}+=$rec->{quantity};
$sales_family{total}+=$line_total;
$sales_family{total_wo_vat}+=$line_total_wo_vat;
$sales_family{total_wo_vat2}+=$line_total_wo_vat_net;
$sales_family{total_qttys}+=$rec->{quantity};
if($rec->{discount}>0){$discounts_family{families}->{$fam_name}->{total}+=$discount_w_vat*$rec->{quantity};
$discounts_family{families}->{$fam_name}->{total_wo_vat}+=$rec->{discount}*$rec->{quantity};
$discounts_family{families}->{$fam_name}->{total_qttys}+=$rec->{quantity};
$discounts_family{total}+=$discount_w_vat*$rec->{quantity};
$discounts_family{total_wo_vat}+=$rec->{discount}*$rec->{quantity};
$discounts_family{total_qttys}+=$rec->{quantity};
$discounts_family{products}->{$prod_name}->{total}+=$discount_w_vat*$rec->{quantity};
$discounts_family{products}->{$prod_name}->{total_wo_vat}+=$rec->{discount}*$rec->{quantity};
$discounts_family{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
my$price_div=$rec->{price}||1;
my$discount=$rec->{discount};
if($price_div){my$percent=($discount_w_vat/$price_div)*100;
$discounts_family{percentages}->{$percent}->{count}++;
$discounts_family{percentages}->{$percent}->{total}+=$discount_w_vat*$rec->{quantity};
$discounts_family{percentages}->{$percent}->{total_wo_vat}+=$rec->{discount}*$rec->{quantity};
$discounts_family{percentages}->{$percent}->{total_qttys}+=$rec->{quantity};}}if(!exists$done_persons{$rec->{header}}){if($rec->{persons_number}>0){if($rec->{doc_type}==2||$rec->{doc_type}==13||$rec->{doc_type}==14||$rec->{doc_type}==33){$rec->{persons_number}=$rec->{persons_number}*-1;}$persons_number{clerks}->{$clerk_name}+=$rec->{persons_number};
$persons_number{areas}->{$AREAS_INFO{$rec->{area}}->[0]}+=$rec->{persons_number};
$persons_number{total}+=$rec->{persons_number};}$done_persons{$rec->{header}}=1;}}if(exists$selections->{"58_refunds.txt"}&&exists$refunds{by_date}){foreach my $date(keys%{$refunds{by_date}}){my$r=$refunds{by_date}->{$date};
foreach my $fo_doc_num(keys%{$r->{docs}}){$r->{docs}->{$fo_doc_num}->{clerk_invoiced}=$r->{docs}->{$fo_doc_num}->{clerk};
my$gbi=$db->select("SELECT gb_identifier FROM sales_headers WHERE business_date = ? AND fo_doc_number = ?",$date,$fo_doc_num,);
if(scalar@$gbi){my$tmp=$db->select(qq{
            SELECT sh.fo_doc_number, sh.business_date, sh.gb_identifier, sh.clerk_close_name
            FROM document_operations doco
            LEFT JOIN sales_headers sh ON (sh.gb_identifier = doco.source_gb_identifier)
            WHERE doco.dest_gb_identifier = ? AND sh.doc_type != 3
          },$gbi->[0]->{gb_identifier});
if(scalar@$tmp){$r->{docs}->{$fo_doc_num}->{doc_original_clerk}=$tmp->[0]->{clerk_close_name};
$r->{docs}->{$fo_doc_num}->{doc_original_fo_doc_number}=$tmp->[0]->{fo_doc_number};}}}}}$cfg->{GLOBAL_CASHIER_OPENING_VALUE}||=0;
my%finantial_info=(opening_amount=>0,total_docs=>$total_docs);
my@date_to_arr=split/-/,$date_to;
my$initial_file=$machine->dir_server()."/server/data/cashier_open_amount/$date_to_arr[0]/$date_to_arr[1]/$date_to/amount.txt";
my$initial_amount=0;
if(scalar keys%sales_by_doctype_fininfo){foreach my $doc_type(keys%{$sales_by_doctype_fininfo{doc_types}}){foreach my $to_customer_account(keys%{$sales_by_doctype_fininfo{doc_types}->{$doc_type}}){foreach my $is_payed(keys%{$sales_by_doctype_fininfo{doc_types}->{$doc_type}->{$to_customer_account}}){my$info=$sales_by_doctype_fininfo{doc_types}->{$doc_type}->{$to_customer_account}->{$is_payed};
next if$info->{doc_type}==4&&!$cfg->{CONSUMPTIONS_AFFECT_SALES};
my$key="";
my$sign=1;
if($doc_type eq$doc_types->{"8"}){$key='receipts';}elsif($doc_type eq$doc_types->{"13"}&&!$to_customer_account){$key='sales';}elsif($doc_type eq$doc_types->{"7"}&&!$is_payed&&$to_customer_account){$key='credits';
$sign=1;}elsif(($doc_types_by_name->{$doc_type}==7&&$is_payed&&!$to_customer_account)||($doc_types_by_name->{$doc_type}==13&&!$to_customer_account)||$doc_types_by_name->{$doc_type}==1||$doc_types_by_name->{$doc_type}==2||$doc_types_by_name->{$doc_type}==90||$doc_types_by_name->{$doc_type}==5){$key='sales';}if($key ne ''){$finantial_info{type}->{$key}->{total}+=$info->{total}*$sign;
if($key ne 'credits'&&$key ne 'receipts'){$finantial_info{balance}+=$info->{total}*$sign;
$finantial_info{balance_wo_vat}+=$info->{total_wo_vat}*$sign;}}}}}}$finantial_info{balance}+=$finantial_info{opening_amount};
$finantial_info{balance_wo_vat}+=$finantial_info{opening_amount};
$recs=$db->select(qq{
    SELECT t.*, EXTRACT(hour FROM t.date) AS hour, EXTRACT(minute FROM t.date) AS minute
    FROM money_counts_operations t
    WHERE t.business_date >= ? AND t.business_date = ? $sql_clerk_query1
    ORDER BY date
  },@sql_params_two);
my%cashier_ops=();
for(@$recs){$_->{hour}=sprintf("%02d",$_->{hour}).":".sprintf("%02d",$_->{minute});
push@{$cashier_ops{$_->{operation}}->{ops}},$_;
if($_->{operation}eq 'INS'){$finantial_info{balance}+=$_->{amount};
$finantial_info{balance_wo_vat}+=$_->{amount};}else{$finantial_info{balance}-=$_->{amount};
$finantial_info{balance_wo_vat}-=$_->{amount};}$cashier_ops{$_->{operation}}->{total}+=$_->{amount};}$recs=$db->select(qq{
    SELECT t.*, EXTRACT(hour FROM t.date) AS hour, EXTRACT(minute FROM t.date) AS minute
    FROM cashdrawer_operations t
    WHERE t.business_date >= ? AND t.business_date = ? $sql_clerk_query1
    ORDER BY date
  },@sql_params_two);
my%cashdrawer_operations;
for(@$recs){my$justification=$_->{params}||"-----";
my$hour="--:--";
my$op=$_->{operation};
next if$_->{operation}eq 'IN'&&$_->{params}eq 'VENDA';
next if$_->{operation}eq 'ESTORNO';
if($op eq 'IN'){$op='INS';}else{$op='OUTS';}push@{$cashdrawer_operations{$op}},$_;}if(scalar keys%cashdrawer_operations){foreach my $op(keys%cashdrawer_operations){my$arr=$cashdrawer_operations{$op};
foreach my $rec(sort{$a->{id}<=>$b->{id}}@$arr){my$justification=$rec->{params}||"-----";
$justification='-----' if$justification eq 'NULL';
my$amount=$rec->{amount};
my$clerk_name=exists$CLERKS_INFO->{$rec->{clerk}}?$CLERKS_INFO->{$rec->{clerk}}->{login}:'-------';
my($hour)=$rec->{date}=~/^\d\d\d\d\-\d\d\-\d\d (\d\d:\d\d)/;
push@{$cashier_ops{$op}->{ops}},{hour=>$hour,clerk=>$clerk_name,justification=>$justification,amount=>$amount,};
if($op eq 'INS'){$finantial_info{balance}+=$amount;
$finantial_info{balance_wo_vat}+=$amount;}else{$finantial_info{balance}-=$amount;
$finantial_info{balance_wo_vat}-=$amount;}$cashier_ops{$op}->{total}+=$amount;}}}$finantial_info{cashier_ops}=\%cashier_ops;
if(exists$selections->{"59_bills_by_clerk.txt"}){$sth=$db->dbh()->prepare(qq{
      SELECT sh.value, sh.business_date, sh.fo_doc_number, sh.clerk_close, sh.table_num,
        c.login AS clerk_login, sh.system_entry_date
      FROM sales_hashes sh
      LEFT JOIN clerks c ON (sh.clerk_close = c.id)
      WHERE sh.doc_type = 6 AND sh.business_date >= ? AND sh.business_date <= ? $sql_clerk_query1
      ORDER BY c.login, sh.header ASC
    });
$sth->execute(@sql_params_two);
while(my$rec=$sth->fetchrow_hashref){my$clerk_name=exists$CLERKS_INFO->{$rec->{clerk_close}}?$CLERKS_INFO->{$rec->{clerk_close}}->{login}:'<UNKNOWN>';
my($hour)=$rec->{system_entry_date}=~/T(\d\d:\d\d)/;
my($fo_doc_number_small)=$rec->{fo_doc_number}=~/\/(\d+)$/;
push@{$bills_by_clerk{clerks}->{$clerk_name}},{business_date=>$rec->{business_date},fo_doc_number=>$rec->{fo_doc_number},fo_doc_number_small=>$fo_doc_number_small,value=>$rec->{value},business_date=>$rec->{business_date},table_number=>$rec->{table_num},hour=>$hour,}}}if(exists$selections->{"60_settlements_by_customer.txt"}){$sth=$db->dbh()->prepare(qq{
      SELECT sh.business_date, sh.fo_doc_number, sh.value,
        sha1.key_value AS is_settlement, sha2.key_value AS canceled,
        sha3.key_value AS refunded,
        c.id AS customer_id
      FROM sales_headers sh
      LEFT JOIN sales_headers_aux sha1 ON (sh.id = sha1.header AND sha1.key_name = 'is_settlement')
      LEFT JOIN sales_headers_aux sha2 ON (sh.id = sha2.header AND sha2.key_name = 'canceled')
      LEFT JOIN sales_headers_aux sha3 ON (sh.id = sha3.header AND sha3.key_name = 'refunded')
      LEFT JOIN customers c ON (sh.customer_id = c.id)
      WHERE sh.business_date >= ? AND sh.business_date <= ? AND sh.customer_id != 0 AND sha1.key_value <> ''
      ORDER BY sh.id
    });
$sth->execute(@sql_params_two);
my%recs_by_customers;
while(my$rec=$sth->fetchrow_hashref){$rec->{canceled}||=0;
$rec->{refunded}||=0;
next if$rec->{canceled}||$rec->{refunded};
push@{$recs_by_customers{$rec->{customer_id}}},$rec;}$sth=$db->dbh()->prepare(qq{
      SELECT c.id, c.name, c.nif, c.settlement, c.aux
      FROM customers c
      WHERE c.deleted = 0 AND c.settlement <> '' AND c.settlement <> '0' AND c.settlement <> '--'
      ORDER BY c.name
    });
$sth->execute();
while(my$rec=$sth->fetchrow_hashref){my$invoices=$recs_by_customers{$rec->{id}}//[];
$settlements_by_customer{customers}->{$rec->{id}}->{invoices}=$invoices;
$settlements_by_customer{customers}->{$rec->{id}}->{name}=$rec->{name};
$settlements_by_customer{customers}->{$rec->{id}}->{nif}=$rec->{nif};
if($rec->{settlement}=~/,/){my@obs=();
if($rec->{aux}){@obs=split/,/,$rec->{aux};}my@tmp=split/,/,$rec->{settlement};
for(my$i=0;$i<scalar@tmp;$i++){my%info=%$rec;
my$str=trim_string($tmp[$i]);
$info{settlement}=$str;
if($i<=scalar@obs){$info{aux}=$obs[$i];}push@{$settlements_by_customer{customers}->{$rec->{id}}->{settlements}},\%info;
$settlements_by_customer{total}+=$info{settlement}<0?$info{settlement}*-1:$info{settlement};}}else{push@{$settlements_by_customer{customers}->{$rec->{id}}->{settlements}},$rec;
$settlements_by_customer{total}+=$rec->{settlement}<0?$rec->{settlement}*-1:$rec->{settlement};}}my%settlements_final=(total=>$settlements_by_customer{total});
foreach my $id(sort{lc($settlements_by_customer{customers}->{$a}->{name})cmp lc($settlements_by_customer{customers}->{$b}->{name})}keys%{$settlements_by_customer{customers}}){my$r=$settlements_by_customer{customers}->{$id};
$r->{invoices}||=[];
for(@{$r->{invoices}}){$r->{invoices_total}+=$_->{value};}push@{$settlements_final{customers}},$r;}%settlements_by_customer=%settlements_final;}if(exists$selections->{"57_money_counts.txt"}){$recs=$db->select(qq{
      SELECT t.*, EXTRACT(hour FROM t.date) AS hour, EXTRACT(minute FROM t.date) AS minute
      FROM money_counts t
      WHERE t.business_date >= ? AND t.business_date = ? $sql_clerk_query1
      ORDER BY date
    },@sql_params_two);
for(@$recs){$_->{txt}=~s/\@paper_cut\@//g;
push@{$money_counts{dates}->{$_->{business_date}}},$_;}}print STDERR"QUERY5\n";
$sth=$db->dbh()->prepare(qq{
    SELECT t.*, tp.*
    FROM transfer_products tp
    LEFT JOIN transfers t ON (tp.transfer_id = t.id)
    WHERE t.date >= ? AND t.date <= ?$sql_clerk_query4
    ORDER BY t.id
  });
$sth->execute(@sql_params_two);
print STDERR"QUERY5.1\n";
while(my$rec=$sth->fetchrow_hashref){my$datehour=$rec->{date}." ".$rec->{hour};
my$clerk_name=&my_conv($rec->{clerk_name});
my$prod_name=&my_conv($rec->{product_name});
my$h=$rec->{hour_registered};
my$area_from=$AREAS_INFO{$rec->{area_from}}->[0];
my$area_to=$AREAS_INFO{$rec->{area_to}}->[0];
$transfers{$clerk_name}->{$datehour}->{station}=$rec->{station};
$transfers{$clerk_name}->{$datehour}->{area_from}=$area_from;
$transfers{$clerk_name}->{$datehour}->{area_to}=$area_to;
$transfers{$clerk_name}->{$datehour}->{table_from}=$rec->{table_from};
$transfers{$clerk_name}->{$datehour}->{table_to}=$rec->{table_to};
$transfers{$clerk_name}->{$datehour}->{hours}->{$h}->{product_name}=$rec->{product_name};
$transfers{$clerk_name}->{$datehour}->{hours}->{$h}->{quantity}=$rec->{quantity};}print STDERR"QUERY7\n";
$sth=$db->dbh()->prepare(qq{
      SELECT sd.product_name, sh.business_date, sd.price, sd.price_wo_vat, sd.quantity,
        sd.discount, sd.offer, sd.vat, sd.operation_type, c.login, sd.free_text,
        sh.table_num, sd.hour, sd.product,
      CASE
        WHEN f.id IS NOT NULL THEN COALESCE(f.name,'----------')
        ELSE COALESCE(sf.name,'----------')
      END as family_name,
      CASE
        WHEN sf.id IS NOT NULL THEN COALESCE(sf.name,'----------')
        ELSE '----------'
      END as subfamily_name
      FROM sales_details sd
      LEFT JOIN sales_headers sh ON (sh.id = sd.header)
      LEFT JOIN products p ON (sd.product = p.id)
      LEFT JOIN (
        SELECT product_id, MAX(group_id) as group_id
        FROM product_groups
        GROUP BY product_id
      ) pg ON (p.id = pg.product_id)
      LEFT JOIN groups sf ON (pg.group_id = sf.id)
      LEFT JOIN groups f ON (sf.parent = f.id)
      LEFT JOIN clerks c ON (sd.clerk = c.id)
      WHERE sd.operation_type IN (2,3,4)
      AND sh.business_date >= ? AND sh.business_date <= ?$sql_clerk_query3
      AND sd.business_date >= ? AND sd.business_date <= ?
    });
$sth->execute(@sql_params_four);
while(my$rec=$sth->fetchrow_hashref){$rec->{price}||=0;
$rec->{discount}||=0;
my$discount_w_vat=$rec->{discount}*(1+$rec->{vat}/100);
my$price=$rec->{price}-$discount_w_vat;
my$line_total=sprintf("%.2f",$rec->{quantity}*$price);
my$line_total_wo_vat=($rec->{quantity}*$price)/(1+$rec->{vat}/100);
my$void_type="";
if($rec->{operation_type}==3){$void_type="VOID_WASTE";}elsif($rec->{operation_type}==2){$void_type="VOID";}elsif($rec->{operation_type}==4){$void_type="CORRECTION";}my$prod_name=&my_conv($rec->{product_name});
my$free_text=$rec->{free_text}||"--------§--------§00:00:00";
$free_text=my_conv($free_text);
$free_text=~s/»«/§/g;
my@tmp=split/§/,$free_text;
my$reason=$tmp[0];
my$clerk_registered=$tmp[1];
my$hour_registered=$tmp[2];
if($rec->{operation_type}==3){$reason=exists$waste_reasons{$reason}?$waste_reasons{$reason}:$reason;
$reason||=&translate($machine,"NAO DEFINIDO");
$reason=trim_string($reason);
if($reason){$voids_by_reason{logins}->{$rec->{login}}->{$reason}->{total}+=$line_total;
$voids_by_reason{logins}->{$rec->{login}}->{$reason}->{total_wo_vat}+=$line_total_wo_vat;
$voids_by_reason{logins}->{$rec->{login}}->{$reason}->{total_qttys}+=$rec->{quantity};
$voids_by_reason{totals}->{$reason}->{total}+=$line_total;
$voids_by_reason{totals}->{$reason}->{total_wo_vat}+=$line_total_wo_vat;
$voids_by_reason{totals}->{$reason}->{total_qttys}+=$rec->{quantity};
$voids_by_reason{products}->{$prod_name}->{$reason}->{total}+=$line_total;
$voids_by_reason{products}->{$prod_name}->{$reason}->{total_wo_vat}+=$line_total_wo_vat;
$voids_by_reason{products}->{$prod_name}->{$reason}->{total_qttys}+=$rec->{quantity};
$voids_by_reason{total}+=$line_total;
$voids_by_reason{total_wo_vat}+=$line_total_wo_vat;
$voids_by_reason{total_qttys}+=$rec->{quantity};}}if($rec->{operation_type}==3||$rec->{operation_type}==2){$hour_registered=~s/\:\d\d$//;
$rec->{hour}=~s/\:\d\d$// if defined$rec->{hour};
my$clerk_name=exists$CLERKS_INFO->{$clerk_registered}?$CLERKS_INFO->{$clerk_registered}->{login}:'<UNKNOWN>';
push@{$voids_detailed{$rec->{login}}->{$rec->{business_date}}},{table_num=>$rec->{table_num},product=>$prod_name,quantity=>$rec->{quantity},hour_registered=>$hour_registered,clerk_registered=>$clerk_name,hour_voided=>$rec->{hour},};}$voids{logins}->{$rec->{login}}->{$void_type}->{total}+=$line_total;
$voids{logins}->{$rec->{login}}->{$void_type}->{total_wo_vat}+=$line_total_wo_vat;
$voids{logins}->{$rec->{login}}->{$void_type}->{total_qttys}+=$rec->{quantity};
$voids{logins}->{$rec->{login}}->{$void_type}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->{product}};
$voids{logins}->{$rec->{login}}->{$void_type}->{products}->{$prod_name}->{total_qttys}+=$rec->{quantity};
$voids{logins}->{$rec->{login}}->{$void_type}->{products}->{$prod_name}->{total}+=$line_total;
$voids{logins}->{$rec->{login}}->{$void_type}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$voids{totals}->{$void_type}->{total}+=$line_total;
$voids{totals}->{$void_type}->{total_wo_vat}+=$line_total_wo_vat;
$voids{totals}->{$void_type}->{total_qttys}+=$rec->{quantity};}print STDERR"QUERY8\n";
$sth=$db->dbh()->prepare(qq{
      SELECT sd.value, sd.value AS total_doc, sd.payment_media, sha6.key_value AS invoice_is_payed,
        sh.doc_type, sh.value AS total_header, sh.station_close, sha7.key_value AS to_customer_account,
        sha3.key_value AS doc_canceled
      FROM sales_payments sd
      LEFT JOIN sales_headers sh ON (sh.id = sd.header)
      LEFT JOIN sales_headers_aux sha3 ON (sh.id = sha3.header AND sha3.key_name = 'canceled')
      LEFT JOIN sales_headers_aux sha6 ON (sh.id = sha6.header AND sha6.key_name = 'invoice_is_payed')
      LEFT JOIN sales_headers_aux sha7 ON (sh.id = sha7.header AND sha7.key_name = 'to_customer_account')
      LEFT JOIN sales_headers_aux sha8 ON (sh.id = sha8.header AND sha8.key_name = 'manualdoc_serie')
      WHERE sh.doc_type IN (5, 1, 8, 2, 90, 7, 13) AND sh.business_date >= ? AND sh.business_date <= ?$sql_clerk_query2
      AND sd.business_date >= ? AND sd.business_date <= ? AND sha8.key_value IS NULL
    });
$sth->execute(@sql_params_four);
while(my$rec=$sth->fetchrow_hashref){$rec->{invoice_is_payed}||=0;
$rec->{to_customer_account}||=0;
$rec->{doc_canceled}||=0;
next if$rec->{doc_canceled};
if(($rec->{doc_type}==7||$rec->{doc_type}==13)&&$rec->{to_customer_account}){next;}my$media_name=$rec->{payment_media};
my$ref=\%sales_by_media;
$ref->{medias}->{uc($media_name)}+=$rec->{value};
$ref->{total}+=$rec->{value};
$ref->{total_doc}+=$rec->{total_doc};
$ref->{total_voids}+=$rec->{value}if$rec->{value}<0;
$station_name=exists$station_names->{$rec->{station_close}}?$station_names->{$rec->{station_close}}:$rec->{station_close};
$ref->{stations}->{$station_name}->{medias}->{uc($media_name)}+=$rec->{value};
$ref->{stations}->{$station_name}->{total}+=$rec->{value};
$ref->{stations}->{$station_name}->{total_doc}+=$rec->{total_doc};
if($rec->{value}<0){$media_name='REFUNDS';
$ref->{medias}->{uc($media_name)}+=$rec->{value};
$media_name='REFUNDS';
$ref->{stations}->{$station_name}->{medias}->{uc($media_name)}+=$rec->{value};
$ref->{stations}->{$station_name}->{total_voids}+=$rec->{value}if$rec->{value}<0;}if($rec->{doc_type}==8){$ref=\%receipts_by_media;
$ref->{medias}->{uc($media_name)}+=$rec->{value};
$ref->{total}+=$rec->{value};
$ref->{total_doc}+=$rec->{total_doc};
$ref->{total_voids}+=$rec->{value}if$rec->{value}<0;
$station_name=exists$station_names->{$rec->{station_close}}?$station_names->{$rec->{station_close}}:$rec->{station_close};
$ref->{stations}->{$station_name}->{medias}->{uc($media_name)}+=$rec->{value};
$ref->{stations}->{$station_name}->{total}+=$rec->{value};
$ref->{stations}->{$station_name}->{total_doc}+=$rec->{total_doc};
if($rec->{value}<0){$media_name='REFUNDS';
$ref->{medias}->{uc($media_name)}+=$rec->{value};
$media_name='REFUNDS';
$ref->{stations}->{$station_name}->{medias}->{uc($media_name)}+=$rec->{value};
$ref->{stations}->{$station_name}->{total_voids}+=$rec->{value}if$rec->{value}<0;}}}my$dir_areas=$machine->dir_server()."/server/data/areas";
my%not_closed_checks=();
my$do_notclosed_report=0;
if($biz_date>=$dstart&&$biz_date<=$dend){$do_notclosed_report=1;}if($do_notclosed_report){print STDERR"QUERY9\n";
my%custom_tables=();
find(sub{return unless-d$File::Find::name;
return if$File::Find::name eq$dir_areas;
my$area=$_;
my$area_info=$AREAS_INFO{$area};
my$start_idx=0;
my$end_idx=0;
if(defined$area_info&&scalar@$area_info>=7){$area_info->[1]||=0;
$area_info->[6]||=0;
$start_idx=$area_info->[6]||1;
$end_idx=($start_idx+$area_info->[1])-1;}else{$area_info->[1]||=0;
$start_idx=1;
$end_idx=($start_idx+$area_info->[1])-1;}$start_idx=0 if$end_idx==0;
$not_closed_checks{areas}->{$area}->{checks}=[];
find(sub{my$number=$_;
return unless$number=~/^\d+$/;
return unless$number<90000;
if($start_idx!=0&&$end_idx!=0){if($number<$start_idx||$number>$end_idx){return;}}my$file_check=$dir_areas."/".$area."/".$number;
my$file_check_total=$dir_areas."/".$area."/".$number.".total";
return unless-e$file_check_total;
my$o=App::POS::Check->new(file=>$file_check);
my$data=$o->data();
my$extra_data=$o->extra_data();
my$products_extra_data=$o->products_extra_data();
$extra_data->{custom_table_number}||="";
if($extra_data->{custom_table_number}){$custom_tables{$extra_data->{original_area}}->{$number}=$extra_data->{custom_table_number};}my$clerk_open=$data->{header}->[3];
my$clerk_name=exists$CLERKS_INFO->{$clerk_open}?$CLERKS_INFO->{$clerk_open}->{login}:'<UNKNOWN>';
$data->{header}->[15]||=0;
$data->{header}->[5]||=0;
if($data->{header}->[15]>0){if(exists$extra_data->{original_area}){$persons_number{areas}->{$AREAS_INFO{$extra_data->{original_area}}->[0]}+=$data->{header}->[15];}else{$persons_number{areas}->{$AREAS_INFO{$area}->[0]}+=$data->{header}->[15];}$persons_number{clerks}->{$clerk_name}+=$data->{header}->[15];
$persons_number{total}+=$data->{header}->[15];}my$total=$data->{header}->[19];
my$table_to_card=exists$TABLES_TO_CARDS{$number}?$TABLES_TO_CARDS{$number}:'';
return unless defined$area&&exists$AREAS_INFO{$area};
my@ainfo=@{$AREAS_INFO{$area}};
my$type=scalar@ainfo>=5?$ainfo[4]:'';
$not_closed_checks{areas}->{$area}->{type}=$type;
my$det=$data->{details}||[];
my%cinfo=();
my$global_discount_tmp=$data->{header}->[10];
my$global_discount_percent=0;
my$global_discount=0;
if($global_discount_tmp){if($global_discount_tmp=~/^(\d+)%$/){$global_discount_percent=1;
$global_discount=$1;}else{$global_discount=$global_discount_tmp;}}foreach my $d(@$det){next unless scalar@$d;
my$clerk_register=$d->[7];
if($cfg->{CLERK_FOR_SALES_DATA}==1||$cfg->{CLERK_FOR_SALES_DATA}==2){$clerk_name=exists$CLERKS_INFO->{$clerk_open}?$CLERKS_INFO->{$clerk_open}->{login}:'<UNKNOWN>';
if(defined$clerk_filter){next unless$clerk==$clerk_open;}}else{$clerk_name=exists$CLERKS_INFO->{$clerk_register}?$CLERKS_INFO->{$clerk_register}->{login}:'<UNKNOWN>';
if(defined$clerk_filter){next unless$clerk==$clerk_register;}}my$pci=$d->[4]||0;
my$q=$d->[3]||0;
my$perciva=$d->[6]||0;
my$psi=$pci/(1+($perciva/100));
my$clerk=$d->[7];
my$prod_name=exists$PRODUCTS_INFO->{$d->[1]}?my_conv($PRODUCTS_INFO->{$d->[1]}->{name}):'UNKNOWN';
my$fam_name=exists$PRODUCTS_INFO->{$d->[1]}?my_conv($PRODUCTS_INFO->{$d->[1]}->{family_name}):'UNKNOWN';
my$sub_fam_name=exists$PRODUCTS_INFO->{$d->[1]}?my_conv($PRODUCTS_INFO->{$d->[1]}->{subfamily_name}):'UNKNOWN';
my$desc=0;
if($global_discount>0){if($global_discount_percent){$desc=$psi*($global_discount/100);}else{my$div=$data->{header}->[19]+$global_discount||1;
my$disc=($global_discount/$div)*100;
$desc=$psi*($disc/100);}}$pci=&my_round((($psi-($desc+$d->[5])))*(1+$perciva/100),$cfg->{DECIMAL_PLACES}||2);
$psi=&my_round((($psi-($desc+$d->[5]))),$cfg->{DECIMAL_PLACES}||2);
if(exists$products_extra_data->{$d->[-1]}){my@toks1=split/;/,$products_extra_data->{$d->[-1]};
for(@toks1){my@toks2=split/:/;
next unless scalar@toks2>1;
$toks2[1]=~s/^\s+//;
$sales_by_product_extra_data{products}->{$prod_name}->{$toks2[0]}->{$toks2[1]}++;}}if(($report_type eq 'resumed'||$report_type eq 'user')){$station_name=exists$station_names->{$data->{header}->[5]}?$station_names->{$data->{header}->[5]}:$data->{header}->[5];
my$line_total=$pci*$q;
my$line_total_wo_vat=$psi*$q;
$sales_by_station{stations}->{$station_name}->{total}+=$line_total;
$sales_by_station{stations}->{$station_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_station{stations}->{$station_name}->{total_qttys}+=$q;
$sales_by_station{total}+=$line_total;
$sales_by_station{total_wo_vat}+=$line_total_wo_vat;
$sales_by_station{total_qttys}+=$q;
$sales_by_product{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$d->[1]};
$sales_by_product{products}->{$prod_name}->{total}+=$line_total;
$sales_by_product{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_product{products}->{$prod_name}->{total_qttys}+=$q;
$sales_by_product{total}+=$line_total;
$sales_by_product{total_qttys}+=$q;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$d->[1]};
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{products}->{$prod_name}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{products}->{$prod_name}->{total_qttys}+=$q;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{subfamilies}->{$sub_fam_name}->{total_qttys}+=$q;
$sales_family{families}->{$fam_name}->{vats}->{$perciva}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{vats}->{$perciva}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{vats}->{$perciva}->{total_qttys}+=$q;
$sales_family{families}->{$fam_name}->{total}+=$line_total;
$sales_family{families}->{$fam_name}->{total_wo_vat}+=$line_total_wo_vat;
$sales_family{families}->{$fam_name}->{total_qttys}+=$q;
$sales_family{total}+=$line_total;
$sales_family{total_wo_vat}+=$line_total_wo_vat;
$sales_family{total_qttys}+=$q;
$station_name=exists$station_names->{$d->[12]}?$station_names->{$d->[12]}:$d->[12];
if(scalar keys%DELIVERY_AREAS&&exists$selections->{"50_products_to_deliver.txt"}&&exists$DELIVERY_AREAS{$area}){if(exists$extra_data->{delivery_date}&&$extra_data->{delivery_date}){my$dd=$extra_data->{delivery_date};
$dd=~s/\-//g;
if($dd>=$date_from_clean&&$dd<=$date_to_clean){$products_to_deliver{dates}->{$extra_data->{delivery_date}}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$d->[1]};
$products_to_deliver{dates}->{$extra_data->{delivery_date}}->{products}->{$prod_name}->{total}+=$pci*$q;
$products_to_deliver{dates}->{$extra_data->{delivery_date}}->{products}->{$prod_name}->{total_wo_vat}+=$psi*$q;
$products_to_deliver{dates}->{$extra_data->{delivery_date}}->{products}->{$prod_name}->{total_qttys}+=$q;
$products_to_deliver{dates}->{$extra_data->{delivery_date}}->{total}+=$pci*$q;
$products_to_deliver{dates}->{$extra_data->{delivery_date}}->{total_wo_vat}+=$psi*$q;
$products_to_deliver{dates}->{$extra_data->{delivery_date}}->{total_qttys}+=$q;
$products_to_deliver{total}+=$pci*$q;
$products_to_deliver{total_qttys}+=$q;}}}$sales_by_station_product{stations}->{$station_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$d->[1]};
$sales_by_station_product{stations}->{$station_name}->{products}->{$prod_name}->{total}+=$pci*$q;
$sales_by_station_product{stations}->{$station_name}->{products}->{$prod_name}->{total_wo_vat}+=$psi*$q;
$sales_by_station_product{stations}->{$station_name}->{products}->{$prod_name}->{total_qttys}+=$q;
$sales_by_station_product{stations}->{$station_name}->{total}+=$pci*$q;
$sales_by_station_product{stations}->{$station_name}->{total_qttys}+=$q;
$sales_by_station_product{total}+=$pci*$q;
$sales_by_station_product{total_qttys}+=$q;
$sales_by_clerk_product{clerks}->{$clerk_name}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$d->[1]};
$sales_by_clerk_product{clerks}->{$clerk_name}->{products}->{$prod_name}->{total}+=$pci*$q;
$sales_by_clerk_product{clerks}->{$clerk_name}->{products}->{$prod_name}->{total_wo_vat}+=$psi*$q;
$sales_by_clerk_product{clerks}->{$clerk_name}->{products}->{$prod_name}->{total_qttys}+=$q;
$sales_by_clerk_product{clerks}->{$clerk_name}->{total}+=$pci*$q;
$sales_by_clerk_product{clerks}->{$clerk_name}->{total_qttys}+=$q;
$sales_by_clerk_product{total}+=$pci*$q;
$sales_by_clerk_product{total_qttys}+=$q;}$not_closed_checks{areas}->{$area}->{clerks}->{$clerk_name}+=$pci*$q;
$not_closed_checks{areas}->{$area}->{total}+=$pci*$q;
if($ainfo[1]==0&&$ainfo[4]){$not_closed_checks{vats}->{$perciva*1}->{total}+=$pci*$q;
$not_closed_checks{vats}->{$perciva*1}->{total_wo_vat}+=$psi*$q;
$not_closed_checks{vats}->{$perciva*1}->{vat}+=$pci*$q-$psi*$q;}$cinfo{$area}->{$number}->{total}+=$pci*$q;
if(!exists$cinfo{$area}->{$number}->{name}){$cinfo{$area}->{$number}->{name}=$o->name();}if($report_type eq 'resumed'||$report_type eq 'user'){if($ainfo[1]>0&&defined$card_sets){my$card_set_info=$card_sets->{by_table}->{$number};
my$line_total=$pci*$q;
my$line_total_wo_vat=$psi*$q;
if(defined$card_set_info){for(@$card_set_info){$sales_by_cardset{"set"}->{sets}->{$_}->{cards}->{$number}->{total}+=$line_total;
$sales_by_cardset{"set"}->{sets}->{$_}->{cards}->{$number}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cardset{"set"}->{sets}->{$_}->{cards}->{$number}->{total_qttys}+=$q;
$sales_by_cardset{"set"}->{sets}->{$_}->{name}=$card_sets->{sets}->{$_}->[0];
$sales_by_cardset{"set"}->{sets}->{$_}->{total}+=$line_total;
$sales_by_cardset{"set"}->{sets}->{$_}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cardset{"set"}->{sets}->{$_}->{total_qttys}+=$q;
$sales_by_cardset{"set"}->{total}+=$line_total;
$sales_by_cardset{"set"}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cardset{"set"}->{total_qttys}+=$q;}}else{$sales_by_cardset{"without_set"}->{total}+=$line_total;
$sales_by_cardset{"without_set"}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cardset{"without_set"}->{total_qttys}+=$q;
$sales_by_cardset{"without_set"}->{cards}->{$number}->{total}+=$line_total;
$sales_by_cardset{"without_set"}->{cards}->{$number}->{total_wo_vat}+=$line_total_wo_vat;
$sales_by_cardset{"without_set"}->{cards}->{$number}->{total_qttys}+=$q;}}}}foreach my $area(keys%cinfo){foreach my $number(keys%{$cinfo{$area}}){my$custom_table="--";
if(exists$custom_tables{$area}->{$number}){$custom_table=$custom_tables{$area}->{$number};}push@{$not_closed_checks{areas}->{$area}->{checks}},{number=>$number,total=>$cinfo{$area}->{$number}->{total},name=>$cinfo{$area}->{$number}->{name},table_to_card=>$table_to_card,custom_table=>$custom_table,};}}my$det_del=$data->{details_deleted}||[];
if(scalar@$det_del){foreach my $rec(@$det_del){if($report_type eq 'user'){next if defined$clerk&&$clerk!=$rec->[7];}$rec->[10]||=0;
$rec->[4]||=0;
$rec->[5]||=0;
$rec->[3]||=1;
$rec->[6]||=0;
my$void_type="";
if($rec->[10]==3){$void_type="VOID_WASTE";}elsif($rec->[10]==2){$void_type="VOID";}elsif($rec->[10]==4){$void_type="CORRECTION";}next unless$void_type;
my$discount=$rec->[11];
if($rec->[11]){if($rec->[11]=~/\%$/){($discount)=$rec->[11]=~/^(.*?)\%$/;
$discount=$rec->[4]*$discount/100;}else{$discount=$rec->[11];}}$discount||=0;
my$price=$rec->[4]-$discount;
my$line_total=$rec->[3]*$price;
my$line_total_wo_vat=($rec->[3]*$price)/(1+$rec->[4]/100);
my$product_name=exists$PRODUCTS_INFO->{$rec->[1]}?$PRODUCTS_INFO->{$rec->[1]}->{name}:'UNKNOWN';
my$prod_name=&my_conv($product_name);
my$reason_tmp=$rec->[13]||"§0";
$reason_tmp=my_conv($reason_tmp);
my@tmp_void=split/§/,$reason_tmp;
my$reason=$tmp_void[0];
my$login=exists$CLERKS_INFO->{$rec->[7]}?$CLERKS_INFO->{$rec->[7]}->{login}:'<UNKNOWN>';
if($rec->[10]==3){$reason=exists$waste_reasons{$reason}?$waste_reasons{$reason}:$reason;
$reason||=&translate($machine,"NAO DEFINIDO");
if($reason){$voids_by_reason{logins}->{$login}->{$reason}->{total}+=$line_total;
$voids_by_reason{logins}->{$login}->{$reason}->{total_wo_vat}+=$line_total_wo_vat;
$voids_by_reason{logins}->{$login}->{$reason}->{total_qttys}+=$rec->[3];
$voids_by_reason{totals}->{$reason}->{total}+=$line_total;
$voids_by_reason{totals}->{$reason}->{total_wo_vat}+=$line_total_wo_vat;
$voids_by_reason{totals}->{$reason}->{total_qttys}+=$rec->[3];}}if($rec->[10]==3||$rec->[10]==2){my($hour_registered,$hour_voided)=split/\@/,$rec->[2];
my$clerk_registered=$tmp_void[1];
$hour_registered=~s/\:\d\d$//;
$hour_voided=~s/\:\d\d$//;
my$clerk_name=exists$CLERKS_INFO->{$clerk_registered}?$CLERKS_INFO->{$clerk_registered}->{login}:'<UNKNOWN>';
push@{$voids_detailed{$login}->{$data->{header}->[23]}},{table_num=>$number,product=>$prod_name,quantity=>$rec->[3],hour_registered=>$hour_registered,clerk_registered=>$clerk_name,hour_voided=>$hour_voided,}}$voids{logins}->{$login}->{$void_type}->{total}+=$line_total;
$voids{logins}->{$login}->{$void_type}->{total_wo_vat}+=$line_total_wo_vat;
$voids{logins}->{$login}->{$void_type}->{total_qttys}+=$rec->[3];
$voids{logins}->{$login}->{$void_type}->{products}->{$prod_name}->{product}=$PRODUCTS_INFO->{$rec->[1]};
$voids{logins}->{$login}->{$void_type}->{products}->{$prod_name}->{total_qttys}+=$rec->[3];
$voids{logins}->{$login}->{$void_type}->{products}->{$prod_name}->{total}+=$line_total;
$voids{logins}->{$login}->{$void_type}->{products}->{$prod_name}->{total_wo_vat}+=$line_total_wo_vat;
$voids{totals}->{$void_type}->{total}+=$line_total;
$voids{totals}->{$void_type}->{total_wo_vat}+=$line_total_wo_vat;
$voids{totals}->{$void_type}->{total_qttys}+=$rec->[3];}}},($dir_areas."/".$area));
$not_closed_checks{areas}->{$area}->{checks}||=[];
if(!scalar@{$not_closed_checks{areas}->{$area}->{checks}}){delete$not_closed_checks{areas}->{$area};}else{$not_closed_checks{areas}->{$area}->{name}=$AREAS_INFO{$area}->[0];}},($dir_areas));}$not_closed_checks{areas}||={};
foreach my $area(keys%{$not_closed_checks{areas}}){$not_closed_checks{$area}=$not_closed_checks{areas}->{$area};}if($cfg->{POINTS_USAGE}){my$recs=$db->select(qq{
      SELECT id, name, nif, points
      FROM customers
      WHERE deleted = 0 AND points > 0
      ORDER BY name
    });
for(@$recs){$_->{points_value}=$_->{points}*$cfg->{POINTS_VALUE_PER_POINT_ON_SALE};
$points_by_customer{customers}->{$_->{name}}=$_;
$points_by_customer{total_points}+=$_->{points};
$points_by_customer{total_points_value}+=$_->{points_value};}}$recs=$db->select(qq{
    SELECT sh.id, sh.business_date, sh.value, sh.fo_doc_number,
      sd.price, sd.quantity, sd.discount, sd.vat,
      sha1.key_value AS manualdoc_serie, sha2.key_value AS manualdoc_total_w_vat,
      sha3.key_value AS manualdoc_total_vat, sha4.key_value AS manualdoc_docnum,
      sha5.key_value AS manualdoc_date, sha6.key_value AS manualdoc_type
    FROM sales_details sd
    LEFT JOIN sales_headers sh ON (sd.header = sh.id)
    LEFT JOIN sales_headers_aux sha1 ON (sh.id = sha1.header AND sha1.key_name = 'manualdoc_serie')
    LEFT JOIN sales_headers_aux sha2 ON (sh.id = sha2.header AND sha2.key_name = 'manualdoc_total_w_vat')
    LEFT JOIN sales_headers_aux sha3 ON (sh.id = sha3.header AND sha3.key_name = 'manualdoc_total_vat')
    LEFT JOIN sales_headers_aux sha4 ON (sh.id = sha4.header AND sha4.key_name = 'manualdoc_docnum')
    LEFT JOIN sales_headers_aux sha5 ON (sh.id = sha5.header AND sha5.key_name = 'manualdoc_date')
    LEFT JOIN sales_headers_aux sha6 ON (sh.id = sha6.header AND sha6.key_name = 'manualdoc_type')
    WHERE sha1.key_value IS NOT NULL AND
      sh.business_date >= ? AND sh.business_date <= ?$sql_clerk_query3
    ORDER BY sh.fo_doc_number
  },@sql_params_two);
foreach my $rec(@$recs){$rec->{date}=$rec->{business_date};
$rec->{price}||=0;
$rec->{discount}||=0;
$rec->{manualdoc_total_w_vat}||=0;
$rec->{manualdoc_total_vat}||=0;
my$discount_w_vat=$rec->{discount}*(1+$rec->{vat}/100);
my$price=$rec->{price}-$discount_w_vat;
my$line_total=sprintf("%.2f",$rec->{quantity}*$price);
my$line_total_wo_vat=($rec->{quantity}*$price)/(1+$rec->{vat}/100);
if(!exists$manual_docs{dates}->{$rec->{business_date}}->{docs}->{$rec->{manualdoc_date}}->{$rec->{fo_doc_number}}){$manual_docs{dates}->{$rec->{business_date}}->{docs}->{$rec->{manualdoc_date}}->{$rec->{fo_doc_number}}=$rec;}$manual_docs{dates}->{$rec->{business_date}}->{docs}->{$rec->{manualdoc_date}}->{$rec->{fo_doc_number}}->{total}+=$line_total;
$manual_docs{dates}->{$rec->{business_date}}->{docs}->{$rec->{manualdoc_date}}->{$rec->{fo_doc_number}}->{total_wo_vat}+=$line_total_wo_vat;
$manual_docs{dates}->{$rec->{business_date}}->{docs}->{$rec->{manualdoc_date}}->{$rec->{fo_doc_number}}->{total_qttys}+=$rec->{quantity};}if(exists$selections->{"62_rfid_stats.txt"}){$recs=$db->select(qq{
      SELECT COUNT(*) AS pending
      FROM rfid_operations
      WHERE synced = 0
    });
my$clerk_id=scalar@sql_params_two==3?pop@sql_params_two:undef;
$rfid_usage{pending_sync}=scalar@$recs?$recs->[0]->{pending}:0;
$recs=$db->select(qq{
      SELECT ro.clerk AS clerk_login, c.id AS clerk_id, COUNT(DISTINCT(ro.card_id)) AS cards_used
      FROM rfid_operations ro
      LEFT JOIN clerks c ON (ro.clerk = c.login)
      WHERE ro.business_date >= ? AND ro.business_date <= ?
      GROUP BY ro.clerk, c.id
    },@sql_params_two);
$rfid_usage{cards_used}=0;
for(@$recs){next if defined$clerk_id&&$_->{clerk_id}!=$clerk_id;
$rfid_usage{cards_used}+=$_->{cards_used};}$recs=$db->select(qq{
      SELECT ro.clerk AS clerk_login, c.id AS clerk_id, ro.operation_type,
        COUNT(DISTINCT card_id) AS nrecs, SUM(ro.amount) AS amount
      FROM rfid_operations ro
      LEFT JOIN clerks c ON (ro.clerk = c.login)
      WHERE ro.business_date >= ? AND ro.business_date <= ? AND
        ro.operation_type IN ('debit', 'credit')
      GROUP BY ro.clerk, c.id, ro.operation_type
    },@sql_params_two);
$rfid_usage{total_credit_amount}=0;
$rfid_usage{total_credit_nrecs}=0;
$rfid_usage{total_debit_amount}=0;
$rfid_usage{total_debit_nrecs}=0;
for(@$recs){next if defined$clerk_id&&$_->{clerk_id}!=$clerk_id;
my$key="total_".$_->{operation_type};
$rfid_usage{$key."_amount"}+=$_->{amount};
$rfid_usage{$key."_nrecs"}+=$_->{nrecs};}}$sales_vat2{vats}||={};
$sales_vat2{received}->{vats}||={};
$sales_vat2{payed}->{vats}||={};
foreach my $vat(keys%{$sales_vat2{vats}}){$sales_vat2{vats}->{$vat}->{total}=$sales_vat2{vats}->{$vat}->{total_wo_vat}*(1+($vat/100));
$sales_vat2{vats}->{$vat}->{total_vat}=$sales_vat2{vats}->{$vat}->{total}-$sales_vat2{vats}->{$vat}->{total_wo_vat};
$sales_vat2{total}+=$sales_vat2{vats}->{$vat}->{total};
$sales_vat2{total_vat}+=$sales_vat2{vats}->{$vat}->{total}-$sales_vat2{vats}->{$vat}->{total_wo_vat};}foreach my $vat(keys%{$sales_vat2{received}->{vats}}){$sales_vat2{received}->{vats}->{$vat}->{total}=$sales_vat2{received}->{vats}->{$vat}->{total_wo_vat}*(1+($vat/100));
$sales_vat2{received}->{vats}->{$vat}->{total_vat}=$sales_vat2{received}->{vats}->{$vat}->{total}-$sales_vat2{received}->{vats}->{$vat}->{total_wo_vat};
$sales_vat2{received}->{total}+=$sales_vat2{received}->{vats}->{$vat}->{total};
$sales_vat2{received}->{total_vat}+=$sales_vat2{received}->{vats}->{$vat}->{total}-$sales_vat2{received}->{vats}->{$vat}->{total_wo_vat};}foreach my $vat(keys%{$sales_vat2{payed}->{vats}}){$sales_vat2{payed}->{vats}->{$vat}->{total}=$sales_vat2{payed}->{vats}->{$vat}->{total_wo_vat}*(1+($vat/100));
$sales_vat2{payed}->{vats}->{$vat}->{total_vat}=$sales_vat2{payed}->{vats}->{$vat}->{total}-$sales_vat2{payed}->{vats}->{$vat}->{total_wo_vat};
$sales_vat2{payed}->{total}+=$sales_vat2{payed}->{vats}->{$vat}->{total};
$sales_vat2{payed}->{total_vat}+=$sales_vat2{payed}->{vats}->{$vat}->{total}-$sales_vat2{payed}->{vats}->{$vat}->{total_wo_vat};}$sales_vat3{vats}||={};
$sales_vat3{received}->{vats}||={};
$sales_vat3{payed}->{vats}||={};
$sales_vat3{received}->{total_wo_vat}=0;
$sales_vat3{received}->{total}=0;
$sales_vat3{received}->{total_vat}=0;
$sales_vat3{payed}->{total_wo_vat}=0;
$sales_vat3{payed}->{total}=0;
$sales_vat3{payed}->{total_vat}=0;
$sales_vat3{total_wo_vat}=0;
$sales_vat3{total}=0;
$sales_vat3{total_vat}=0;
foreach my $vat(keys%{$sales_vat3{vats}}){my$total_wo_vat=$sales_vat3{vats}->{$vat}->{total}/(1+($vat/100));
$sales_vat3{vats}->{$vat}->{total_vat}=$sales_vat3{vats}->{$vat}->{total}-$total_wo_vat;
$sales_vat3{total_wo_vat}+=$total_wo_vat;
$sales_vat3{total_vat}+=$sales_vat3{vats}->{$vat}->{total}-$sales_vat3{vats}->{$vat}->{total_wo_vat};
$sales_vat3{total}+=$sales_vat3{vats}->{$vat}->{total};
$sales_vat3{vats}->{$vat}->{total_wo_vat}=$total_wo_vat;}foreach my $vat(keys%{$sales_vat3{received}->{vats}}){my$total_wo_vat=$sales_vat3{received}->{vats}->{$vat}->{total}/(1+($vat/100));
$sales_vat3{received}->{vats}->{$vat}->{total_wo_vat}=$total_wo_vat;
$sales_vat3{received}->{vats}->{$vat}->{total_vat}=$sales_vat3{received}->{vats}->{$vat}->{total}-$total_wo_vat;
$sales_vat3{received}->{total_wo_vat}+=$total_wo_vat;
$sales_vat3{received}->{total}+=$sales_vat3{received}->{vats}->{$vat}->{total};
$sales_vat3{received}->{total_vat}+=$sales_vat3{received}->{vats}->{$vat}->{total}-$total_wo_vat;}foreach my $vat(keys%{$sales_vat3{payed}->{vats}}){my$total_wo_vat=$sales_vat3{payed}->{vats}->{$vat}->{total}/(1+($vat/100));
$sales_vat3{payed}->{vats}->{$vat}->{total_wo_vat}=$total_wo_vat;
$sales_vat3{payed}->{vats}->{$vat}->{total_vat}=$sales_vat3{payed}->{vats}->{$vat}->{total}-$total_wo_vat;
$sales_vat3{payed}->{total_wo_vat}+=$total_wo_vat;
$sales_vat3{payed}->{total}+=$sales_vat3{received}->{vats}->{$vat}->{total};
$sales_vat3{payed}->{total_vat}+=$sales_vat3{received}->{vats}->{$vat}->{total}-$total_wo_vat;}$sales_by_area{areas}||={};
foreach my $area(keys%{$sales_by_area{areas}}){foreach my $vat(keys%{$sales_by_area{areas}->{$area}->{vats}}){$sales_by_area{areas}->{$area}->{vats}->{$vat}->{total2}=$sales_by_area{areas}->{$area}->{vats}->{$vat}->{total_wo_vat2}*(1+($vat/100));
$sales_by_area{areas}->{$area}->{vats}->{$vat}->{total_vat2}=$sales_by_area{areas}->{$area}->{vats}->{$vat}->{total2}-$sales_by_area{areas}->{$area}->{vats}->{$vat}->{total_wo_vat2};
$sales_by_area{areas}->{$area}->{total2}+=$sales_by_area{areas}->{$area}->{vats}->{$vat}->{total2};
$sales_by_area{areas}->{$area}->{total_vat2}+=$sales_by_area{areas}->{$area}->{vats}->{$vat}->{total_vat2};
$sales_by_area{total2}+=$sales_by_area{areas}->{$area}->{vats}->{$vat}->{total2};
$sales_by_area{total_vat2}+=$sales_by_area{areas}->{$area}->{vats}->{$vat}->{total_vat2};}}if($cfg->{LANG}eq 'SAP'){my%control;
my$sql_clerk="";
if($sql_clerk_query3){$sql_clerk=" AND sh.clerk_mov = ?";}$recs=$db->select(qq{
      SELECT sh.id, c.login, sh.date_mov, sh.hour_mov, sd.tipo_doc, sd.fo_doc_number,
        sd.date_doc, sd.value_doc, sd.obs_doc, sh.value_mov
      FROM sap_movs_details sd
      LEFT JOIN sap_movs_headers sh ON (sd.header = sh.id)
      LEFT JOIN clerks c ON (sh.clerk_mov = c.id)
      WHERE sh.date_mov >= ? AND sh.date_mov <= ?$sql_clerk
      ORDER BY sh.date_mov, sh.hour_mov, sd.date_doc
    },@sql_params_two);
for(@$recs){$sap_by_clerk{total}+=$_->{value_doc};
if(!exists$control{$_->{id}}){$control{$_->{id}}=1;
$sap_by_clerk{nrecs}++;
$sap_by_clerk{clerks}->{$_->{login}}->{nrecs}++;
$sap_by_clerk{dates}->{$_->{date_mov}}->{clerks}->{$_->{login}}->{nrecs}++;}$sap_by_clerk{clerks}->{$_->{login}}->{total}+=$_->{value_doc};
$sap_by_clerk{dates}->{$_->{date_mov}}->{clerks}->{$_->{login}}->{total}+=$_->{value_doc};
if($_->{value_doc}<0){$sap_by_clerk{total_payed}+=$_->{value_doc}*-1;}if($_->{value_doc}>0){$sap_by_clerk{total_received}+=$_->{value_doc};}if(!exists$sap_by_clerk{clerks}->{$_->{login}}->{total_received}){$sap_by_clerk{clerks}->{$_->{login}}->{total_received}=0}if(!exists$sap_by_clerk{clerks}->{$_->{login}}->{total_payed}){$sap_by_clerk{clerks}->{$_->{login}}->{total_payed}=0}if($_->{value_doc}<0){$sap_by_clerk{clerks}->{$_->{login}}->{total_payed}+=$_->{value_doc}*-1;}if($_->{value_doc}>0){$sap_by_clerk{clerks}->{$_->{login}}->{total_received}+=$_->{value_doc};}$sap_by_clerk{transactions}->{$_->{date_mov}}->{$_->{id}}->{headers}={login=>$_->{login},hour_mov=>$_->{hour_mov},value_mov=>$_->{value_mov},};
push@{$sap_by_clerk{transactions}->{$_->{date_mov}}->{$_->{id}}->{details}},{tipo_doc=>$_->{tipo_doc},fo_doc_number=>$_->{fo_doc_number},date_doc=>$_->{date_doc},value_doc=>$_->{value_doc},obs_doc=>$_->{obs_doc},};}}$cfg->{MACHINE_LABEL}||="";
$info{machine_label}=$cfg->{MACHINE_LABEL};
$info{business_date}=&running_business_date($machine);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{station}=$station;
$info{voids}=\%voids;
$info{voids_detailed}=\%voids_detailed;
$info{consumptions}=\%consumptions;
$info{discounts_family}=\%discounts_family;
$info{persons_number}=\%persons_number;
$info{sales_family}=\%sales_family;
$info{sales_vat}=\%sales_vat;
$info{sales_vat2}=\%sales_vat2;
$info{sales_vat3}=\%sales_vat3;
$info{sales_vat_by_date}=\%sales_vat_by_date;
$info{sales_media}=\%sales_by_media;
$info{receipts_by_media}=\%receipts_by_media;
$info{voids_by_reason}=\%voids_by_reason;
$info{sales_by_doctype}=\%sales_by_doctype;
$info{returns_by_product}=\%returns_by_product;
$info{points_by_customer}=\%points_by_customer;
$info{sales_by_product}=\%sales_by_product;
$info{sales_by_menus}=\%sales_by_menus;
$info{sales_by_product_extra_data}=\%sales_by_product_extra_data;
$info{sales_by_product_no_sons}=\%sales_by_product_no_sons;
$info{sales_by_station}=\%sales_by_station;
$info{sales_by_station_product}=\%sales_by_station_product;
$info{sales_by_clerk_product}=\%sales_by_clerk_product;
$info{sales_by_clerk_area_product}=\%sales_by_clerk_area_product;
$info{sales_by_clerk}=\%sales_by_clerk;
$info{bills_by_clerk}=\%bills_by_clerk;
$info{sales_by_serie}=\%sales_by_serie;
$info{sales_by_customer}=\%sales_by_customer;
$info{sales_by_customer_product}=\%sales_by_customer_product;
$info{sales_by_customer_vat}=\%sales_by_customer_vat;
$info{sales_by_pricelevel}=\%sales_by_pricelevel;
$info{sales_by_cardset}=\%sales_by_cardset;
$info{sales_by_cashier}=\%sales_by_cashier;
$info{sales_by_hour}=\%sales_by_hour;
$info{sales_by_parent}=\%sales_by_parent;
$info{sales_by_area}=\%sales_by_area;
$info{sales_by_cost}=\%sales_by_cost;
$info{stock_by_product}=\%stock_by_product;
$info{sales_by_list}=\%sales_by_list;
$info{service_charges_removes}=\%service_charges_removes;
$info{total_tips}=$total_tips;
$info{total_service_charges}=$total_service_charges;
$info{counters_by_doctype}=\%counters_by_doctype;
$info{not_closed_checks}=\%not_closed_checks;
$info{clocks_by_clerk}=\%clocks_by_clerk;
$info{transfers}=\%transfers;
$info{date_from}=$business_date;
$info{date_to}=$date_to;
$info{docs_canceled}=\%docs_canceled;
$info{drawer_openings}=\%drawer_openings;
$info{kept_in_source}=\%kept_in_source;
$info{sales_detailed}=\%sales_detailed;
$info{manual_docs}=\%manual_docs;
$info{products_to_deliver}=\%products_to_deliver;
$info{scale_tickets}=\%scale_tickets;
$info{training_sales}=\%training_sales;
$info{clerks_comissions}={};
$info{money_counts}=\%money_counts;
$info{finantial_info}=\%finantial_info;
$info{refunds}=\%refunds;
$info{settlements_by_customer}=\%settlements_by_customer;
$info{sales_for_comissions}=\%sales_for_comissions;
$info{rfid_usage}=\%rfid_usage;
$info{sap_by_clerk}=\%sap_by_clerk;
if($sql_params_two[0]ne$sql_params_two[1]){$info{finantial_info}->{dont_show}=1;}foreach my $clerk(keys%$CLERKS_INFO){$info{clerks_comissions}->{$CLERKS_INFO->{$clerk}->{login}}=$CLERKS_INFO->{$clerk}->{commission_group};}$db->do("SET enable_nestloop TO on");
return\%info;}sub print_report_detailed{print_report(@_);}sub print_report_resumed{print_report(@_);}sub print_report_clerk{print_report(@_);}sub print_report_closeday{print_report(@_);}sub print_report{my($machine,$file)=@_;
if($machine->is_station()){return 2;}my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
my$SAFT_CompanyId=$cfg->{SAFT_CompanyId}//"";
my$SAFT_TaxRegistratioNumber=$cfg->{SAFT_TaxRegistratioNumber}//"";
my$SAFT_CompanyName=$cfg->{SAFT_CompanyName}//"";
my$SAFT_BusinessName=$cfg->{SAFT_BusinessName}//"";
my$SAFT_AddressDetail=$cfg->{SAFT_AddressDetail}//"";
my$SAFT_City=$cfg->{SAFT_City}//"";
my$SAFT_PostalCode=$cfg->{SAFT_PostalCode}//"";
my$INTERNET_SEND_MONTHLY_Z=$cfg->{INTERNET_SEND_MONTHLY_Z}//"";
my$INTERNET_SEND_DAILY_Z=$cfg->{INTERNET_SEND_DAILY_Z}//"";
my$INTERNET_REPORT_FORMAT=$cfg->{INTERNET_REPORT_FORMAT}//"pdf";
my$INTERNET_SEND_CLOSE_DAY_EMAIL=$cfg->{INTERNET_SEND_CLOSE_DAY_EMAIL}//"";
my$PRINT_DAY_CLOSE_REPORT=$cfg->{PRINT_DAY_CLOSE_REPORT}||"";
$cfg->{STATION_NAME}||="";
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my$business_date=&running_business_date($machine);
my($type,$date,$date_to,$station,$clerk,$to_pdf,$to_screen,$to_xls,$custom,$send_mail,$email_addr)=$file=~/\/report#(.*?)#(.*?)#(.*?)#(\d+)#(\d+)#(\d+)#(\d+)#(\d+)#(.*?)#(\d+)#(.*?)\.txt$/;
$email_addr//="";
$send_mail//=0;
my$doc_serie="";
if($send_mail){$doc_serie=$machine->decode_doctype_serie();
my%valid_formats=(pdf=>1,xls=>1);
if(!exists$valid_formats{lc($INTERNET_REPORT_FORMAT)}){print STDERR"Invalid email format - $INTERNET_REPORT_FORMAT - returning...\n";
return;}}my$date_from=$date;
my$tmp1=$date;
my$tmp2=$date_to;
$tmp1=~s/\-//g;
$tmp2=~s/\-//g;
$date=$date_to if$tmp2<$tmp1;
my$clerk_filter=$type eq 'user'?1:undef;
my@custom_tags=();
my$sales_details=0;
my%selections=();
if($custom eq 'custom'||$custom eq 'json'){push@custom_tags,'01_header.txt';
if(open(F,"< $file")){my$has_footer=0;
while(<F>){chop;
next unless$_;
$has_footer=1 if/_footer/;
push@custom_tags,$_;
$selections{$_}=1;
$sales_details=1 if$_ eq '41_sales_detailed.txt';}if(scalar(@custom_tags)>0&&$has_footer==0){push@custom_tags,'99_footer.txt';}close(F);}}$sales_details=1;
my$selections_file="";
if(!scalar%selections){if($type eq 'detailed'){$selections_file="/opt/pos/common/reports/02_daily_detailed/selections.txt";}elsif($type eq 'resumed'){$selections_file="/opt/pos/common/reports/01_daily_simple/selections.txt";}elsif($type eq 'user'){$selections_file="/opt/pos/common/reports/03_daily_clerk/selections.txt";}}if(-e$selections_file){my@lines=read_file($selections_file);
for(@lines){chop;
$selections{$_}=1;}}my$content="";
my$tmpl="";
my$rep_dir="";
if($type eq 'closeday'){$tmpl="report_dayclose.tmpl";}elsif($type eq 'resumed'){$tmpl="report_daily_simple.tmpl";
$rep_dir="01_daily_simple";}elsif($type eq 'detailed'){$tmpl="report_daily_detailed.tmpl";
$rep_dir="02_daily_detailed";}elsif($type eq 'user'){$tmpl="report_daily_clerk.tmpl";
$rep_dir="03_daily_clerk";}my$info=_data_for_report($machine,$clerk,$station,$date,$date_to,$clerk_filter,$sales_details,\%selections,$type);
$info->{report_type}=$type;
unlink($file);
$info->{station_name}=$cfg->{STATION_NAME};
if($custom eq 'json'){open(F,"> /tmp/report.json");
print F encode_json($info);
close(F);
return;}if(scalar@custom_tags&&$rep_dir){my$report_dir=$machine->dir_server()."/common/reports/$rep_dir";
my$report_cnt="";
foreach my $tag(@custom_tags){my$file="$report_dir/$tag";
$file="$report_dir/$tag.custom" if-e"$report_dir/$tag.custom";
if(open(F,"< $file")){my$c=0;
while(<F>){next if$c++ ==0;
$report_cnt.=$_;}close(F);}}my$report=$machine->dir_server().'/common/print_templates/report_custom.tmpl';
$tmpl="report_custom.tmpl";
if(open(F,"> $report")){print F$report_cnt;
close(F);}}else{if(-e$machine->dir_server().'/common/print_templates/'.$tmpl.".custom"){$tmpl.=".custom";}}my$pt_certified_software=-e$machine->dir()."/common/skins/".$cfg->{LANG}."/sign_server"?1:0;
my$report_counter=App::POS::Counters->new(db=>$db,name=>'report_counter',);
if($cfg->{LANG}eq 'SAP'){if($type eq 'detailed'||$type eq 'user'){if($cfg->{EXTERNAL_INVOICE_TOPRINTER}){$tmpl='sap_detailed_html.tmpl';}else{$tmpl='sap_detailed.tmpl';}}elsif($type eq 'resumed'){if($cfg->{EXTERNAL_INVOICE_TOPRINTER}){$tmpl='sap_resumed_html.tmpl';}else{$tmpl='sap_resumed.tmpl';}}}$info->{counter}=$report_counter->increment($tmpl);
$info->{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($tmpl,$info,\$content,)||die$TEMPLATE->error;
if($cfg->{LANG}eq 'SAP'){$cfg->{EXTERNAL_INVOICE_TOPRINTER}||=0;
if($cfg->{EXTERNAL_INVOICE_TOPRINTER}){my$pdf_file="/opt/pos/common/data/report_".$type."_sap.pdf";
my$rrr=int(rand(10000));
my$fff="/tmp/print_$rrr.html";
my$server_cfg=$machine->is_server()?$cfg:$machine->server_config->load();
_replace_saft_tags($server_cfg,\$content);
open(F,"> $fff");
print F"$content";
close(F);
_pdf_gen($cfg,$fff,$pdf_file);
if($send_mail&&$email_addr){my$LANG=$cfg->{LANG}||"";
my$SAFT_TaxRegistratioNumber=$cfg->{SAFT_TaxRegistratioNumber}||"";
my$SAFT_BusinessName=$cfg->{SAFT_BusinessName}||"";
my$subject_tmp="";
if($type eq 'resumed'){$subject_tmp='Totais de Caixa';}else{$subject_tmp='Detalhe de Caixa';}my$stat=send_mail(to=>$email_addr,subject=>"[$LANG] $subject_tmp / $date_from / $date_to",content=>"$LANG<br>====<p>$SAFT_BusinessName<p>$SAFT_TaxRegistratioNumber<br>",attachment=>[$pdf_file],);}else{system("lpr \"$pdf_file\"");}return;}else{return unless$REAL_PRINT;
if($to_screen||$to_pdf||$to_xls){return;}my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}return;}}if($pt_certified_software){if(-e"/opt/pos/.nif"){open(F,"< /opt/pos/.nif");
my$nif=<F>;
chop($nif);
close(F);
if($nif){my@lines=split/\n/,$content;
$lines[2].="\nContribuinte n. $nif";
$content=join("\n",@lines);}}}if($to_xls){my$content_tmp=$content;
$content_tmp=decode("utf8",$content_tmp);
$content_tmp=~s/\@size_double\@//g;
$content_tmp=~s/\@size_normal\@//g;
$content_tmp=~s/\@size_small\@//g;
$content_tmp=~s/\@paper_cut\@//g;
$content_tmp=~s/\@justify_.*?\@//g;
$content_tmp=~s/\@logo.*?\@//g;
my$server_cfg=$machine->is_server()?$cfg:$machine->server_config->load();
_replace_saft_tags($server_cfg,\$content_tmp);
my$doc_serie_tmp=$doc_serie//"";
if($doc_serie_tmp eq ''){$doc_serie_tmp=`cat /opt/pos/etc/config.ini | grep DOC_TYPE_SERIE | cut -f2 -d= | sed 's/\@YEAR\@//'`;
chop($doc_serie_tmp);
$doc_serie_tmp//="";}my$xls_file="/opt/pos/common/data/report_".$type."_".$date_from."_".$date_to."_".$SAFT_TaxRegistratioNumber."_".$doc_serie_tmp.".xls";
&_to_xls($content_tmp,$business_date,$date_to,$xls_file);
if(-e$xls_file&&$send_mail&&$email_addr){my$LANG=$cfg->{LANG}||"";
my$SAFT_TaxRegistratioNumber=$cfg->{SAFT_TaxRegistratioNumber}||"";
my$SAFT_BusinessName=$cfg->{SAFT_BusinessName}||"";
my$subject_tmp="";
if($type eq 'resumed'){$subject_tmp=translate($machine,"DIARIO RESUMIDO - X");}elsif($type eq 'closeday'){$subject_tmp=translate($machine,"FECHO DE DIA");}else{$subject_tmp=translate($machine,"DIARIO DETALHADO - Z");}my$stat=send_mail(to=>$email_addr,subject=>"[$LANG] $subject_tmp / $doc_serie / $date_from / $date_to",content=>"$LANG<br>====<p>$SAFT_BusinessName<p>$SAFT_TaxRegistratioNumber<br>",attachment=>[$xls_file],);}elsif(-e$xls_file&&$send_mail&&$INTERNET_REPORT_FORMAT eq 'xls'){my$dstr=&strftime("%Y",localtime)."/".&strftime("%m",localtime);
make_path("/opt/pos/common/data/emails_to_send/$dstr")unless-d"/opt/pos/common/data/emails_to_send/$dstr";
&move($xls_file,"/opt/pos/common/data/emails_to_send/$dstr");}}if($to_screen){my$dir_tmp=$machine->server_dir_tmp();
my$cnt_tmp=$content;
$cnt_tmp=~s/\@size_.*?\@//g;
$cnt_tmp=~s/\@paper_cut\@//g;
$cnt_tmp=~s/\@justify_.*?\@//g;
$cnt_tmp=~s/\@logo\d\@//g;
my$server_cfg=$machine->is_server()?$cfg:$machine->server_config->load();
_replace_saft_tags($server_cfg,\$cnt_tmp);
open(F,">> $dir_tmp/printer_screen#$station.txt");
print F$cnt_tmp;
print F"=======================================\n\n\n";
close(F);}if($to_pdf){my$d1=$date_from;
my$d2=$date_to;
$d1=~s/\-//g;
$d2=~s/\-//g;
my$doc_serie_tmp=$doc_serie//"";
if($doc_serie_tmp eq ''){$doc_serie_tmp=`cat /opt/pos/etc/config.ini | grep DOC_TYPE_SERIE | cut -f2 -d= | sed 's/\@YEAR\@//'`;
chop($doc_serie_tmp);
$doc_serie_tmp//="";}my$pdf_file="/opt/pos/common/data/report_".$type."_".$d1."_".$d2."_".$SAFT_TaxRegistratioNumber."_".$doc_serie_tmp.".pdf";
my$rrr=int(rand(10000));
my$fff="/tmp/print_$rrr.html";
my$content_tmp=$content;
$content_tmp=encode_entities(decode("utf8",$content_tmp));
$content_tmp=~s/\@size_double\@//g;
$content_tmp=~s/\@size_normal\@//g;
$content_tmp=~s/\@size_small\@//g;
$content_tmp=~s/\@paper_cut\@//g;
$content_tmp=~s/\@justify_.*?\@//g;
$content_tmp=~s/\@logo.*?\@//g;
my$server_cfg=$machine->is_server()?$cfg:$machine->server_config->load();
_replace_saft_tags($server_cfg,\$content_tmp);
open(F,"> $fff");
print F"<html><body><pre>$content_tmp</pre></body></html>";
close(F);
_pdf_gen($cfg,$fff,$pdf_file);
if(-e$pdf_file&&$send_mail&&$email_addr){my$LANG=$cfg->{LANG}||"";
my$SAFT_TaxRegistratioNumber=$cfg->{SAFT_TaxRegistratioNumber}||"";
my$SAFT_BusinessName=$cfg->{SAFT_BusinessName}||"";
my$subject_tmp="";
if($type eq 'resumed'){$subject_tmp=translate($machine,"DIARIO RESUMIDO - X");}elsif($type eq 'closeday'){$subject_tmp=translate($machine,"FECHO DE DIA");}else{$subject_tmp=translate($machine,"DIARIO DETALHADO - Z");}my$stat=send_mail(to=>$email_addr,subject=>"[$LANG] $subject_tmp / $doc_serie / $date_from / $date_to",content=>"$LANG<br>====<p>$SAFT_BusinessName<p>$SAFT_TaxRegistratioNumber<br>",attachment=>[$pdf_file],);}elsif(-e$pdf_file&&$send_mail&&$INTERNET_REPORT_FORMAT eq 'pdf'){my$dstr=&strftime("%Y",localtime)."/".&strftime("%m",localtime);
make_path("/opt/pos/common/data/emails_to_send/$dstr")unless-d"/opt/pos/common/data/emails_to_send/$dstr";
&move($pdf_file,"/opt/pos/common/data/emails_to_send/$dstr");}}return unless$REAL_PRINT;
if($to_pdf&&$type eq 'closeday'&&$cfg->{PRINT_DAY_CLOSE_REPORT}){}else{if($to_screen||$to_pdf||$to_xls){return;}}my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}print STDERR"done...\n";}sub _to_xls{my($cnt,$date_from,$date_to,$file,$field_separator)=@_;
my$start_title=0;
my$start_content=0;
print STDERR"##> _to_xls - $file\n";
$cnt=~s/\@.*?\@//g;
require Spreadsheet::WriteExcel;
my$workbook=Spreadsheet::WriteExcel->new($file)or die"error: $!";
$workbook->set_properties(utf8=>1);
my$text=$workbook->add_format();
my$text_b=$workbook->add_format();$text_b->set_bold();
my$numeric=$workbook->add_format();$numeric->set_num_format('0.000');
my$numeric_s=$workbook->add_format();$numeric_s->set_num_format();
my$worksheet=$workbook->add_worksheet("$date_from - $date_to");
my@lines=split/\n/,$cnt;
my$c=0;
foreach my $line(@lines){my$y=0;
my$arr=&_str2xls($line,$field_separator);
if(!scalar@$arr){$worksheet->write_string($c++,$y,"");}else{foreach my $v(@$arr){if(is_number($v)){$worksheet->write_string($c,$y++,$v,$numeric);}else{$worksheet->write_string($c,$y++,$v);}}$c++;}}}sub _str2xls{my$str=shift;
my$field_separator=shift;
$str=~s/\n//g;
return[]unless$str;
my@final_words=();
$str=trim_string($str);
$str=~s/\(//g;
$str=~s/\)//g;
$str=~s/^\-//g;
$str=~s/^\=//g;
if(defined$field_separator){my@words=split/$field_separator/,$str;
return\@words;}if(has_numbers($str)){if($str=~/^\s+\d/){$str=~s/^\s+//;}else{$str=~s/^\s+/---/;}$str=~s/\s+/ /g;
my@words=split/ /,$str;
my$tmp_str="";
foreach my $word(@words){if(is_number($word)||is_hour($word)||is_date($word)){if($tmp_str){$tmp_str=~s/\s+$//;
push@final_words,$tmp_str;
$tmp_str="";}push@final_words,$word;}else{$tmp_str.=$word." ";}}if($tmp_str){push@final_words,$tmp_str;}}else{push@final_words,$str;}return\@final_words;}sub print_moneycount{my($machine,$file)=@_;
my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
my$config=$machine->config()->load();
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}if(!defined$CLOSE_HOUR){my$close_hour=$cfg->{END_BUSINESS_HOUR}||"05:00";
$CLOSE_HOUR=$close_hour;
my@tmp=split/:/,$close_hour;
$CLOSE_HOUR.=":00" if scalar@tmp==2;}$cfg->{STATION_NAME}||="";
&get_clerks_information($db);
my($clerk,$station,$is_manual,$data)=$file=~/\/moneycount#(\d+)#(\d+)#(\d+)#(.*?)#/;
print STDERR"### print_moneycount -> $file -> $clerk -> $station -> $is_manual -> $data\n";
my$date=&strftime("%Y-%m-%d",localtime);
my$hour=&strftime("%H:%M:%S",localtime);
my$business_date=&date_based_on_closing_hour("$date $hour",$CLOSE_HOUR);
my$ccards=$cfg->{CREDIT_CARDS};
my@ccards_tmp=split/,/,$ccards;
my%ccards=map{uc(translate($machine,$_))=>1}@ccards_tmp;
my$pmethods=$cfg->{PAYMENT_METHODS}||"";
my@pmethods=split/,/,$pmethods;
my%pmethods=map{uc(translate($machine,$_))=>1}@pmethods;
$pmethods{INS}=1;
$pmethods{OUTS}=1;
$pmethods{VOUCHER}=1;
foreach my $ccard(keys%ccards){$pmethods{$ccard}=$ccard;}my$clerk_login=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
if(open(F,"< $file")){my%info=(miscs=>[]);
my$content="";
while(<F>){chop;
my@toks=split/#/;
next unless scalar@toks;
my$meth=uc(translate($machine,$toks[0]));
if(exists$pmethods{$meth}){if($meth eq 'INS'||$meth eq 'OUTS'){if($toks[3]=~/;/){my@toks2=split/;/,$toks[3];
if(scalar@toks2==2&&$toks2[0]*1>0){$db->do("INSERT INTO money_counts_operations (
                  business_date, clerk, station, operation, justification, amount
                ) VALUES (?, ?, ?, ?, ?, ?)",$business_date,$clerk_login,$station,$meth,$toks2[1],$toks2[0],);}}else{if($toks[-1]*1>0){$db->do("INSERT INTO money_counts_operations (
                  business_date, clerk, station, operation, justification, amount
                ) VALUES (?, ?, ?, ?, ?, ?)",$business_date,$clerk_login,$station,$meth,'--------',$toks[-1],);}}}if($toks[3]=~/;/){my@toks2=split/;/,$toks[3];
if($toks2[0]*1>0){$info{total_counted}+=$toks2[0]unless$toks[0]eq 'OUTs'||$toks[0]eq 'INs';
$info{totals_pmethod}->{$toks[0]}+=$toks2[0];
push@{$info{pmethods}->{$toks[0]}},{name=>$toks2[1],value=>$toks2[0],custom=>1};}}else{if($toks[3]*1>0){$info{total_counted}+=$toks[3]unless$toks[0]eq 'OUTs'||$toks[0]eq 'INs';
$info{totals_pmethod}->{$toks[0]}+=$toks[3];
my$str=$toks[2]ne '-9999'?$toks[1]." x ".$toks[2]:'==>';
push@{$info{pmethods}->{$toks[0]}},{name=>$str,value=>$toks[3],custom=>0};}}}else{if($toks[0]eq 'CLERK'){my@toks2=split/\-/,$toks[3];
$info{clerk_code}=$toks2[0];
$info{clerk_name}=$CLERKS_INFO->{$toks2[0]}->{login};}elsif($toks[0]eq 'NEXT_DAY'){$info{next_day}=$toks[3];}elsif($toks[0]eq 'POS_SALES'){$info{pos_sales}=$toks[3];}elsif($toks[0]eq 'PREVIOUS_DAY'){$info{previous_day}=$toks[3];}elsif($toks[0]eq 'TOTAL_COUNTED'){$info{total_money}=$toks[3];}elsif($toks[0]eq 'DEPOSIT'){$info{deposit}=$toks[3];}elsif($toks[0]eq 'DAILY_BALANCE'){$info{daily_balance}=$toks[3];}}}my$money_counts=App::POS::Counters->new(db=>$db,name=>'money_counts',);
my$money_counts_last=App::POS::Counters->new(db=>$db,name=>'money_counts_last',);
my$business_date=$data;
my$date_str='NONE';
$info{transit}||=0;
$info{sales_global}||=0;
$info{sales_clerk}||=0;
$info{returns_global}||=0;
$info{returns_clerk}||=0;
$info{is_manual}||=$is_manual;
if(defined$business_date&&$business_date){$date_str=$business_date;}if($info{is_manual}){$date_str='NONE';
$money_counts->increment($date_str);}else{if($money_counts->exists($date_str)){return;}else{$money_counts->increment($date_str);}}$info{clerk_code}=$clerk;
$info{clerk_name}=$CLERKS_INFO->{$clerk}->{login}||'UNKNOWN';
$info{count_number}=$money_counts->cur_value($date_str);
$info{station}=$station;
$info{station_name}=$cfg->{STATION_NAME};
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$config->{DETAILED_REPORT_AFTER_MONEYCOUNT}||=0;
if($config->{DETAILED_REPORT_AFTER_MONEYCOUNT}){my$max_id="";
if($money_counts_last->exists($business_date)){$max_id=$money_counts_last->cur_value($business_date);}my$dir_tmp=$machine->dir_tmp();
open(F,"> $dir_tmp/report#detailed#$business_date#$station#$clerk#$max_id.txt");
print F"1\n";
close(F);}my$res=$db->select("SELECT max(id) AS max_id FROM sales_headers WHERE business_date = ?",$business_date);
if(scalar@$res&&defined$res->[0]->{max_id}){$money_counts_last->set_value($business_date,$res->[0]->{max_id});}$info{totals_pmethod}->{OUTs}||=0;
$info{total_counted}+=$info{totals_pmethod}->{OUTs};
$info{diff_sales_global}=(($info{total_counted}-$info{transit})-$info{sales_global})+$info{returns_global};
$info{diff_sales_clerk}=($info{total_counted}-$info{sales_clerk})+$info{returns_clerk};
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process('money_counter.tmpl',\%info,\$content,)||die$TEMPLATE->error;
$db->do("INSERT INTO money_counts (business_date, clerk, station, txt) VALUES (?, ?, ?, ?)",$business_date,$clerk_login,$station,$content,);
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(N,"> $print_file");
print N$content;
close(N);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}close(F);}}sub integrate_clerk_in_out{my($machine,$file)=@_;
return 2 unless$machine->is_server();
my($op,$station,$clerk,$desc)=$file=~/clerk_in_out#(.*?)#(\d+)#(\d+)#(.*?)#/;
print STDERR"> clerk_in_out - $file, $op, $station, $clerk, $desc\n";
my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
&get_clerks_information($db);
my$clerk_name=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'UNKNOWN';
my$date=&strftime("%Y-%m-%d",localtime);
my$hour=&strftime("%H:%M:%S",localtime);
my$business_date=&date_based_on_closing_hour("$date $hour",$CLOSE_HOUR);
my$id=$db->next_id("time_clock");
$db->do(qq{
    INSERT INTO time_clock VALUES (?, ?, ?, ?, ?, ?, ?, ?)
  },$id,$business_date,$hour,$station,$clerk,$clerk_name,$op,$desc);
my%info=();
my$content="";
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{justification}=$desc;
$info{operation}=$op;
my$template="clerk_in_out.tmpl";
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template,\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub print_notclosed_check{my($machine,$file)=@_;
return 2 unless$machine->is_server();
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}my($station,$area,$is_internal,$dont_print,$check,$from_mobile,$station_print_checks)=$file=~/\/print_notclosed_check#(\d+)#(\d+)#(\d)#(\d)#(.*?)#(\d+)#(.*?)\.txt$/;
$from_mobile=1 if$station>80;
my$cfg=$machine->config()->load_fixed();
my$db=$machine->database();
my$station_original=$station;
if($station>80){$station=0;}my$pt_certified_software=-e$machine->dir()."/common/skins/".$cfg->{LANG}."/sign_server"?1:0;
&get_products_information($db);
&get_clerks_information($db);
$cfg->{CHECK_GROUP_PRODUCTS_TEXT}||="";
$cfg->{SHOW_ZERO_PRICE_IN_BILL}||=0;
$cfg->{PRINT_EXTRAS_IN_DOCUMENTS}||=0;
$cfg->{MENUS_OPTIMIZED_FOR_GROUPS}||=0;
$cfg->{PRINT_GROUP_WEIGHT_QUANTITIES}||=0;
$cfg->{PRINT_CHECK_PRODUCTS_MODE}||=1;
my$check_file=$machine->dir_areas()."/$area/$check".".toprint";
return unless-f$check_file;
my$o=App::POS::Check->new(file=>$check_file);
my$data=$o->data();
my$ex=$o->extra_data();
my$products_extra_data=$o->products_extra_data();
$ex->{print_language}||="";
$ex->{send_to_network_printer}||="";
my$last_menu;
unlink$check_file;
if($ex->{print_language}){&get_products_translations($db);}my$h=$data->{header}||[];
my$details=$data->{details}||[];
my$details_deleted=$data->{details_deleted}||[];
my$footers=$data->{footers}||[];
my$info={};
if(!scalar@$h){print STDERR"CHECK FILE - $check_file\n";
print STDERR"print_notclosed_check> Check without headers, returning...\n";
return;}$cfg->{SHOW_ZERO_PRICE_IN_INVOICE}=1 if$h->[8]==10;
my$discount_percent=$h->[10];
my$clerk_open_name=exists$CLERKS_INFO->{$h->[2]}?$CLERKS_INFO->{$h->[2]}->{login}:'UNKNOWN';
my$clerk_close_name=exists$CLERKS_INFO->{$h->[3]}?$CLERKS_INFO->{$h->[3]}->{login}:'UNKNOWN';
my$clerk_closed=$h->[3];
if(!defined$CLOSE_HOUR){my$close_hour=$cfg->{END_BUSINESS_HOUR}||"05:00";
$CLOSE_HOUR=$close_hour;
my@tmp=split/:/,$close_hour;
$CLOSE_HOUR.=":00" if scalar@tmp==2;}my$request_numbers=App::POS::Counters->new(db=>$db,name=>'request_numbers',);
my$doc_serie=$machine->decode_doctype_serie();
my$doc_type_str=$cfg->{DOC_TYPE_TABLE_PRINT};
my$doc_type=6;
my$vias=exists$RECEIPT_PRINT_VIAS{$doc_type}?$RECEIPT_PRINT_VIAS{$doc_type}:1;
my$cur_date=&strftime("%Y-%m-%d",localtime);
my$cur_hour=&strftime("%H:%M:%S",localtime);
my$memc=memcache($machine->server_ip(),'pos_files',\%MEMC_CONNECTIONS);
my$business_date=&date_based_on_closing_hour($cur_date." ".$cur_hour,$CLOSE_HOUR);
my$bdate=$business_date;
$bdate=~s/\-//g;
my$external_doc="";
my$fo_doc_number=$doc_type_str;
my$doc_counter_key=$doc_type_str.'#'.$external_doc.'#'.$doc_serie;
$info->{headers}={meals_txt=>&translate($machine,$cfg->{CHECK_GROUP_PRODUCTS_TEXT}),business_date=>$business_date,cur_date=>$cur_date,cur_hour=>$cur_hour,date=>$h->[0],date_closed=>$h->[1],hour=>$cur_hour,value=>$h->[19],discount=>$h->[9],clerk_open=>$h->[2],clerk_close=>$h->[3],station_open=>$h->[4],station_close=>$h->[5],area=>$h->[7],area_name=>exists$AREAS_INFO{$h->[7]}?$AREAS_INFO{$h->[7]}->[0]:'<UNKNOWN>',area_tables=>exists$AREAS_INFO{$h->[7]}?$AREAS_INFO{$h->[7]}->[1]:0,meals=>$h->[14],clerk_open_name=>$clerk_open_name,clerk_close_name=>$clerk_close_name,customer=>$h->[12],customer_nif=>$h->[13],service_charge=>$h->[17]||0,};
$info->{headers}->{station_close_name}=exists$STATION_NAMES->{$info->{headers}->{station_close}}?$STATION_NAMES->{$info->{headers}->{station_close}}:$info->{headers}->{station_close};
$info->{headers}->{station_open_name}=exists$STATION_NAMES->{$info->{headers}->{station_open}}?$STATION_NAMES->{$info->{headers}->{station_open}}:$info->{headers}->{station_open};
if($info->{headers}->{area}eq$SPLIT_AREA_IDX){if(exists$ex->{original_table_number}&&$ex->{original_table_number}){$info->{headers}->{table_number}=$ex->{original_table_number};}if(exists$ex->{original_area}&&$ex->{original_area}){$info->{headers}->{area}=$ex->{original_area};
$info->{headers}->{area_name}=exists$AREAS_INFO{$info->{headers}->{area}}?$AREAS_INFO{$info->{headers}->{area}}->[0]:'<UNKNOWN>';
$info->{headers}->{area_tables}=exists$AREAS_INFO{$info->{headers}->{area}}?$AREAS_INFO{$info->{headers}->{area}}->[1]:0;}}if($info->{headers}->{customer_nif}){my$cust=$db->select(qq{
      SELECT * FROM customers WHERE nif = ? ORDER BY deleted ASC LIMIT 1
    },$info->{headers}->{customer_nif});
if(scalar@$cust){$info->{headers}->{customer_id}=$cust->[0]->{id};
$info->{headers}->{customer_credit}=$cust->[0]->{total_credit};
$info->{headers}->{customer_address}=my_conv($cust->[0]->{address});
$info->{headers}->{customer_postal_code}=my_conv($cust->[0]->{postal_code});
$info->{headers}->{customer_city}=my_conv($cust->[0]->{city});
$info->{headers}->{customer_contact}=my_conv($cust->[0]->{contact});}else{my$id=$db->next_id("customers");
if($id<1000000){$id+=1000000;}$db->do("INSERT INTO customers (id, name, nif) VALUES (?, ?, ?)",$id,$info->{headers}->{customer},$info->{headers}->{customer_nif},);
$info->{headers}->{customer_id}=$id;}}if(!scalar@$details){print STDERR"CHECK WITH NO DETAILS, ABORTING...\n";
return;}$cfg->{MENUS_VALUE_BY_COMPONENT}||=0;
$cfg->{CUSTOMER_VAT_ISENTION}||="";
my$menu_original_prices={};
if($cfg->{MENUS_VALUE_BY_COMPONENT}){my%menus_info=();
my%menus_products=();
my%new_prices=();
my$last_product={};
my$last_product_id="";
my$last_product_gbi="";
my$components_will_start=0;
my$had_menus=0;
foreach my $d(@$details){next unless scalar@$d;
next unless defined$d->[3];
if($d->[13]eq 'MENU'){$had_menus=1;
if($components_will_start){$menus_products{$last_product_gbi}=$last_product;
$menu_original_prices->{$last_product->[-1]}=$last_product->[4]*$last_product->[3];}$menu_original_prices->{$d->[-1]}=$d->[4]*$d->[3];
push@{$menus_info{$last_product_gbi}},$d;
$components_will_start=0;}else{$last_product=$d;
$last_product_id=$d->[1];
$last_product_gbi=$d->[-1];
$components_will_start=1;}}if($had_menus){my$area_price=$AREAS_INFO{$info->{headers}->{area}}->[2]||"";
if($area_price){$area_price=~s/sell_price//;}foreach my $menu(keys%menus_products){my$h=$menus_products{$menu};
my$menu_price=$h->[4];
$new_prices{$h->[-1]}=0;
my$total_price=0;
foreach my $component(@{$menus_info{$menu}}){my$p=$PRODUCTS_INFO->{$component->[1]}->{"sell_price".$area_price}||0;
$total_price+=$p;}foreach my $component(@{$menus_info{$menu}}){if($total_price>0){my$p=$PRODUCTS_INFO->{$component->[1]}->{"sell_price".$area_price}||0;
my$ratio=$p/$total_price;
my$price=$menu_price*$ratio;
$new_prices{$component->[-1]}=$price;}}}}for(@$details){next unless scalar@$_;
next unless defined$_->[3];
next unless exists$new_prices{$_->[-1]};
my$new_price=$new_prices{$_->[-1]};
if($_->[13]eq 'MENU'){if($_->[4]*1>0){$_->[4]=$_->[4]+$new_price;}else{$_->[4]=$new_price;}}else{$_->[4]=$new_price;}$_->[4]=sprintf("%.2f",$_->[4]);}}my$check_has_menus=0;
my%t=();
foreach my $d(@{$details}){next unless defined$d->[1];
$check_has_menus=1 if exists$PRODUCTS_MENUS->{$d->[1]};
$d->[5]||=0;
my$txt=exists$products_extra_data->{$d->[-1]}?$products_extra_data->{$d->[-1]}:"";
my$key=$d->[6]."#".$d->[1]."#".($d->[4]*1)."#".$d->[5]."#".$txt;
if($PRODUCTS_INFO->{$d->[1]}->{is_weight}&&!$cfg->{PRINT_GROUP_WEIGHT_QUANTITIES}){$key=$d->[0];}my$is_menu=exists$PRODUCTS_MENUS->{$d->[1]}&&$d->[13]ne 'MENU'?1:0;
if(!$cfg->{SHOW_ZERO_PRICE_IN_BILL}&&!$is_menu){next unless$d->[4]*1!=0;}if($is_menu||$d->[13]eq 'MENU'){$key=$d->[0];}if(exists$t{$key}){$t{$key}->[3]+=$d->[3];}else{$t{$key}=$d;}}my@t=values%t;
$details=\@t;
my$mult=1;
my$total_quantities=0;
my$global_discount_percent=0;
my$global_discount=0;
my$total_discounts=0;
my$total_offers=0;
my%vat=();
if($h->[10]){if($h->[10]=~/\%$/){$global_discount_percent=1;
($global_discount)=$h->[10]=~/^(\d+)\%$/;}else{$global_discount=$h->[10];}foreach my $d(@$details){next unless scalar@$d;
next unless defined$d->[3];
my$qtty=$d->[3]*$mult;
my$PIVA=$d->[6];
my$PCI=$d->[4];
my$PSI=$PCI/(1+$PIVA/100);
my$checkLineDiscount=$d->[5];
$PSI||=0;
$checkLineDiscount||=0;
if(($PSI-$checkLineDiscount)>0){$total_quantities+=$qtty;}}}my$use_print_priorities=0;
foreach my $d(sort{$a->[0]<=>$b->[0]}@$details){next unless scalar@$d;
next if$d->[3]*1==0;
next if exists$PRODUCTS_MESSAGES->{$d->[1]}&&$d->[4]==0;
if(!$cfg->{PRINT_EXTRAS_IN_DOCUMENTS}){next if exists$PRODUCTS_EXTRAS->{$d->[1]}&&$d->[4]==0;}next if$PRODUCTS_INFO->{$d->[1]}->{name}=~/^\-\-\-\-\-\-\-\-\-\-/;
my$product_name;
if($ex->{print_language}&&exists$PRODUCTS_TRANSLATIONS->{$d->[1]}->{$ex->{print_language}}){$product_name=$PRODUCTS_TRANSLATIONS->{$d->[1]}->{$ex->{print_language}};}else{$product_name=exists$PRODUCTS_INFO->{$d->[1]}?$PRODUCTS_INFO->{$d->[1]}->{name}:'UNKNOWN';}$product_name=&my_conv($product_name,26);
my$clerk_name=exists$CLERKS_INFO->{$d->[7]}?$CLERKS_INFO->{$d->[7]}->{login}:'UNKNOWN';
my$qtty=$d->[3];
my$PIVA=$d->[6]||0;
my$PCI=$d->[4];
my$PSI=$PCI/(1+$PIVA/100);
my$VDU=$d->[5];
my$PU=$PSI-$VDU;
my$PL=$PU*$d->[3];
my$VIVA=$PL*($PIVA/100);
my$discount=$d->[5]||0;
my$discount_orig=0;
my$line_discount=0;
my$gdiscount=0;
if($global_discount_percent){$discount+=($PSI-$discount)*($global_discount/100);}elsif($global_discount>0){my$div=$info->{headers}->{value}+$global_discount||1;
my$disc=($global_discount/$div)*100;
$discount+=($PSI-$discount)*($disc/100);}$total_discounts+=$discount*$qtty*(1+$PIVA/100);
$use_print_priorities=1 if$PRINTING_PRIORITIES->{$d->[1]};
my$product_type="";
if($d->[13]eq 'MENU'){$product_type='MENU';}elsif($d->[13]eq 'EXTRA'){$product_type='EXTRA';}my$d={is_menu=>exists$PRODUCTS_MENUS->{$d->[1]}&&$d->[13]ne 'MENU'?1:0,product_type=>$product_type,printing_priority=>$PRINTING_PRIORITIES->{$d->[1]},product=>$d->[1],hour=>$d->[2],quantity=>$d->[3],price=>$d->[4],price_wo_vat=>$PSI,discount=>$discount,offer=>0,vat=>$d->[6],clerk=>$d->[7],station=>$d->[12],product_name=>$product_name,clerk_name=>$clerk_name,free_text=>$d->[13],order=>$d->[0],idx=>$d->[0],gbi=>$d->[-1],gb_identifier=>$d->[-1],line_total_wo_vat=>$PSI*$d->[3],};
$last_menu=$d if$d->{is_menu};
if(!$d->{is_menu}&&$product_type ne 'MENU'){$last_menu="";
push@{$info->{details}},$d;}else{push@{$info->{details}},$d;
if($cfg->{MENUS_VALUE_BY_COMPONENT}){if($d->{is_menu}&&!$d->{no_components}){$d->{menu_total_price}+=$menu_original_prices->{$d->{gbi}}if exists$menu_original_prices->{$d->{gbi}};}elsif($d->{product_type}eq 'MENU'){if(exists$menu_original_prices->{$d->{gbi}}){$last_menu->{menu_total_price}+=$menu_original_prices->{$d->{gbi}};
$last_menu->{menu_total_price_wo_vat}+=$d->{line_total_wo_vat};}}}else{if($d->{is_menu}&&!$d->{no_components}){$d->{menu_total_price}+=$d->{price};}}}}$info->{details}||=undef;
return unless defined$info->{details};
if($check_has_menus&&!$cfg->{MENUS_OPTIMIZED_FOR_GROUPS}){my$last_key="";
my$last_menu_price=0;
my%to_ignore=();
my%grouping=();
my$count=-1;
my$idx=0;
my%t=();
my%t2=();
my%menu_group=();
my%menu_keys=();
my%menu_qttys=();
my$last_menu_gbi="";
my$last_menu_key="";
foreach my $d(sort{$a->{idx}<=>$b->{idx}}@{$info->{details}}){if($d->{is_menu}){$last_menu_gbi=$d->{gb_identifier};
$menu_group{$last_menu_gbi}=$d->{vat}."#".$d->{product}."#".($d->{price}*1)."#".$d->{discount}.",";
$menu_qttys{$last_menu_gbi}+=$d->{quantity};}elsif($d->{product_type}eq 'MENU'){my$key=$d->{vat}."#".$d->{product}."#".($d->{price}*1)."#".$d->{discount}.",";
$menu_group{$last_menu_gbi}.=$key;}}foreach my $k(keys%menu_group){$menu_keys{$menu_group{$k}}+=$menu_qttys{$k};}my%did_keys=();
my%did_grouped_menus_id=();
my%did_components=();
my%did_details=();
my$qty_increment=0;
my@menu_new_details=();
my$prev_menu;
my$prev_menu_key="";
my$qty_hardcoded=0;
foreach my $d(sort{$a->{idx}<=>$b->{idx}}@{$info->{details}}){my$gbi=$d->{gb_identifier};
if($d->{is_menu}){$prev_menu=$d;
my$key=$menu_group{$gbi};
if(defined$key){$d->{menus_grouping_key}=$key;
if(!exists$did_grouped_menus_id{$key}){$d->{quantity}=exists$menu_keys{$key}?$menu_keys{$key}:1;
$d->{menu_original_price}=$d->{menu_original_price}*$d->{quantity}if exists$d->{menu_original_price};
$did_keys{$key}=$d->{quantity};
push@menu_new_details,$d;}$did_grouped_menus_id{$key}++;}else{push@menu_new_details,$d;}}elsif($d->{product_type}eq 'MENU'){if(!exists$prev_menu->{menus_grouping_key}){push@menu_new_details,$d;}elsif(defined$prev_menu&&!exists$did_grouped_menus_id{$prev_menu->{menus_grouping_key}}){push@menu_new_details,$d;}else{if($did_grouped_menus_id{$prev_menu->{menus_grouping_key}}==1){$d->{quantity}=$prev_menu->{quantity};
push@menu_new_details,$d;}}}else{push@menu_new_details,$d;}}$info->{details}=\@menu_new_details;}$use_print_priorities=0 if$check_has_menus;
if($use_print_priorities){my@t_tmp=reverse sort{$a->{gbi}cmp$b->{gbi}}@{$info->{details}};
my$idx=1;
for(@t_tmp){$_->{printing_priority}||=0;
$_->{printing_priority}=$_->{printing_priority}?$_->{printing_priority}:$idx++;}@{$info->{details}}=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@t_tmp;}else{@{$info->{details}}=sort{$a->{idx}<=>$b->{idx}}@{$info->{details}};}my$total_wo_discount=0;
my$products_quantities=0;
my$total_w_vat=0;
my$value_net=0;
my$had_vat=0;
foreach my $d(@{$info->{details}}){$last_menu=$d if$d->{is_menu};
$d->{quantity}=$d->{quantity}*$mult;
$d->{vat}||=0;
$had_vat=1 if$d->{vat}*1>0;
$d->{txts}=[];
if(exists$products_extra_data->{$d->{gbi}}){my$prod_extra_data=$products_extra_data->{$d->{gbi}};
if(defined$prod_extra_data&&$prod_extra_data){my@t=split/;/,$prod_extra_data;
for my $tok(@t){next unless$tok;
$Text::Wrap::columns=38;
my$l=wrap('','',$tok);
my@lines=split/\n/,$l;
for(@lines){push@{$d->{txts}},$_;}}if(scalar@{$d->{txts}}==1&&$d->{txts}->[0]eq 'MENU'){$d->{txts}=[];}}}my$sem_iva=&my_round($d->{quantity}*$d->{price_wo_vat},$cfg->{DECIMAL_PLACES}||2);
my$com_iva=$d->{quantity}*$d->{price};
my$desconto_com_iva=0;
my$desconto_sem_iva=0;
if($d->{price}!=0){$d->{price_wo_vat}||=1;
$desconto_com_iva=$d->{quantity}*$d->{price}*($d->{discount}/$d->{price_wo_vat});
$desconto_sem_iva=&my_round($d->{quantity}*$d->{discount},$cfg->{DECIMAL_PLACES}||2);}$com_iva=$com_iva-$desconto_com_iva;
$sem_iva=$sem_iva-$desconto_sem_iva;
$value_net+=($sem_iva-$desconto_sem_iva);
my$valor_iva=$com_iva-$sem_iva;
$d->{line_total}=$d->{price}*$d->{quantity};
$d->{line_total_wo_vat}=$d->{price_wo_vat}*$d->{quantity};
$d->{unit_price}=$d->{price};
$d->{quantity}=$d->{quantity};
$d->{line_total_w_discount}=$d->{line_total}-my_round($desconto_com_iva,$cfg->{DECIMAL_PLACES}||2);
$d->{line_total_w_discount_not_rounded}=$d->{line_total}-$desconto_com_iva;
$d->{line_total_wo_vat_w_discount}=$d->{line_total_wo_vat}-my_round($desconto_sem_iva,$cfg->{DECIMAL_PLACES}||2);
$d->{line_total_wo_vat_w_discount_not_rounded}=$d->{line_total_wo_vat}-$desconto_sem_iva;
$total_w_vat+=$d->{line_total_w_discount_not_rounded};
if(exists$d->{menu_total_price}){$d->{menu_total_price}=$d->{menu_total_price}*$d->{quantity};}if($d->{vat}*1!=0){$vat{$d->{vat}}->{w_vat}+=$d->{line_total_w_discount};
$vat{$d->{vat}}->{wo_vat}+=$desconto_com_iva>0?my_round($sem_iva,$cfg->{DECIMAL_PLACES}||2):$sem_iva;}else{$vat{$d->{vat}}->{wo_vat}+=$sem_iva*$mult;
$vat{$d->{vat}}->{w_vat}+=$sem_iva*$mult;}$total_wo_discount+=$d->{line_total};
$products_quantities+=$d->{quantity}unless exists$PRODUCTS_EXTRAS->{$d->{product}};
if(!$d->{is_menu}&&$d->{product_type}ne 'MENU'){$last_menu="";
push@$details,$d;}else{push@$details,$d;
if($cfg->{MENUS_VALUE_BY_COMPONENT}){if($d->{is_menu}&&!$d->{no_components}){$d->{menu_total_price}+=$d->{menu_original_price};}elsif($d->{product_type}eq 'MENU'){$last_menu->{menu_total_price}+=$d->{menu_original_price}*$d->{quantity};
$last_menu->{menu_total_price_wo_vat}+=$d->{line_total_wo_vat};}}else{if($d->{is_menu}&&!$d->{no_components}){$d->{menu_total_price}+=$d->{price}*$d->{quantity};}}}}my$total_vat=0;
my$tmp_total=0;
for(keys%vat){$vat{$_}->{vat_v}=&my_round($vat{$_}->{w_vat},$cfg->{DECIMAL_PLACES}||2)-&my_round($vat{$_}->{wo_vat},$cfg->{DECIMAL_PLACES}||2);
$total_vat+=$vat{$_}->{vat_v};
$tmp_total+=$vat{$_}->{w_vat};
if($vat{$_}->{vat_v}==0&&$vat{$_}->{wo_vat}==0){delete$vat{$_};}}$tmp_total=abs($tmp_total);
my$total_diff=my_round($total_w_vat,$cfg->{DECIMAL_PLACES}||2)-my_round($tmp_total,$cfg->{DECIMAL_PLACES}||2);
if($total_diff!=0){foreach my $vat(sort keys%vat){next if$vat*1==0;
$vat{$vat}->{w_vat}+=$total_diff;
$vat{$vat}->{vat_v}+=$total_diff;
last;}}my$discount_was_percentage=0;
if($discount_percent=~/%/){$discount_was_percentage=1;
$discount_percent=~s/%//g;}$info->{products_number}=scalar@{$info->{details}};
$info->{products_quantities}=$products_quantities;
$info->{total_discounts}=$total_discounts;
$info->{discount_percent}=$discount_percent;
$info->{discount_was_percentage}=$discount_was_percentage;
$info->{total_wo_discount}=$total_wo_discount;
foreach my $vat(sort keys%vat){$info->{total_wo_vat}+=$vat{$vat}->{wo_vat};}if(defined$AREAS_INFO{$info->{headers}->{area}}->[10]&&$AREAS_INFO{$info->{headers}->{area}}->[10]){$info->{area_wo_vat}=1}my%info=(check=>$info);
my$content="";
$cfg->{DONT_RESTART_REQUEST_NUMBERS}||=0;
my$req_key=$cfg->{DONT_RESTART_REQUEST_NUMBERS}?'requests':$business_date;
$info{menus_value_by_component}=$cfg->{MENUS_VALUE_BY_COMPONENT};
$info{vat_info}=\%vat;
$info{check_number}=$check;
$info{check_name}=$o->name();
$info{request_number}=$request_numbers->cur_value($req_key);
$info{in_payment}=exists$ex->{in_payment}?$ex->{in_payment}:'';
$info{print_products_mode}=$cfg->{PRINT_CHECK_PRODUCTS_MODE};
$ex->{obs}//="";
if($ex->{obs}){$ex->{obs}=my_conv($ex->{obs});
my@obs=split/§/,$ex->{obs};
$info{check}->{obs}=\@obs;}$info{check_name}||="";
if(!$info{check_name}){$info{table_desc}=$check;}else{if($info{check_name}=~/^\d+$/){$info{table_desc}=$info{check_name};}else{$info{table_desc}=$check." (".$info{check_name}.")";}}$info{table_desc}=~s/<br>/ /g;
$info{check}->{extra_data}=$ex;
$data->{extra_data}->{delivery_info}||="";
if($data->{extra_data}->{delivery_info}){my$dinfo=&_decode_delivery_info($db,$data);
$info{delivery_info}=$dinfo;}if($cfg->{PRINT_BARCODE_IN_BILL}&&$info{check_number}=~/^\d+$/){my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$file;
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,"999990".sprintf("%06s",sprintf("%02d",$info->{headers}->{area}).sprintf("%04d",$info{check_number})),5);}my$tmpl_file="";
if($is_internal){if(-e$machine->dir().'/common/print_templates/'.$cfg->{LANG}."/print_bill_internal.tmpl"){$tmpl_file=$cfg->{LANG}."/print_bill_internal.tmpl";}else{$tmpl_file="print_bill_internal.tmpl";}}else{if(-e$machine->dir().'/common/print_templates/'.$cfg->{LANG}."/print_bill.tmpl"){$tmpl_file=$cfg->{LANG}."/print_bill.tmpl";}else{$tmpl_file="print_bill.tmpl";}}if(-e"/opt/pos/common/print_templates/".$tmpl_file.".custom"){$tmpl_file=$tmpl_file.".custom";}my$print_station=undef;
my$print_printer=undef;
$cfg->{MOBILE_PRINT_BILLS}||="";
if($cfg->{MOBILE_PRINT_BILLS}&&$from_mobile){if(!%MOBILE_PRINT_BILLS){my@by_area=split/\-/,$cfg->{MOBILE_PRINT_BILLS};
foreach my $tok(@by_area){my@toks=split/,/,$tok;
if(scalar@toks==3){$MOBILE_PRINT_BILLS{$toks[0]}->{station}=$toks[1];
$MOBILE_PRINT_BILLS{$toks[0]}->{printer}=$toks[2];}elsif(scalar@toks==2){$MOBILE_PRINT_BILLS{ALL}->{station}=$toks[0];
$MOBILE_PRINT_BILLS{ALL}->{printer}=$toks[1];}}}if(exists$MOBILE_PRINT_BILLS{$info{check}->{headers}->{area_name}}){$print_station=$MOBILE_PRINT_BILLS{$info{check}->{headers}->{area_name}}->{station};
$print_printer=$MOBILE_PRINT_BILLS{$info{check}->{headers}->{area_name}}->{printer};}elsif(exists$MOBILE_PRINT_BILLS{ALL}){$print_station=$MOBILE_PRINT_BILLS{ALL}->{station};
$print_printer=$MOBILE_PRINT_BILLS{ALL}->{printer};}}$cfg->{PRINTER_OVERRIDE_BY_DOCUMENT}||="";
if($cfg->{PRINTER_OVERRIDE_BY_DOCUMENT}&&!defined$print_station&&!defined$print_printer){if(!%DOC_TYPE_PRINTER_OVERRIDES){my@by_doc=split/\-/,$cfg->{PRINTER_OVERRIDE_BY_DOCUMENT};
foreach my $tok(@by_doc){my@toks=split/,/,$tok;
if(scalar@toks==3){$DOC_TYPE_PRINTER_OVERRIDES{$toks[0]}->{station}=$toks[1];
$DOC_TYPE_PRINTER_OVERRIDES{$toks[0]}->{printer}=$toks[2];}else{print STDERR"PRINTER_OVERRIDE_BY_DOCUMENT has an incorrect value... Check docs...\n";}}}if(exists$DOC_TYPE_PRINTER_OVERRIDES{$doc_type}){$from_mobile=1;
$print_station=$DOC_TYPE_PRINTER_OVERRIDES{$doc_type}->{station};
$print_printer=$DOC_TYPE_PRINTER_OVERRIDES{$doc_type}->{printer};}}my$file_control=$machine->dir_server().'/server/data/tmp/.in_print_bill';
if(-e$file_control){my$count=0;
while(1){last unless-e$file_control;
last if$count++ ==2;
sleep 1;}}write_file($file_control,1);
my$valid=undef;
my$dnumber;
my$previous_hash;
while(!defined$valid){($dnumber,$previous_hash)=$machine->find_next_doc_id($doc_type,$doc_serie,$business_date,$fo_doc_number,$pt_certified_software,);
$fo_doc_number=$fo_doc_number." ".$doc_serie."/".$dnumber;
my$doc_exists=$db->select(qq{
      SELECT fo_doc_number FROM sales_hashes WHERE doc_type = ? AND fo_doc_number = ?
    },$doc_type,$fo_doc_number);
$valid=1 unless scalar@$doc_exists;}my@hash_chars=();
my$hash="";
if($pt_certified_software){my$cdt=&strftime($cur_date."T".$cur_hour,localtime);
my$str_to_sign=$business_date.";".$cdt.";".$fo_doc_number.";".sprintf("%.02f",$h->[19]).";".$previous_hash;
$hash=$machine->sign_string($str_to_sign);
if(length($hash)!=172){$machine->send_alert_to_ui(&translate($machine,"ASSINATURA DOCUMENTO INVALIDA.<BR>TENTE NOVAMENTE."));
print STDERR"INVALID HASH!!!!! $hash, aborting...\n";
return undef;}my$doc_hashes=App::POS::Counters->new(db=>$db,name=>'doc_hashes',);
$doc_hashes->set_value($doc_type."___".$doc_serie,$hash);
@hash_chars=&_get_sign_chars(signature=>$hash);}$info->{headers}->{fo_doc_number}=$fo_doc_number;
$info->{headers}->{hash}=$hash;
$info->{headers}->{hash_small}=join("",@hash_chars);
if($pt_certified_software){if($machine->software_version>=202301){my$series_codes=$machine->get_series_codes();
if(exists$series_codes->{$doc_type_str." ".$doc_serie}){my$serie_code=$series_codes->{$doc_type_str." ".$doc_serie}||'0';
if(length($serie_code)>10){$serie_code='0';}$info{check}->{extra_data}->{atcud}="$serie_code-$dnumber";}else{$info{check}->{extra_data}->{atcud}="0-$dnumber";}}else{$info{check}->{extra_data}->{atcud}="0";}}my$r=int(rand(10000));
my$time=time;
$time-=2;
my$new_id=&create_UUID_as_string();
my$dbh=$db->dbh();
my$atcud=exists$info{check}->{extra_data}->{atcud}?$info{check}->{extra_data}->{atcud}:'';
$dbh->do("COPY sales_hashes (id, header, gb_identifier_header, business_date, system_entry_date, fo_doc_number, "."value, hash, hash_small, doc_type, value_net, clerk_close, customer_id, serie, table_num, atcud) FROM stdin");
$info->{headers}->{customer_id}||=0;
$value_net||=0;
$value_net=0 if$info->{headers}->{value}*1==0;
if(!$had_vat){$value_net=$info->{headers}->{value};}my$table_number=$o->table_number();
$table_number=-1 if$table_number!~/^\d+$/;
my$serie=1;
$dbh->pg_putline($new_id."\t0\t".$h->[-1]."\t$business_date\t".$cur_date."T".$cur_hour."\t$fo_doc_number\t".$info->{headers}->{value}."\t$hash\t".(join("",@hash_chars))."\t$doc_type\t$value_net\t".$info->{headers}->{clerk_close}."\t".$info->{headers}->{customer_id}."\t$serie\t$table_number\t$atcud\n");
$dbh->pg_endcopy();
if($pt_certified_software){$dbh->do("COPY sales_hashes_products (hash_id, gb_identifier_detail, business_date, product_id, "."price, price_wo_vat, quantity, discount, vat) FROM stdin");
foreach my $d(@{$info->{details}}){$dbh->pg_putline("$new_id\t$d->{gbi}\t$business_date\t$d->{product}\t$d->{price}\t$d->{price_wo_vat}\t"."$d->{quantity}\t$d->{discount}\t$d->{vat}\n");
$memc->set('gbi_had_bill#'.$d->{gbi},1);}$dbh->pg_endcopy();}else{foreach my $d(@{$info->{details}}){$memc->set('gbi_had_bill#'.$d->{gbi},1);}}unlink$file_control;
$info{check}->{extra_data}=$data->{extra_data};
$info{check}->{kept_in_source}||=0;
$info{check}->{in_happy_hour}=-e$machine->dir_server().'/server/data/tmp/in_happy_hour.txt'?1:0;
$machine->get_customer_info($db,$data->{header}->[11],$info{check}->{headers})if$data->{header}->[11];
if(!$dont_print){return unless$REAL_PRINT;
for(my$i=0;$i<$vias;$i++){my$via=$i+1;
my$txt=&translate($machine,$DOC_VIAS{$via});
$content="";
$info{check}->{headers}->{via}=$txt;
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($tmpl_file,\%info,\$content,)||die$TEMPLATE->error;
if($pt_certified_software){if($print_bill_md5_valid){$print_bill_md5_cur=_file_md5("/opt/pos/common/print_templates/print_bill.tmpl")unless defined$print_bill_md5_cur;
if($print_bill_md5_cur ne$print_bill_md5_valid){$content="\n\n"."---------------------------------------\n"."DOCUMENTO DE IMPRESSAO NAO E VALIDO\n"."---------------------------------------\n"."\n\n\@paper_cut\@";}}if(-e"/opt/pos/.nif"){open(F,"< /opt/pos/.nif");
my$nif=<F>;
chop($nif);
close(F);
if($nif){my@lines=split/\n/,$content;
$lines[2].="\nContribuinte n. $nif";
$content=join("\n",@lines);}}}_add_custom_top_and_bottom($machine,$info->{headers}->{area},\$content);
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station_original,station_close=>$station_original,station_print_checks=>$station_print_checks,);
if(defined$print_file){$data->{extra_data}->{print_send_to}||="";
$data->{extra_data}->{save_to_hdd}||="";
if($data->{extra_data}->{print_send_to}){my$dir_server_tmp=$machine->server_dir_tmp();
my@a_tmp=split/#/,$print_file;
write_file("$dir_server_tmp/$a_tmp[4].print_send_to",$data->{extra_data}->{print_send_to});}if($data->{extra_data}->{save_to_hdd}){my$dir_server_tmp=$machine->server_dir_tmp();
my@a_tmp=split/#/,$print_file;
write_file("$dir_server_tmp/$a_tmp[4].save_to_hdd",$data->{extra_data}->{save_to_hdd});}if($from_mobile&&defined$print_station){$print_printer--;
$print_file=~s/print#\d+#\d+#/print#$print_station#$print_printer#/;}if($pt_certified_software){$cfg->{INVOICE_QRCODES_TURN_OFF}//=0;
if($machine->software_version>=202101&&!$cfg->{INVOICE_QRCODES_TURN_OFF}){&set_owner_information($machine,$info{check}->{headers});
require App::POS::PrinterQrCode;
my$qr_code=App::POS::PrinterQrCode->new(machine=>$machine,info=>\%info,);
$qr_code->generate_png("/tmp/qrcode.png");
my$qr_content=$qr_code->generate();
my$memc=memcache($machine->server_ip(),'qr_codes',\%MEMC_CONNECTIONS);
my@a=split/#/,$print_file;
$memc->set($a[4],$qr_content,5);}}$cfg->{DELIVERY_QRCODE_PRINT}||=0;
if($cfg->{DELIVERY_QRCODE_PRINT}&&exists$DELIVERY_AREAS{$info->{headers}->{area}}){my$link="";
if($cfg->{DELIVERY_QRCODE_PRINT}==1){$link='https://www.google.com/maps/place/';}else{$link='https://waze.com/ul?q=';}$link.=url_encode($info->{headers}->{customer_address}.", ".$info->{headers}->{customer_postal_code}.", ".$info->{headers}->{customer_city}.", Portugal");
my$qr_code=App::POS::PrinterQrCode->new(machine=>$machine,info=>{},);
my$qr_content=$qr_code->generate_link($link);
my$memc=memcache($machine->server_ip(),'qr_codes',\%MEMC_CONNECTIONS);
my@a=split/#/,$print_file;
$memc->set($a[4].".delivery",$qr_content,5);}if($ex->{send_to_network_printer}ne ''){lazy_file_create("/opt/pos/common/tmp/send_to_network_printer.txt",$ex->{send_to_network_printer});}open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}}}sub print_check_hold{my($machine,$file)=@_;
if($machine->is_server()){my($check_file,$station)=$file=~/^(.*?)\.(\d+)\.hold$/;
&_handle_request_prints($machine,$check_file,'hold',$station,$file);}else{return 2;}}sub printer_error{my($machine,$file)=@_;
my$cfg=$machine->config->load();
my($station,$printer,$error_station)=$file=~/printer_error#(\d+)#(\d+)#(\d+)\.txt$/;
print STDERR"printer_error - $file\n";
my$my_station=defined$error_station&&$error_station?$error_station:$station;
my$ignore_file=$machine->server_dir_data()."/printer_ignore#$my_station#$printer.txt";
my$alert_file=$machine->dir_tmp()."/printer_error_alert#$my_station#$printer#$station.txt";
if(!-e$ignore_file){if($cfg->{STATION_NUMBER}==$my_station){print STDERR"This is the alert station, will create alert file - $alert_file\n";
&copy($file,$alert_file);
unlink$file;}else{print STDERR"This isn't the alert station ignoring...\n";
return 2;}}else{print STDERR"Error printer is being ignored, will remove alert file...\n";
unlink($alert_file);}}sub print_file{my($machine,$file)=@_;
my$cfg=$machine->config->load();
my($path,$station,$printer,$finalize,$timestamp)=$file=~/^(.*?)\/print#(\d+)#(\d+)#(\d+)#(.*?)\.txt$/;
my($station_who_printed)=$timestamp=~/_(\d+)$/;
$station_who_printed//="";
my$printer_errors_config=_load_printer_errors_config($machine);
my$binary=$timestamp=~/binary/?1:0;
my$pt_certified_software=-e$machine->dir()."/common/skins/".$cfg->{LANG}."/sign_server"?1:0;
if($cfg->{STATION_NUMBER}==$station){if($finalize==1){if(exists$cfg->{STATION_PRINT_CHECKS}&&$cfg->{STATION_PRINT_CHECKS}!=99){my$new_file=$path."/print#".$cfg->{STATION_PRINT_CHECKS}."#$printer#$finalize#$timestamp.txt";
if($file eq$new_file){}else{my$new_file2=$path."/ttttt#".$cfg->{STATION_PRINT_CHECKS}."#$printer#$finalize#".time.".txt";
&copy($file,$new_file2);
&copy($new_file2,$new_file);
unlink($new_file2);
return;}}}my$dir_server_tmp=$machine->server_dir_tmp();
my$dir_server_data=$machine->server_dir_data();
my$ignore_file="$dir_server_data/printer_ignore#$station#$printer.txt";
if(-e$ignore_file){print STDERR"Won't print file because printer is being ignored...\n";
return;}my@redirects=<$dir_server_data/printer_redirect#$station#$printer#*.txt>;
if(scalar@redirects){foreach my $re(@redirects){my($from_station,$from_printer,$to_station,$to_printer)=$re=~/\/printer_redirect#(\d+)#(\d+)#(\d+)#(\d+)\./;
my$new_file="$dir_server_tmp/print#$to_station#$to_printer#$finalize#$timestamp.txt";
if($file eq$new_file){print STDERR"Redirect to itself, ignoring...\n";}else{my$new_file2="$dir_server_tmp/ttttt#$to_station#$to_printer#$finalize#$timestamp.txt";
&copy($file,$new_file2);
&copy($new_file2,$new_file);
unlink($new_file2);
unlink($file);
return;}}}my$printers=$machine->printers();
my$printer_idx=$printer;
my$error_str="";
my$in_error_printer=-1;
my$in_error_station=-1;
my$error_file="/tmp/printer_error#".$printer_idx.".txt";
my$prn_check=$printers->[$printer_idx];
if(defined$prn_check&&scalar keys%$prn_check){return if$prn_check->{Inactive};}if(-e$error_file){my$error_printer_idx=$printer_idx+1;
$error_str="!!ERROR > ".$printers->[$printer_idx]->{Description}."!!";
$printers->[$printer_idx]->{BackupPrinter}||="";
$station_who_printed=$station unless defined$station_who_printed&&$station_who_printed ne '';
if($station_who_printed ne ''&&exists$printer_errors_config->{$error_printer_idx}&&exists$printer_errors_config->{$error_printer_idx}->{$station_who_printed}){my$ref=$printer_errors_config->{$error_printer_idx}->{$station_who_printed};
$ref->{station}//="";
$ref->{printer}//="";
if($ref->{station}ne ''&&$ref->{printer}ne ''){$in_error_printer=$ref->{printer};
$in_error_printer=$in_error_printer-1;
$in_error_station=$ref->{station};}}elsif($in_error_printer==-1&&$in_error_station==-1){$in_error_station=$station;
if($printers->[$printer_idx]->{BackupPrinter}){my@t=split/\-/,$printers->[$printer_idx]->{BackupPrinter};
if(scalar@t==1&&$t[0]=~/^\d+$/){print STDERR"PRINTER $error_printer_idx IS IN ERROR, WILL SEND TO PRINTER $t[0]\n";
$t[0]-- if$t[0]>0;
$in_error_printer=$t[0];
if(-e"/tmp/printer_error#".$t[0].".txt"){print STDERR"WILL STOP HERE BECAUSE BACKUP PRINTER IS IN ERROR...\n";
return;}}elsif(scalar@t==2&&$t[0]=~/^\d+$/&&$t[1]=~/^\d+$/){print STDERR"PRINTER $error_printer_idx IS IN ERROR, WILL SEND TO STATION $t[0] PRINTER $t[1]\n";
$in_error_station=$t[0]if$t[0]!=$station;
$t[1]-- if$t[1]>0;
$in_error_printer=$t[1];
if($station==$t[0]){if(-e"/tmp/printer_error#".$t[1].".txt"){print STDERR"WILL STOP HERE BECAUSE BACKUP PRINTER IS IN ERROR...\n";
return;}}}else{print STDERR"PRINTER $error_printer_idx IS IN ERROR, INVALID FORMAT FOR BACKUP PRINTER - ".$printers->[$printer_idx]->{BackupPrinter}."\n";}}else{if($printers->[$printer_idx]->{Main}ne '1'){my$tmp_idx=$STATION_MAIN_PRINTER+1;
print STDERR"PRINTER $error_printer_idx IS IN ERROR, WILL SEND TO STATION MAIN PRINTER - $tmp_idx\n";
$in_error_printer=$STATION_MAIN_PRINTER;}else{print STDERR"WILL STOP HERE BECAUSE MAIN PRINTER IS IN ERROR...\n";
return;}}}}if($in_error_printer!=-1&&$in_error_station!=-1){$timestamp=time.$$.int(rand(10000));
my$new_file="$dir_server_tmp/print#$in_error_station#$in_error_printer#$finalize#$timestamp.txt";
open(M,"< $file");
my@m=<M>;
close(M);
open(M,"> $file");
print M"$error_str\n";
print M@m;
close(M);
&copy($file,$new_file);
unlink($file);
return;}if(scalar@$printers&&exists$printers->[$printer_idx]){$printers->[$printer_idx]->{Device}||="";
$printers->[$printer_idx]->{PaperCutLinesAdvance}||="0";
my$cnt2="";
my$size=-s$file;
{if(open(F,"< $file")){local$/=undef;
$cnt2=<F>;
close(F);}}if($printers->[$printer_idx]->{PaperCutLinesAdvance}){my$ad=$printers->[$printer_idx]->{PaperCutLinesAdvance};
my$start_count=0;
my$c=0;
my@lines=split/\n/,$cnt2;
my@new_lines=();
for(my$i=0;$i<scalar(@lines);$i++){my$txt=$lines[$i];
if($txt=~/\@paper_cut\@/){$txt=~s/\@paper_cut\@//g;
$start_count=1;}if($c==$ad){$c=0;
$start_count=0;
$txt='@paper_cut@'.$txt;}push@new_lines,$txt if$txt;
$c++ if$start_count;}if($c<$ad){while($c++ <$ad){push@new_lines,"";}push@new_lines,'@paper_cut@';}$cnt2=join("\n",@new_lines);}my$server_cfg=$machine->is_server()?$cfg:$machine->server_config->load();
_replace_saft_tags($server_cfg,\$cnt2);
my($ip_redirect)=$file=~/_ipredir=(.*?)\.txt$/;
$ip_redirect||="";
my$printer_custom_ip="";
my$printer_custom_port="";
if(-e"/opt/pos/common/tmp/send_to_network_printer.txt"){my$network=read_file("/opt/pos/common/tmp/send_to_network_printer.txt");
$network//="";
if($network){($printer_custom_ip,$printer_custom_port)=_network_printer_decode($network);}unlink"/opt/pos/common/tmp/send_to_network_printer.txt";}if(uc($printers->[$printer_idx]->{Device})eq '@SCREEN@'&&!$ip_redirect&&!$printer_custom_ip){my$dir_tmp=$machine->dir_tmp();
$cnt2=~s/\@.*?\@//g;
if($error_str){$cnt2.="\n$error_str\n";}my@a_tmp=split/#/,$file;
if(-e"$dir_server_tmp/$a_tmp[4].save_to_hdd"){my$dest=read_file("$dir_server_tmp/$a_tmp[4].save_to_hdd");
my$f="$dir_server_tmp/$dest";
write_file($f,$cnt2);
unlink"$dir_server_tmp/$a_tmp[4].save_to_hdd";}open(F,">> $dir_tmp/printer_screen.txt");
print F$cnt2;
print F"=======================================\n\n\n";
close(F);
return;}my$script=$printers->[$printer_idx]->{Script}||'';
my$driver=$printers->[$printer_idx]->{Driver}||'';
my$invert=$printers->[$printer_idx]->{Invert}||0;
my$printer=App::POS::Printer->new(driver=>$driver);
my$p_class=$printer->get_class();
if(exists$printers->[$printer_idx]->{TextEncoding}&&$binary==0){$cnt2=encode($printers->[$printer_idx]->{TextEncoding},decode("utf8",$cnt2));}return unless defined$cnt2;
my$qrcode;
my$memc=memcache($machine->server_ip(),'qr_codes',\%MEMC_CONNECTIONS);
my@a_tmp=split/#/,$file;
$qrcode=$memc->get($a_tmp[4]);
$qrcode||="";
$memc->delete($a_tmp[4]);
if($qrcode){_apply_qrcode_to_doc($cfg,\$cnt2,'@qrcode@',\$qrcode,$ip_redirect);}$qrcode=$memc->get($a_tmp[4].'.delivery');
$qrcode||="";
$memc->delete($a_tmp[4].'.delivery');
if($qrcode){_apply_qrcode_to_doc($cfg,\$cnt2,'@qrcode_delivery@',\$qrcode,$ip_redirect);}my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
@a_tmp=split/#/,$file;
my($tmp)=$a_tmp[4]=~/^(\d+)/;
my$barcode_tmp=$memc_b->get($tmp);
$barcode_tmp||="";
$memc_b->delete($tmp);
if($barcode_tmp){my$barcode=$barcode_tmp;
my@l=();
my$barcode_method="";
my$barcode_method_small="";
my$barcode_method_big="";
if(length($barcode)==12){$barcode_method='barcode_ean13';
$barcode_method_small='barcode_small_ean13';
$barcode_method_big='barcode_big_ean13';
my$cd=calcEAN13CD($barcode);
$barcode=$barcode.$cd;}elsif(length($barcode)==13){$barcode_method='barcode_ean13';
$barcode_method_small='barcode_small_ean13';
$barcode_method_big='barcode_big_ean13';}else{$barcode_method='barcode_39';
$barcode_method_small='barcode_small_39';
$barcode_method_big='barcode_big_39';}if($cnt2=~/\@barcode\@/mg){@l=$p_class->$barcode_method();
$cnt2=~s/\@barcode\@/\@barcode\@$barcode\n\@justify_left\@/mg;}elsif($cnt2=~/\@barcode_small\@/mg){@l=$p_class->$barcode_method_small();
$cnt2=~s/\@barcode_small\@/\@barcode_small\@$barcode\n\@justify_left\@/mg;}elsif($cnt2=~/\@barcode_big\@/mg){@l=$p_class->$barcode_method_big();
$cnt2=~s/\@barcode_big\@/\@barcode_big\@$barcode\n\@justify_left\@/mg;}my$seq="";
if(scalar@l){for(@l){$_=~s/#BARCODE#/$barcode/g;
$seq.=$_;}}$cnt2=~s/\@barcode\@/$seq/mg;
$cnt2=~s/\@barcode_small\@/$seq/mg;
$cnt2=~s/\@barcode_big\@/$seq/mg;}if($ip_redirect!~/android/){&_printer_tags_replace($p_class,$printers->[$printer_idx],\$cnt2)if$binary==0;}if($error_str){$cnt2="$error_str\n$cnt2";}if($ip_redirect){my$printer_ip="";
my$printer_port="";
$cfg->{LOG_POS_DEBUG}||=0;
my$log_file="/opt/pos/server/data/network_printing_".strftime("%Y%m%d",localtime).".log";
if($cfg->{LOG_POS_DEBUG}){open(F,">> $log_file");
print F"Will try to print to $printer_ip:$printer_port\n";
close(F);}if($ip_redirect=~/android/){$ip_redirect=~s/android//i;
($printer_ip,$printer_port)=_network_printer_decode($ip_redirect);
$cnt2=~s/\@logo(\d+)*?\@//g;
my$logo="";
if($1){my$num=$1;
if(-e"/opt/pos/common/data/logo".$num.".bmp"){if(!-e"/tmp/android_logo.png"){system("convert /opt/pos/common/data/logo".$num.".bmp /tmp/android_logo.png");}$logo=encode_base64(read_file("/tmp/android_logo.png"));}}$cnt2=~s/\@size_.*?\@//g;
$cnt2=~s/\@justify_.*?\@//g;
my$paper_cut=$p_class->paper_cut();
$cnt2=~s/\@paper_cut\@/$paper_cut/g;
if(-e"/tmp/qrcode.png"){my$qr=encode_base64(read_file("/tmp/qrcode.png"));
my$cnt=post("http://$printer_ip:51051/qrcode",{qrcode=>$qr,content=>$cnt2,logo=>$logo,},5);
$cnt||="";
unlink"/tmp/qrcode.png";}}else{($printer_ip,$printer_port)=_network_printer_decode($ip_redirect);
my$sock=_get_socket($printer_ip,$printer_port);
$cnt2=~s/\@size_.*?\@//g;
$cnt2=~s/\@paper_cut\@//g;
$cnt2=~s/\@justify_.*?\@//g;
if(defined$sock){my$cls=$printer->get_class();
if($invert){my$seq=$cls->print_invert();
$cnt2=$seq.$cnt2;
print$sock $cnt2;}else{my$seq=$cls->print_normal();
print$sock $seq.$cnt2;}if($cfg->{LOG_POS_DEBUG}){open(F,">> $log_file");
print F"Sent to printer:\n\n";
print F$cnt2."\n";
close(F);}close($sock);
sleep 0.4;}else{$machine->send_alert_to_ui(&translate($machine,"PRINTER ERROR"." - $ip_redirect"));
if($cfg->{LOG_POS_DEBUG}){open(F,">> $log_file");
print F"ERROR IN SOCKET - 2 - $printer_ip:$printer_port\n";
close(F);}}}return;}my$stop_now=0;
if(-e"$dir_server_tmp/$a_tmp[4].save_to_hdd"){my$dest=read_file("$dir_server_tmp/$a_tmp[4].save_to_hdd");
my$f="$dir_server_tmp/$dest";
write_file($f,$cnt2);
unlink"$dir_server_tmp/$a_tmp[4].save_to_hdd";
$stop_now=1;}if(-e"$dir_server_tmp/$a_tmp[4].print_send_to"){my$dest=read_file("$dir_server_tmp/$a_tmp[4].print_send_to");
my$f="$dir_server_tmp/$$.print_send_to.txt";
write_file($f,$cnt2);
system("cat $f | timeout 3s lp -h $dest -");
unlink($f);
unlink"$dir_server_tmp/$a_tmp[4].print_send_to";
$stop_now=1;}return if$stop_now;
if($script&&-e$script){my$tmp_dir=$machine->dir_server()."/server/data/tmp";
my$r=$$."_".int(rand(10000));
my$file="$tmp_dir/$r.txt";
open(F,"> $file");
print F$cnt2;
close(F);
my$check_info_file="none";
if(defined$LAST_INTEGRATED_CHECK){$r=$$."_".int(rand(10000));
$check_info_file="$tmp_dir/$r.txt";
open(F,"> $check_info_file");
print F Dumper($LAST_INTEGRATED_CHECK);
close(F);
$LAST_INTEGRATED_CHECK=undef;}system("\"$script\" \"$file\" \"$check_info_file\"");}eval{local$SIG{ALRM}=sub{die"alarm\n"};
$printers->[$printer_idx]->{Device}||="";
$printers->[$printer_idx]->{IP}||="";
$printers->[$printer_idx]->{CupsQueue}||="";
if($printers->[$printer_idx]->{CupsQueue}){print STDERR"Will print file to CupsQueue > ".$printers->[$printer_idx]->{CupsQueue}."\n";
alarm 15;
my$cls=$printer->get_class();
if($invert){my$seq=$cls->print_invert();
$cnt2=$seq.$cnt2;}else{my$seq=$cls->print_normal();
$cnt2=$seq.$cnt2;}my$r=$$."_".int(rand(10000));
my$file="/tmp/$r.txt";
open(F,"> $file");
print F$cnt2;
close(F);
system("lp -d \"".$printers->[$printer_idx]->{CupsQueue}."\" $file");}elsif($printer_custom_ip||$printers->[$printer_idx]->{IP}){alarm 3;
my($printer_ip,$printer_port)=("","");
if($printer_custom_ip){($printer_ip,$printer_port)=($printer_custom_ip,$printer_custom_port);}else{print STDERR"Will print file $file to IP ".$printers->[$printer_idx]->{IP}." (2)\n";
($printer_ip,$printer_port)=_network_printer_decode($printers->[$printer_idx]->{IP});}$cfg->{LOG_POS_DEBUG}||=0;
my$log_file="/opt/pos/server/data/network_printing_".strftime("%Y%m%d",localtime).".log";
if($cfg->{LOG_POS_DEBUG}){open(F,">> $log_file");
print F"Will try to print to $printer_ip:$printer_port\n";
close(F);}my$sock=_get_socket($printer_ip,$printer_port);
if(defined$sock){$printers->[$printer_idx]->{IPActiveControlDisable}//=0;
if(!$printer_custom_ip&&!$printers->[$printer_idx]->{IPActiveControlDisable}){my($error1,$error2,$error3,$error4)=("","","","");
print$sock chr(16).chr(4).chr(1);
$sock->read($error1,1);
print$sock chr(16).chr(4).chr(2);
$sock->read($error2,1);
print$sock chr(16).chr(4).chr(3);
$sock->read($error3,1);
print$sock chr(16).chr(4).chr(4);
$sock->read($error4,1);
if((ord($error1)==22||ord($error1)==18)&&ord($error2)==18&&ord($error3)==18&&(ord($error4)==18||ord($error4)==30)){}else{open(F,"> $error_file");
print F"PRINTER ".$printer_idx.": PRINTER STATUS ERROR\n";
close(F);
die"PRINTER STATUS ERROR - $printer_ip:$printer_port\n";}}my$cls=$printer->get_class();
if($invert){my$seq=$cls->print_invert();
$cnt2=$seq.$cnt2;
print$sock $cnt2;}else{my$seq=$cls->print_normal();
print$sock $seq.$cnt2;}if($cfg->{LOG_POS_DEBUG}){open(F,">> $log_file");
print F"Sent to printer:\n\n";
print F$cnt2."\n";
close(F);}close($sock);
sleep 0.4;}else{if($cfg->{LOG_POS_DEBUG}){open(F,">> $log_file");
print F"ERROR IN SOCKET - 1 - $printer_ip:$printer_port\n";
close(F);}open(F,"> $error_file");
print F"PRINTER ".$printer_idx.": PRINTER STATUS ERROR\n";
close(F);
die"ERROR IN SOCKET - 1 - $printer_ip:$printer_port\n";}}elsif($printers->[$printer_idx]->{Device}){alarm 15;
if(-l$printers->[$printer_idx]->{Device}){my$file_link=readlink($printers->[$printer_idx]->{Device});
$file_link//="";
return unless$file_link;
if($file_link=~/^\/dev\//&&!-e$file_link){return;}}elsif(!-e$printers->[$printer_idx]->{Device}){return;}my$cls=$printer->get_class();
if($invert){my$seq=$cls->print_invert();
$cnt2=$seq.$cnt2;}else{my$seq=$cls->print_normal();
$cnt2=$seq.$cnt2;}$printers->[$printer_idx]->{SlowBuffer}||=0;
my$sl=0;
if($printers->[$printer_idx]->{SlowBuffer}){$sl=2;
my@t=split/,/,$printers->[$printer_idx]->{SlowBuffer};
if(scalar@t>1){$sl=$t[1];}else{$sl=$t[0];}}if(($printers->[$printer_idx]->{Device}=~/lp/&&$driver=~/TM300/)||($printers->[$printer_idx]->{SlowBuffer})){my@l=split/\n/,$cnt2;
alarm 25;
_write_slow_buffer($printers->[$printer_idx]->{Device},$sl,\@l);}else{if($cfg->{USE_TICKET_SCALE}&&$printers->[$printer_idx]->{Device}=~/doc2print/){my$r="/tmp/doc2print.txt.".$$.int(rand(10000));
open(Z,"> $r");
print Z$cnt2;
close(Z);}else{if($printers->[$printer_idx]->{SlowBuffer}){my@l=split/\n/,$cnt2;
alarm 25;
_write_slow_buffer($printers->[$printer_idx]->{Device},$sl,\@l);}else{if(-e$printers->[$printer_idx]->{Device}){if(open(F,"> ".$printers->[$printer_idx]->{Device})){print F$cnt2;
close(F);}else{print STDERR"ERROR WRITING TO: ".$printers->[$printer_idx]->{Device}." - $!\n";}}}}}}alarm 0;};
if($@){print STDERR"ERROR ALARM() PRINTING - $@\n";
return 2;}else{return;}}}else{return 2;}}sub _register_printers{my($machine,$devices,$init_hardware)=@_;
$init_hardware=1 unless defined$init_hardware;
my$config=$machine->config()->load();
$config->{STATION_NUMBER}||=0;
my$has_main=0;
my$d=$machine->dir_server()."/server/data/registered_printers/".$config->{STATION_NUMBER};
if(!-d$d){make_path($d);}&find(sub{return unless-f$File::Find::name;
unlink$File::Find::name;},($d));
foreach my $device(@$devices){my$num=$device->{num};
my$info=$device->{device};
$info->{Main}||=0;
$info->{Inactive}||=0;
if($info->{Inactive}){next;}$info->{InitSystemCommand}||="";
if($init_hardware&&$info->{InitSystemCommand}){my$device=$info->{Device};
$info->{InitSystemCommand}=~s/\@DEVICE\@/$device/g;
print STDERR"Will execute: ".$info->{InitSystemCommand}."\n";
my$res=system($info->{InitSystemCommand});
print STDERR"Result: $res\n";}$info->{Device}||="";
$info->{IP}||="";
$info->{CupsQueue}||="";
my$ip="";
my$device="";
if(!$info->{CupsQueue}){if($info->{Device}){$device=$info->{Device};}elsif($info->{IP}){$ip=$info->{IP};}else{print STDERR"ERROR!!!! Device or IP needs to be defined for printer $num...\n";}}$info->{logo1}||="";
$info->{logo2}||="";
$info->{logo3}||="";
if($info->{logo1}){my$logof="/opt/pos/common/data/".$info->{logo1};
my$o=App::POS::Hardware::PrinterLogo->new(image=>$logof);
my$cnt=$o->image_data();}if($info->{logo2}){my$logof="/opt/pos/common/data/".$info->{logo2};
my$o=App::POS::Hardware::PrinterLogo->new(image=>$logof);
my$cnt=$o->image_data();}if($info->{logo3}){my$logof="/opt/pos/common/data/".$info->{logo3};
my$o=App::POS::Hardware::PrinterLogo->new(image=>$logof);
my$cnt=$o->image_data();}open(F,"> $d/$num");
print F"Printer=$num#Device=".$device."#Description=".$info->{Description}."#IP=$ip\n";
close(F);
if($info->{Main}){$STATION_MAIN_PRINTER=$num;
$has_main=1;
open(F,"> $d/main");
print F"$num\n";
close(F);}}if(!$has_main){print STDERR">>>>>>>>>>>> NO MAIN PRINTER DEFINED!!!!!!!!!!\n";}}sub printers_monitor_group{my($daemon_name,$machine,$devices)=@_;
$machine->config()->__cache("");
$devices=$machine->config()->devices_by_type($daemon_name);
my$config=$machine->config()->load();
$config->{STATION_NUMBER}||=0;
$config->{SERIAL_LINE_FLOW_CONTROL}||="";
my$station=$config->{STATION_NUMBER};
my$register=1;
my$did_register=0;
my$init_hardware=1;
my%serial_devices_monitor=();
my%printers_in_error=();
my$has_ip_printers=0;
foreach my $device(@$devices){next unless$device->{device}->{Inactive}eq '0';
$device->{device}->{SerialMonitor}||=0;
$device->{device}->{IP}||="";
if($device->{device}->{IP}){$has_ip_printers=1;}my$error_file="/tmp/printer_error#".$device->{num}.".txt";
unlink($error_file)if-e$error_file;
if($device->{device}->{SerialMonitor}){eval{my$PortObj=Device::SerialPort->new($device->{device}->{Device});
if(defined$PortObj){$serial_devices_monitor{$device->{num}}=$PortObj;}else{print STDERR"Error (7) on ".$device->{device}->{Device}."\n";}};
if($@){print STDERR"Error (7) on ".$device->{device}->{Device}." - $@\n";}}}my$sleep_delay=$has_ip_printers?15:5;
while(1){if($machine->is_server()){if($register){print STDERR"[SERVER] Will register printers...\n";
&_register_printers($machine,$devices,$init_hardware);
$did_register=1;}$register=0;}else{my$sdir=$machine->dir_server()."/server/data/registered_printers/$station";
if(-e"/tmp/server_error.txt"){$register=1;}else{$register=1 unless-d$sdir;}if($register){print STDERR"[STATION] Will register printers...\n";
$machine->register_on_server();
&_register_printers($machine,$devices,$init_hardware);
$did_register=1;}$register=0;}if($did_register){foreach my $device(@$devices){my$num=$device->{num};
print STDERR"Will register device $num...\n";
my$info=$device->{device};
$info->{Device}||="";
next if uc($info->{Device})eq '@SCREEN@';
&_handle_printer($machine,$config,$num,$info,$config->{STATION_NUMBER});}$did_register=0;}foreach my $device(@$devices){$device->{device}->{SerialMonitor}||=0;
my$error_file="/tmp/printer_error#".$device->{num}.".txt";
if($device->{device}->{SerialMonitor}){my$PortObj=$serial_devices_monitor{$device->{num}};
my$i=&_linestatus($PortObj);
my$line_to_use='dsr';
my$custom_line_to_use=$config->{SERIAL_LINE_FLOW_CONTROL}?lc($config->{SERIAL_LINE_FLOW_CONTROL}):'';
if($custom_line_to_use&&exists$i->{$custom_line_to_use}){$line_to_use=$custom_line_to_use;}if($i->{$line_to_use}eq 'OFF'){print STDERR"PRINTER ".$device->{num}.": OFFLINE ($line_to_use)\n";
open(F,"> $error_file");
print F"PRINTER ".$device->{num}.": OFFLINE\n";
close(F);}else{if(!exists$printers_in_error{$device->{num}}){$printers_in_error{$device->{num}}=0;}my($count_in,$string_in)=$PortObj->read(10);
if($count_in==1){if(ord($string_in)==19){$printers_in_error{$device->{num}}=1;
print STDERR"PRINTER ".$device->{num}.": NOT OK\n";}elsif(ord($string_in)==17){$printers_in_error{$device->{num}}=0;
print STDERR"PRINTER ".$device->{num}.": OK\n";}else{print STDERR"PRINTER ".$device->{num}.": Unknown char read from printer...\n";}}if($printers_in_error{$device->{num}}==1){open(F,"> $error_file");
print F"PRINTER ".$device->{num}.": ERROR\n";
close(F);}else{unlink$error_file if-e$error_file;}}}elsif($device->{device}->{IP}&&$device->{device}->{Inactive}eq '0'){unlink$error_file if-e$error_file;}}sleep$sleep_delay;}}sub _handle_printer{my($machine,$config,$num,$info,$station_number)=@_;
print STDERR"> _handle_printer - $num\n";
$info->{Inactive}||=0;
if($info->{Inactive}){return;}my$dir_tmp=$machine->dir_tmp();
my$station=$config->{STATION_NUMBER};
my$d_server=$machine->dir_server();
my$dir_server_tmp=$machine->server_dir_tmp();
my$dir_server_data=$machine->server_dir_data();
if(uc($info->{Device})eq '@SCREEN@'){print STDERR"SCREEN printer, returning...\n";
return;}my$ignore_file="$dir_server_data/printer_ignore#$station#$num.txt";
my$d=$d_server."/server/data/registered_printers/".$config->{STATION_NUMBER};
my$error_station=0;
if(exists$info->{StationShowErrors}&&defined$info->{StationShowErrors}&&$info->{StationShowErrors}ne ''){$error_station=$info->{StationShowErrors};}else{$error_station=$config->{STATION_NUMBER};}my$fh=IO::File->new();
my@redirects=<$dir_server_data/printer_redirect#$station#$num#*.txt>;
if(!-e$ignore_file&&!scalar@redirects){my$timeout=5;
my$error=0;
eval{local$SIG{ALRM}=sub{die"alarm\n"};
alarm$timeout;
$info->{IP}||="";
$info->{CupsQueue}||="";
if(!$info->{IP}&&!$info->{CupsQueue}){my$device=$info->{Device}//"";
if($device ne ''&&-l$device){my$file_link=readlink($device);
$file_link//="";
if($file_link&&$file_link=~/^\/dev\//){$device=$file_link;}}if($device ne ''&&-e$device){}else{print STDERR"Device ".$info->{Device}." doesn't exists...\n";
$error=1;}}alarm 0;};
if($@||$error){print STDERR"ERROR - $@ - $error\n";
print STDERR"Can't CONNECT to printer $num in ".$info->{Device}." or ".$info->{IP}."\n";
my$f="$dir_server_tmp/printer_error#$station#$num#".$error_station.".txt";
if(open(NN,"> $f")){print NN"Printer=$num#Device=".$info->{Device}."#Description=".$info->{Description}."\n";
close(NN);}}else{print STDERR"printer in station $station with number $num is cool...\n";
my$e_file="$dir_server_tmp/printer_error#$station#$num#".$error_station.".txt";
unlink$e_file if-e$e_file;}}else{print STDERR"STATION: $station_number, PRINTER: $num - is being ignored or redirected, ignoring...\n";}$info->{Device}||="";
$info->{IP}||="";
$info->{CupsQueue}||="";
my$ip="";
my$device="";
if(!$info->{CupsQueue}){if($info->{Device}){$device=$info->{Device};}elsif($info->{IP}){$ip=$info->{IP};}else{print STDERR"ERROR!!!! Device or IP needs to be defined for printer $num...\n";}}$info->{CupsQueue}||="";
if($info->{CupsQueue}ne ''){system("/etc/init.d/cups restart");}open(F,"> $d/$num");
print F"Printer=$num#Device=".$device."#Description=".$info->{Description}."#IP=$ip\n";
close(F);
if($info->{Main}){open(F,"> $d/main");
print F"$num\n";
close(F);}undef$fh;}sub _format_string{my($w,$len)=@_;
$w=sprintf("%-".$len."s",decode("utf8",$w));
return encode("utf8",$w);}sub _printer_tags_cleanup{my($cnt_ref)=@_;
return unless defined$cnt_ref;
$$cnt_ref=~s/\@document_start\@//g;
$$cnt_ref=~s/\@document_end\@//g;
$$cnt_ref=~s/\@print_invert\@//g;
$$cnt_ref=~s/\@print_normal\@//g;
$$cnt_ref=~s/\@red\@//g;
$$cnt_ref=~s/\@bold\@//g;
$$cnt_ref=~s/\@size_small\@//g;
$$cnt_ref=~s/\@size_double\@//g;
$$cnt_ref=~s/\@size_normal\@//g;
$$cnt_ref=~s/\@size_huge\@//g;
$$cnt_ref=~s/\@underlined\@//g;
$$cnt_ref=~s/\@underlined_off\@//g;
$$cnt_ref=~s/\@line_change\@//g;
$$cnt_ref=~s/\@cashdrawer1_open\@//g;
$$cnt_ref=~s/\@cashdrawer2_open\@//g;
$$cnt_ref=~s/\@line_spacing_normal\@//g;
$$cnt_ref=~s/\@line_spacing_big\@//g;
$$cnt_ref=~s/\@paper_cut\@//g;
$$cnt_ref=~s/\@bell\@//g;
$$cnt_ref=~s/\@black\@//g;
$$cnt_ref=~s/\@justify_center\@//g;
$$cnt_ref=~s/\@justify_left\@//g;
$$cnt_ref=~s/\@justify_right\@//g;
$$cnt_ref=~s/\@logo\@//g;
$$cnt_ref=~s/\@logo1\@//g;
$$cnt_ref=~s/\@logo2\@//g;
$$cnt_ref=~s/\@logo2\@//g;}sub _printer_tags_replace{my($printer,$printer_info,$cnt_ref)=@_;
return unless defined$cnt_ref;
my$value=$printer->document_start();
$$cnt_ref=~s/\@document_start\@/$value/g if defined$value;
$value=$printer->document_end();
$$cnt_ref=~s/\@document_end\@/$value/g if defined$value;
$value=$printer->print_invert();
$$cnt_ref=~s/\@print_invert\@/$value/g if defined$value;
$value=$printer->print_normal();
$$cnt_ref=~s/\@print_normal\@/$value/g if defined$value;
$value=$printer->red();
$$cnt_ref=~s/\@red\@/$value/g if defined$value;
$value=$printer->bold();
$$cnt_ref=~s/\@bold\@/$value/g if defined$value;
$value=$printer->size_small();
$$cnt_ref=~s/\@size_small\@/$value/g if defined$value;
$value=$printer->size_double();
$$cnt_ref=~s/\@size_double\@/$value/g if defined$value;
$value=$printer->size_normal();
$$cnt_ref=~s/\@size_normal\@/$value/g if defined$value;
$value=$printer->size_huge();
$$cnt_ref=~s/\@size_huge\@/$value/g if defined$value;
$value=$printer->underlined();
$$cnt_ref=~s/\@underlined\@/$value/g if defined$value;
$value=$printer->underlined_off();
$$cnt_ref=~s/\@underlined_off\@/$value/g if defined$value;
$value=$printer->line_change();
$$cnt_ref=~s/\@line_change\@/$value/g if defined$value;
$value=$printer->cashdrawer1_open();
$$cnt_ref=~s/\@cashdrawer1_open\@/$value/g if defined$value;
$value=$printer->cashdrawer2_open();
$$cnt_ref=~s/\@cashdrawer2_open\@/$value/g if defined$value;
$value=$printer->line_spacing_normal();
$$cnt_ref=~s/\@line_spacing_normal\@/$value/g if defined$value;
$value=$printer->line_spacing_big();
$$cnt_ref=~s/\@line_spacing_big\@/$value/g if defined$value;
$value=$printer->paper_cut();
$$cnt_ref=~s/\@paper_cut\@/$value/g if defined$value;
$value=$printer->bell();
$$cnt_ref=~s/\@bell\@/$value/g if defined$value;
$value=$printer->black();
$$cnt_ref=~s/\@black\@/$value/g if defined$value;
$value=$printer->justify_center();
$$cnt_ref=~s/\@justify_center\@/$value/g if defined$value;
$value=$printer->justify_left();
$$cnt_ref=~s/\@justify_left\@/$value/g if defined$value;
$value=$printer->justify_right();
$$cnt_ref=~s/\@justify_right\@/$value/g if defined$value;
$value=$printer->logo();
$$cnt_ref=~s/\@logo\@/$value/g if defined$value;
if(defined$printer_info&&$$cnt_ref=~/\@logo1\@/){my$logo=$printer_info->{logo1}||"";
if($logo){my$logof="/opt/pos/common/data/".$logo;
if(-e"/opt/pos/common/data/".$logo){my$o=App::POS::Hardware::PrinterLogo->new(image=>$logof);
my$cnt=$o->image_data();
$cnt.=chr(0x1B).chr(0x32);
$$cnt_ref=~s/\@logo1\@/$cnt/g;}}else{$$cnt_ref=~s/\@logo1\@//g;}}if(defined$printer_info&&$$cnt_ref=~/\@logo2\@/){my$logo=$printer_info->{logo2}||"";
if($logo){my$logof="/opt/pos/common/data/".$logo;
if(-e"/opt/pos/common/data/".$logo){my$o=App::POS::Hardware::PrinterLogo->new(image=>$logof);
my$cnt=$o->image_data();
$cnt.=chr(0x1B).chr(0x32);
$$cnt_ref=~s/\@logo2\@/$cnt/g;}}else{$$cnt_ref=~s/\@logo2\@//g;}}if(defined$printer_info&&$$cnt_ref=~/\@logo3\@/){my$logo=$printer_info->{logo3}||"";
if($logo){my$logof="/opt/pos/common/data/".$logo;
if(-e"/opt/pos/common/data/".$logo){my$o=App::POS::Hardware::PrinterLogo->new(image=>$logof);
my$cnt=$o->image_data();
$cnt.=chr(0x1B).chr(0x32);
$$cnt_ref=~s/\@logo3\@/$cnt/g;}}else{$$cnt_ref=~s/\@logo3\@//g;}}$printer->tweak_content($cnt_ref);}sub _handle_request_prints{my($machine,$check_file,$mode,$hold_station,$hold_file)=@_;
my$hold_check_file=$hold_file.".check";
my$db=$machine->database();
my$cfg=$machine->config()->load();
$cfg->{STATION_PRINT_CHECKS}=exists$cfg->{STATION_PRINT_CHECKS}?$cfg->{STATION_PRINT_CHECKS}:99;
$cfg->{STATION_PRINT_CHECKS}=99 if$cfg->{STATION_PRINT_CHECKS}==$cfg->{STATION_NUMBER};
$cfg->{CHECK_GROUP_PRODUCTS_TEXT}||="";
$cfg->{PRINT_REQUESTS}||=0;
$cfg->{PRINT_VOIDS}||=0;
$cfg->{VOIDS_MONITORING}||="";
$cfg->{ORDER_SEPARATORS}||="";
$cfg->{PRINT_HISTORY_SAVE}||=0;
if($cfg->{VOIDS_MONITORING}&&!scalar keys%VOIDS_MONITORING){if(!ref($cfg->{VOIDS_MONITORING})){$cfg->{VOIDS_MONITORING}=[$cfg->{VOIDS_MONITORING}];}foreach my $rule(@{$cfg->{VOIDS_MONITORING}}){my@a=split/,/,$rule;
next unless scalar@a==2;
$a[0]=trim_string($a[0]);
$a[1]=trim_string($a[1]);
my@b=split/\-/,$a[1];
next unless scalar@b==2;
push@{$VOIDS_MONITORING{$a[0]}},\@b;}$VOIDS_MONITORING{none}=[];}my$printers=$machine->printers();
my$fsize=-s$hold_file;
my%idx_to_hold=();
my$len=-s$hold_file;
if(defined$len&&$len>2){local$/=undef;
open(F,"< $hold_file");
my$cnt=<F>;
chop($cnt);
close(F);
if($cnt=~/^IDX=/){$cnt=~s/^IDX=//;
my@t=split/;/,$cnt;
map{$idx_to_hold{$_}=1}@t;}}if(!defined$PRINTING_ZONES_CONFIG){$PRINTING_ZONES_CONFIG=$machine->printing_zones_quick();}if(!defined$PRINTING_ZONES_FILTERS){$PRINTING_ZONES_FILTERS=$machine->printing_zones_filters();}&get_clerks_information($db);
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}if(!defined$CLOSE_HOUR){my$close_hour=$cfg->{END_BUSINESS_HOUR}||"05:00";
$CLOSE_HOUR=$close_hour;
my@tmp=split/:/,$close_hour;
$CLOSE_HOUR.=":00" if scalar@tmp==2;}&get_products_information($db);
my$check_o=App::POS::Check->new(file=>$hold_check_file);
my$check=$check_o->data();
my$save_to_hdd=exists$check->{extra_data}->{save_to_hdd}?$check->{extra_data}->{save_to_hdd}:'';
$save_to_hdd=0;
$check->{header}=[]unless defined$check->{header};
$check->{details}=[]unless defined$check->{details};
unlink$hold_check_file;
if(!scalar@{$check->{header}}){print STDERR"_handle_request_prints> Check without headers, returning...\n";
return;}if(defined$check_o->table_number()&&$check_o->table_number()>90000){print STDERR"Check is above 90000 (created from other checks), won't print requests for it...\n";
return;}my$total_quantities=0;
for(@{$check->{details}}){$total_quantities+=$_->[3];}my$ex=$check_o->extra_data();
my$products_extra_data=$check_o->products_extra_data();
my$check_business_date=&date_based_on_closing_hour($check->{header}->[0],$CLOSE_HOUR);
my$cur_business_date=&date_based_on_closing_hour(&strftime("%Y-%m-%d %H:%M:%S",localtime),$CLOSE_HOUR);
$check->{header}->[8]||=0;
my$customer_name=$check->{header}->[12]||"";
my$customer_nif=$check->{header}->[13]||"";
if($check->{header}->[8]==99){return;}my$check_number=$check_o->{table_number};
my$area=$check_o->{area};
my$my_area=$area;
if(exists$check->{extra_data}->{original_area}){$my_area=$check->{extra_data}->{original_area};}$cfg->{AREA}||=[];
$cfg->{GROUP_REQUESTS_BY_PRINTER}||=0;
$cfg->{GROUP_REQUESTS_BY_PRINTER_SPECIFIC}||=[];
$cfg->{SORT_REQUESTS_BY_PRINT_PRIORITY}||=0;
$cfg->{DONT_GROUP_WITH_DECIMALS}||=0;
$cfg->{GROUPING_REQUESTED_PRODUCTS}||=0;
$cfg->{DAILY_SPECIALS_CONTROL_QUANTITIES}||=0;
$cfg->{DONT_RESTART_REQUEST_NUMBERS}||=0;
$cfg->{REQUEST_NUMBERS_REINIT_LIMIT}||=0;
$cfg->{PRODUCT_EXTRA_DATA_HIDE_ON_ORDERS}||=0;
my$grouping_request_products=$cfg->{GROUPING_REQUESTED_PRODUCTS};
my$area_grouping_method=undef;
if(scalar@{$AREAS_INFO{$my_area}}>=25){my$tmp=$AREAS_INFO{$my_area}->[24]//"";
$area_grouping_method=$tmp if$tmp ne '';}if(defined$area_grouping_method){$grouping_request_products=$area_grouping_method;}my$group_printer_specific={};
if(scalar@{$cfg->{GROUP_REQUESTS_BY_PRINTER_SPECIFIC}}&&!$cfg->{GROUP_REQUESTS_BY_PRINTER}){for(@{$cfg->{GROUP_REQUESTS_BY_PRINTER_SPECIFIC}}){my@t=split/,/,$_;
if(scalar@t==2){my($station,$printer)=($t[0],$t[1]);
$printer--;
$printer=0 if$printer<0;
$group_printer_specific->{$station}->{$printer}=1;}}}my$is_new_check=0;
my%voids_printed=();
my%to_print_request;
my%to_print_void;
my$info={};
my$products_requested=App::POS::Counters->new(db=>$db,name=>'products_requested',);
my$products_requested_req_number=App::POS::Counters->new(db=>$db,name=>'products_requested_req_number',);
my$products_voided=App::POS::Counters->new(db=>$db,name=>'products_voided',);
my$products_voided_qttys=App::POS::Counters->new(db=>$db,name=>'products_voided_qttys',);
my$request_numbers=App::POS::Counters->new(db=>$db,name=>'request_numbers',);
my$request_numbers_checks=App::POS::Counters->new(db=>$db,name=>'request_numbers_checks',);
my$doc_type=$check->{header}->[8];
if($mode eq 'finalize'&&($doc_type==2||$doc_type==13||$doc_type==14||$doc_type==15||$doc_type==33)){print STDERR"WON'T PRINT REQUESTS BECAUSE WE'RE FINALIZING A VOID\n";
return;}my$doc_types=$machine->config()->doc_types_by_id();
my$doc_types_by_name=$machine->config()->doc_types_by_name();
$doc_type=$doc_types->{$doc_type};
my$check_unique_id=$check->{header}->[-1];
my$group_products=1;
my%qttys_requested=();
my%qttys_voided=();
my$prod_idx_total=0;
my%util_grouping_by_prod=();
my$util_grouping_key="0_none";
my$util_grouping_idx=0;
my$had_group_keys=0;
my$dt_ctrl=strftime("%Y%m%d",localtime);
if($doc_type eq$cfg->{DOC_TYPE_VOID}){my$last_valid_zone="";
my$last_product_zone="";
my$menu_started=0;
for(@{$check->{details}}){next unless scalar@$_;
my$bd_key=$_->[-1];
my($dtt)=$bd_key=~/^(\d\d\d\d\d\d\d\d)/;
my$i=$PRODUCTS_INFO->{$_->[1]};
my$printing_zone=$i->{printing_zone};
if(defined$printing_zone&&$printing_zone&&$printing_zone ne '-----'){$last_product_zone=$printing_zone;}next unless$products_requested->exists($bd_key);
my$vrequested=$products_requested->cur_value($bd_key);
my$del_key=$bd_key."#".$_->[0];
next if$products_voided_qttys->exists($del_key);
next unless$vrequested>0;
my$waste_reason='';
my$waste_reason_str=my_conv($_->[13]);
my@waste_tmp=split/§/,$waste_reason_str;
if(scalar@waste_tmp==2){$_->[13]=~s/§.*?$//;
$waste_reason=$waste_tmp[0];}$util_grouping_key=$_->[8]?$_->[8]:"";
$products_voided->set_value($bd_key,$_->[3]);
$products_voided_qttys->set_value($del_key,1);
if($vrequested-$_->[3]<0){$_->[3]=$vrequested;
$products_requested->set_value($bd_key,0);}else{$vrequested-=$_->[3];
$products_requested->set_value($bd_key,$vrequested);}if($cfg->{DONT_GROUP_WITH_DECIMALS}){$_->[3]=$_->[3]*1;
if($_->[3]!~/^\d+$/){$group_products=0;}}$qttys_voided{$_->[1]}+=$_->[3];
if(exists$PRODUCTS_EXTRAS->{$_->[1]}||exists$PRODUCTS_MESSAGES->{$_->[1]}){$group_products=0;
if($printing_zone eq '-----'||!$printing_zone){$printing_zone=0;}if($last_valid_zone&&!$printing_zone){$printing_zone=$last_valid_zone;}}if(defined$CAN_DELIVER_PRODUCT_ID&&$_->[1]==$CAN_DELIVER_PRODUCT_ID){if(defined$printing_zone&&$printing_zone eq '-----'||!$printing_zone){$printing_zone=0;}if($last_valid_zone&&!$printing_zone){$printing_zone=$last_valid_zone;}if($printing_zone eq '0'){$printing_zone=$last_product_zone;}}my$name_prefix="";
if($_->[13]eq 'MENU'&&!$menu_started){$_->[13]="";}if($_->[13]eq 'MENU'&&$menu_started){$group_products=0;
$name_prefix=" ";}if(!$_->[13]&&$_->[13]ne 'EXTRA'&&$_->[13]ne 'MENU'){$last_valid_zone="";}next unless$printing_zone&&exists$PRINTING_ZONES_CONFIG->{$printing_zone};
my@notes=();
my$extra_data=exists$products_extra_data->{$_->[-1]}?$products_extra_data->{$_->[-1]}:"";
if(exists$PRODUCTS_MESSAGES->{$_->[1]}||($extra_data&&!$cfg->{PRODUCT_EXTRA_DATA_HIDE_ON_ORDERS})){$group_products=0;
@notes=split/;/,$extra_data;}if($_->[13]ne 'EXTRA'){$last_valid_zone=$printing_zone;}my$pname=$name_prefix.&shrink_words($PRODUCTS_INFO->{$_->[1]}->{name},$cfg);
my($hour_registered,$hour_voided)=split/\@/,$_->[2];
$hour_registered=~s/\:\d\d$//;
$hour_voided=~s/\:\d\d$//;
if(!$had_group_keys){$had_group_keys=$_->[8]?1:0;}my$is_product_menu=exists$PRODUCTS_MENUS->{$_->[1]}&&$_->[13]ne 'MENU'?1:0;
$menu_started=1 if$is_product_menu;
push@{$to_print_void{$printing_zone}},{product_info=>$PRODUCTS_INFO->{$_->[1]},order=>$_->[0],handy_plu=>$PRODUCTS_INFO->{$_->[1]}->{handy_plu}||'',product_menu=>$is_product_menu,product_code=>$_->[1],product_name=>$pname,quantity=>$_->[3]*1,hour=>$_->[2],hour_registered=>$hour_registered,hour_voided=>$hour_voided,price=>$_->[4],vat=>$_->[6],clerk_code=>$_->[7],group_key=>$_->[8],clerk_name=>$CLERKS_INFO->{$_->[7]}->{login}||'UNKNOWN',station=>$_->[12],station_name=>exists$STATION_NAMES->{$_->[12]}?$STATION_NAMES->{$_->[12]}:$_->[12],area=>$area,is_extra=>exists$PRODUCTS_EXTRAS->{$_->[1]}?1:0,is_menu=>$_->[13]eq 'MENU'?1:0,is_weight=>$PRODUCTS_INFO->{$_->[1]}->{is_weight},is_message=>exists$PRODUCTS_MESSAGES->{$_->[1]}?1:0,foreign=>0,key=>$bd_key,waste_reason=>$waste_reason,};}$menu_started=0;
for(@{$check->{details_deleted}}){next unless scalar@$_;
my$bd_key=$_->[-1];
my($dtt)=$bd_key=~/^(\d\d\d\d\d\d\d\d)/;
next unless$dtt eq$dt_ctrl;
my$i=$PRODUCTS_INFO->{$_->[1]};
my$printing_zone=$i->{printing_zone};
if(defined$printing_zone&&$printing_zone&&$printing_zone ne '-----'){$last_product_zone=$printing_zone;}next unless$products_requested->exists($bd_key);
my$vrequested=$products_requested->cur_value($bd_key);
my$del_key=$bd_key."#".$_->[0];
next if$products_voided_qttys->exists($del_key);
next unless$vrequested>=0;
$products_voided->set_value($bd_key,$_->[3]);
$products_voided_qttys->set_value($del_key,1);
if($vrequested-$_->[3]<0){$_->[3]=$vrequested;
$products_requested->set_value($bd_key,0);}else{my$num=$vrequested-$_->[3];
$products_requested->set_value($bd_key,$num);}$qttys_voided{$_->[1]}+=$_->[3];
my$waste_reason='';
my$waste_reason_str=my_conv($_->[13]);
my@waste_tmp=split/§/,$waste_reason_str;
if(scalar@waste_tmp==2){$_->[13]=~s/§.*?$//;
$waste_reason=$waste_tmp[0];}if(exists$PRODUCTS_EXTRAS->{$_->[1]}||exists$PRODUCTS_MESSAGES->{$_->[1]}){$group_products=0;
if($printing_zone eq '-----'||!$printing_zone){$printing_zone=0;}if($last_valid_zone&&!$printing_zone){$printing_zone=$last_valid_zone;}}if(defined$CAN_DELIVER_PRODUCT_ID&&$_->[1]==$CAN_DELIVER_PRODUCT_ID){if(defined$printing_zone&&$printing_zone eq '-----'||!$printing_zone){$printing_zone=0;}if($last_valid_zone&&!$printing_zone){$printing_zone=$last_valid_zone;}if($printing_zone eq '0'){$printing_zone=$last_product_zone;}}my$name_prefix="";
if($_->[13]eq 'MENU'&&!$menu_started){$_->[13]="";}if($_->[13]eq 'MENU'&&$menu_started){$group_products=0;
$name_prefix=" ";}if(!$_->[13]&&$_->[13]ne 'EXTRA'&&$_->[13]ne 'MENU'){$last_valid_zone="";}next unless$printing_zone&&exists$PRINTING_ZONES_CONFIG->{$printing_zone};
my@notes=();
my$extra_data=exists$products_extra_data->{$_->[-1]}?$products_extra_data->{$_->[-1]}:"";
if(exists$PRODUCTS_MESSAGES->{$_->[1]}||($extra_data&&!$cfg->{PRODUCT_EXTRA_DATA_HIDE_ON_ORDERS})){$group_products=0;
@notes=split/;/,$extra_data;}if($_->[13]ne 'EXTRA'){$last_valid_zone=$printing_zone;}my$pname=$name_prefix.&shrink_words($PRODUCTS_INFO->{$_->[1]}->{name},$cfg);
my($hour_registered,$hour_voided)=split/\@/,$_->[2];
$hour_registered=~s/\:\d\d$//;
$hour_voided=~s/\:\d\d$//;
if(!$had_group_keys){$had_group_keys=$_->[8]?1:0;}$prod_idx_total=$prod_idx_total+($_->[3]*1)unless exists$PRODUCTS_EXTRAS->{$_->[1]};
my$is_product_menu=exists$PRODUCTS_MENUS->{$_->[1]}&&$_->[13]ne 'MENU'?1:0;
$menu_started=1 if$is_product_menu;
push@{$to_print_void{$printing_zone}},{product_info=>$PRODUCTS_INFO->{$_->[1]},idx=>$prod_idx_total,order=>$_->[0],handy_plu=>$PRODUCTS_INFO->{$_->[1]}->{handy_plu}||'',product_menu=>$is_product_menu,product_code=>$_->[1],product_name=>$pname,quantity=>$_->[3]*1,hour=>$_->[2],hour_registered=>$hour_registered,hour_voided=>$hour_voided,price=>$_->[4],vat=>$_->[6],clerk_code=>$_->[7],group_key=>$_->[8],clerk_name=>$CLERKS_INFO->{$_->[7]}->{login}||'UNKNOWN',station=>$_->[12],station_name=>exists$STATION_NAMES->{$_->[12]}?$STATION_NAMES->{$_->[12]}:$_->[12],area=>$area,is_extra=>exists$PRODUCTS_EXTRAS->{$_->[1]}?1:0,is_menu=>$_->[13]eq 'MENU'?1:0,is_weight=>$PRODUCTS_INFO->{$_->[1]}->{is_weight},is_message=>exists$PRODUCTS_MESSAGES->{$_->[1]}?1:0,foreign=>0,key=>$bd_key,waste_reason=>$waste_reason,notes=>\@notes,};
if($had_group_keys){$util_grouping_by_prod{$bd_key}=$util_grouping_key;}}}else{my$idx2=0;
my$idx=0;
my$last_valid_zone="";
my$last_product_zone="";
my$menu_started=0;
my$prod_idx=0;
for(@{$check->{details}}){next unless scalar@$_;
if(scalar keys%idx_to_hold&&!exists($idx_to_hold{$idx2})){$idx2++;
next;}$idx2++;
my$bd_key=$_->[-1];
my($dtt)=$bd_key=~/^(\d\d\d\d\d\d\d\d)/;
if(exists$products_extra_data->{$bd_key}&&$products_extra_data->{$bd_key}eq 'ignore_request_print'){next;}my$i=$PRODUCTS_INFO->{$_->[1]};
my$printing_zone=$i->{printing_zone};
if(defined$printing_zone&&$printing_zone&&$printing_zone ne '-----'){$last_product_zone=$printing_zone;}if($products_requested->exists($bd_key)){next;}if($SPLIT_AREA_IDX eq$check_o->area()){print STDERR"request being made in the 'splits' area, ignoring...\n";
next;}$util_grouping_key=$_->[8]?$_->[8]:"";
$qttys_requested{$_->[1]}+=$_->[3];
if($cfg->{DONT_GROUP_WITH_DECIMALS}){$_->[3]=$_->[3]*1;
if($_->[3]!~/^\d+$/){$group_products=0;}}if(exists$PRODUCTS_EXTRAS->{$_->[1]}||exists$PRODUCTS_MESSAGES->{$_->[1]}){$group_products=0;
if($printing_zone eq '-----'||!$printing_zone){$printing_zone=0;}if($last_valid_zone&&!$printing_zone){$printing_zone=$last_valid_zone;}}if(defined$CAN_DELIVER_PRODUCT_ID&&$_->[1]==$CAN_DELIVER_PRODUCT_ID){if(defined$printing_zone&&$printing_zone eq '-----'||!$printing_zone){$printing_zone=0;}if($last_valid_zone&&!$printing_zone){$printing_zone=$last_valid_zone;}if($printing_zone eq '0'){$printing_zone=$last_product_zone;}}my$name_prefix="";
if($_->[13]eq 'MENU'&&!$menu_started){$_->[13]="";}if($_->[13]eq 'MENU'&&$menu_started){$group_products=0;
$name_prefix=" ";}if(!$_->[13]&&$_->[13]ne 'EXTRA'&&$_->[13]ne 'MENU'){$last_valid_zone="";}next unless$printing_zone&&exists$PRINTING_ZONES_CONFIG->{$printing_zone};
$products_requested->set_value($bd_key,$_->[3]);
my@notes=();
my$extra_data=exists$products_extra_data->{$_->[-1]}?$products_extra_data->{$_->[-1]}:"";
if(exists$PRODUCTS_MESSAGES->{$_->[1]}||($extra_data&&!$cfg->{PRODUCT_EXTRA_DATA_HIDE_ON_ORDERS})){$group_products=0;
@notes=split/;/,$extra_data;}if($_->[13]ne 'EXTRA'){$last_valid_zone=$printing_zone;}my$pname=$name_prefix.&shrink_words($PRODUCTS_INFO->{$_->[1]}->{name},$cfg);
if(!$had_group_keys){$had_group_keys=$_->[8]?1:0;}$prod_idx_total=$prod_idx_total+($_->[3]*1)unless exists$PRODUCTS_EXTRAS->{$_->[1]};
my$is_product_menu=exists$PRODUCTS_MENUS->{$_->[1]}&&$_->[13]ne 'MENU'?1:0;
$menu_started=1 if$is_product_menu;
push@{$to_print_request{$printing_zone}},{product_info=>$PRODUCTS_INFO->{$_->[1]},idx=>$prod_idx_total,order=>$_->[0],handy_plu=>$PRODUCTS_INFO->{$_->[1]}->{handy_plu}||'',product_menu=>$is_product_menu,product_code=>$_->[1],product_name=>$pname,quantity=>$_->[3]*1,hour=>$_->[2],price=>$_->[4],vat=>$_->[6],clerk_code=>$_->[7],group_key=>$_->[8],clerk_name=>$CLERKS_INFO->{$_->[7]}->{login}||'UNKNOWN',station=>$_->[12],station_name=>exists$STATION_NAMES->{$_->[12]}?$STATION_NAMES->{$_->[12]}:$_->[12],area=>$area,is_extra=>exists$PRODUCTS_EXTRAS->{$_->[1]}?1:0,is_menu=>$_->[13]eq 'MENU'?1:0,is_weight=>$PRODUCTS_INFO->{$_->[1]}->{is_weight},is_message=>exists$PRODUCTS_MESSAGES->{$_->[1]}?1:0,notes=>\@notes,foreign=>0,key=>$bd_key,};
if($had_group_keys){$util_grouping_by_prod{$bd_key}=$util_grouping_key;}$idx++;}$menu_started=0;
for(@{$check->{details_deleted}}){next unless scalar@$_;
my$bd_key=$_->[-1];
my($dtt)=$bd_key=~/^(\d\d\d\d\d\d\d\d)/;
next unless$dtt eq$dt_ctrl;
my$i=$PRODUCTS_INFO->{$_->[1]};
my$printing_zone=$i->{printing_zone};
if(defined$printing_zone&&$printing_zone&&$printing_zone ne '-----'){$last_product_zone=$printing_zone;}next unless$products_requested->exists($bd_key);
my$vrequested=$products_requested->cur_value($bd_key);
my$del_key=$bd_key."#".$_->[0];
next if$products_voided_qttys->exists($del_key);
next unless$vrequested>0;
my$waste_reason='';
my$waste_reason_str=my_conv($_->[13]);
my@waste_tmp=split/§/,$waste_reason_str;
if(scalar@waste_tmp==2){$_->[13]=~s/§.*?$//;
$waste_reason=$waste_tmp[0];}$util_grouping_key=$_->[8]?$_->[8]:"";
$products_voided->set_value($bd_key,$_->[3]);
$products_voided_qttys->set_value($del_key,1);
if($vrequested-$_->[3]<0){$_->[3]=$vrequested;
$products_requested->set_value($bd_key,0);}else{my$num=$vrequested-$_->[3];
$products_requested->set_value($bd_key,$num);}$qttys_voided{$_->[1]}+=$_->[3];
if(exists$PRODUCTS_EXTRAS->{$_->[1]}||exists$PRODUCTS_MESSAGES->{$_->[1]}){$group_products=0;
if($printing_zone eq '-----'||!$printing_zone){$printing_zone=0;}if($last_valid_zone&&!$printing_zone){$printing_zone=$last_valid_zone;}}if(defined$CAN_DELIVER_PRODUCT_ID&&$_->[1]==$CAN_DELIVER_PRODUCT_ID){if(defined$printing_zone&&$printing_zone eq '-----'||!$printing_zone){$printing_zone=0;}if($last_valid_zone&&!$printing_zone){$printing_zone=$last_valid_zone;}if($printing_zone eq '0'){$printing_zone=$last_product_zone;}}my$name_prefix="";
if($_->[13]eq 'MENU'&&!$menu_started){$_->[13]="";}if($_->[13]eq 'MENU'&&$menu_started){$group_products=0;
$name_prefix=" ";}if(!$_->[13]&&$_->[13]ne 'EXTRA'&&$_->[13]ne 'MENU'){$last_valid_zone="";}next unless$printing_zone&&exists$PRINTING_ZONES_CONFIG->{$printing_zone};
my@notes=();
my$extra_data=exists$products_extra_data->{$_->[-1]}?$products_extra_data->{$_->[-1]}:"";
if(exists$PRODUCTS_MESSAGES->{$_->[1]}||($extra_data&&!$cfg->{PRODUCT_EXTRA_DATA_HIDE_ON_ORDERS})){$group_products=0;
@notes=split/;/,$extra_data;}if($_->[13]ne 'EXTRA'){$last_valid_zone=$printing_zone;}my$pname=$name_prefix.&shrink_words($PRODUCTS_INFO->{$_->[1]}->{name},$cfg);
my($hour_registered,$hour_voided)=split/\@/,$_->[2];
$hour_registered=~s/\:\d\d$//;
$hour_voided=~s/\:\d\d$//;
if(!$had_group_keys){$had_group_keys=$_->[8]?1:0;}$prod_idx_total=$prod_idx_total+($_->[3]*1)unless exists$PRODUCTS_EXTRAS->{$_->[1]};
my$is_product_menu=exists$PRODUCTS_MENUS->{$_->[1]}&&$_->[13]ne 'MENU'?1:0;
$menu_started=1 if$is_product_menu;
push@{$to_print_void{$printing_zone}},{product_info=>$PRODUCTS_INFO->{$_->[1]},idx=>$prod_idx_total,order=>$_->[0],handy_plu=>$PRODUCTS_INFO->{$_->[1]}->{handy_plu}||'',product_menu=>$is_product_menu,product_code=>$_->[1],product_name=>$pname,quantity=>$_->[3]*1,hour=>$_->[2],hour_registered=>$hour_registered,hour_voided=>$hour_voided,price=>$_->[4],vat=>$_->[6],clerk_code=>$_->[7],group_key=>$_->[8],clerk_name=>$CLERKS_INFO->{$_->[7]}->{login}||'UNKNOWN',station=>$_->[12],station_name=>exists$STATION_NAMES->{$_->[12]}?$STATION_NAMES->{$_->[12]}:$_->[12],area=>$area,is_extra=>exists$PRODUCTS_EXTRAS->{$_->[1]}?1:0,is_menu=>$_->[13]eq 'MENU'?1:0,is_weight=>$PRODUCTS_INFO->{$_->[1]}->{is_weight},is_message=>exists$PRODUCTS_MESSAGES->{$_->[1]}?1:0,foreign=>0,key=>$bd_key,waste_reason=>$waste_reason,notes=>\@notes,};
if($had_group_keys){$util_grouping_by_prod{$bd_key}=$util_grouping_key;}}}$cfg->{GROUP_BY_PRODUCT_UTIL_NAME}=$had_group_keys;
$cfg->{PRINTING_ZONE_LINK}||=[];
$cfg->{PRINTING_ZONE_LINK_NAME_ONLY}||=0;
my$link_info={};
foreach my $l(@{$cfg->{PRINTING_ZONE_LINK}}){my@t=split/,/,$l;
if(scalar@t==2){$link_info->{$t[0]}->{$t[1]}=1;}}if(scalar keys%to_print_request&&scalar keys%$link_info){foreach my $k(keys%$link_info){my@keys=keys%{$link_info->{$k}};
foreach my $kk(@keys){&_slurp_printing_zone(\%to_print_request,$kk,$k);}}}if(scalar keys%to_print_void&&scalar keys%$link_info){foreach my $k(keys%$link_info){my($tmp1,$tmp2)=each%{$link_info->{$k}};
&_slurp_printing_zone(\%to_print_void,$tmp1,$k);}}my$zones_by_name=$machine->config->printing_zones_by_name();
if(!scalar keys%to_print_request&&!scalar%to_print_void){return;}my$last_print_priority=0;
if($cfg->{PRINT_REQUESTS}&&defined$check_number){my$check_number_map=exists$TABLES_TO_CARDS{$check_number}?$TABLES_TO_CARDS{$check_number}:$check_number;
my$req_key=$cfg->{DONT_RESTART_REQUEST_NUMBERS}?'requests':$check_o->business_date();
if($cfg->{REQUEST_NUMBERS_REINIT_LIMIT}&&$request_numbers->cur_value($req_key)>=$cfg->{REQUEST_NUMBERS_REINIT_LIMIT}){$request_numbers->set_value($req_key,1);}else{if(!$request_numbers->exists($req_key)){$request_numbers->set_value($req_key,1);}else{$request_numbers->increment($req_key);}}my@prod_final=();
my$area_price=$AREAS_INFO{$my_area}->[2]||"";
if($area_price){$area_price=~s/sell_price//;}my%info=(in_payment=>exists$ex->{in_payment}?$ex->{in_payment}:'',foreign_dont_list_products=>$cfg->{PRINTING_ZONE_LINK_NAME_ONLY},clerk_open=>$CLERKS_INFO->{$check->{header}->[2]}->{login},clerk_close=>$CLERKS_INFO->{$check->{header}->[3]}->{login},customer_name=>$customer_name,customer_nif=>$customer_nif,custom_table_number=>$ex->{custom_table_number},area_price=>$area_price,area=>$my_area,area_name=>$AREAS_INFO{$my_area}->[0],area_tables=>$AREAS_INFO{$my_area}->[1],check_number=>$check_number,check_number_map=>$check_number_map,datetime=>&strftime("%Y-%m-%d %H:%M:%S",localtime),date=>&strftime("%Y-%m-%d",localtime),hour=>&strftime("%H:%M:%S",localtime),request_number=>$request_numbers->cur_value($req_key),persons_number=>$check->{header}->[15]||0,total_quantities=>$total_quantities,extra_data=>$check->{extra_data},idx_total=>$prod_idx_total,);
$machine->get_customer_info($db,$check->{header}->[11],\%info)if$check->{header}->[11];
$check->{extra_data}->{queue_name}||="";
if($check->{extra_data}->{queue_name}){$info{queue_number}=_read_queue_number($machine,$check->{extra_data}->{queue_name});}my$name_file=$machine->dir_server()."/server/data/areas/$area/$check_number.name";
if(-e$name_file){local$/=undef;
open(F,"< ".$name_file);
my$cnt=<F>;
chop($cnt);
close(F);
$cnt=~s/\n//g;
$info{check_name}=$cnt;}$info{check_name}||="";
if(!$info{check_name}){$info{table_desc}=$check_number;}else{if($info{check_name}=~/^\d+$/){$info{table_desc}=$info{check_name};}else{$info{table_desc}=$check_number." (".$info{check_name}.")";}}$info{table_desc}=~s/<br>/ /g;
my$v1=$request_numbers->cur_value($req_key);
if($request_numbers_checks->exists($check_unique_id)){my$v2=$request_numbers_checks->cur_value($check_unique_id);
$v2.=",".$v1;
$request_numbers_checks->set_value($check_unique_id,$v2);}else{$request_numbers_checks->set_value($check_unique_id,$v1);}my%group_by_printer=();
my%group_by_pzone=();
my$have_printing_priority=0;
foreach my $zone(keys%to_print_request){my$printers=$PRINTING_ZONES_CONFIG->{$zone};
my$products=$to_print_request{$zone};
my%i=();
my@prod=();
my$seat_idx=0;
my$seats=$check_o->{data}->{extra_data}->{seats};
$seats||="";
my@seats=split/#/,$seats;
if($grouping_request_products){if($grouping_request_products==1){$group_products=1;}elsif($grouping_request_products==2||$grouping_request_products==4){$group_products=0;}}if(scalar keys%util_grouping_by_prod){$group_products=0;}if(!$group_products){my$idx=0;
foreach my $p(sort{$a->{order}<=>$b->{order}}@$products){$products_requested_req_number->set_value($p->{key},$info{request_number});
my$seats_str="";
$seat_idx=$p->{order};
if(scalar@seats&&$p->{order}<=scalar(@seats)){$seats_str=$seats[$seat_idx];}my$id=$p->{product_code};
my$printing_priority=exists$PRINTING_PRIORITIES->{$id}?$PRINTING_PRIORITIES->{$id}:0;
$have_printing_priority=1 if$printing_priority;
if(!$p->{is_extra}){$last_print_priority=$printing_priority;}else{$printing_priority=0;
$printing_priority=$last_print_priority unless$printing_priority;}$p->{quantity}||=1;
if($grouping_request_products==4&&$p->{quantity}>1&&$p->{quantity}!~/\./){for(my$i=0;$i<$p->{quantity};$i++){$idx++ if!$p->{is_message}&&!$p->{is_extra};
push@prod,{product_info=>$p->{product_info},idx=>$idx,seats=>$seats_str,foreign=>$p->{foreign},foreign_orig=>$p->{foreign_orig},quantity=>1,hour=>$p->{hour},station=>$p->{station},station_name=>exists$STATION_NAMES->{$p->{station}}?$STATION_NAMES->{$p->{station}}:$p->{station},area=>$p->{area},vat=>0,price=>0,clerk_name=>$p->{clerk_name},clerk_code=>$p->{clerk_code},group_key=>$p->{group_key},product_code=>$p->{product_code},product_name=>$p->{product_name},product_menu=>$p->{product_menu},handy_plu=>$p->{handy_plu},is_extra=>$p->{is_extra},is_menu=>$p->{is_menu},is_message=>$p->{is_message},is_weight=>$p->{is_weight},key=>$p->{key},printing_priority=>$printing_priority,order=>$p->{order},};}}else{if(!exists$PRODUCTS_MESSAGES->{$p->{product_code}}){$idx++ if!$p->{is_message}&&!$p->{is_extra};
push@prod,{product_info=>$p->{product_info},idx=>$idx,seats=>$seats_str,foreign=>$p->{foreign},foreign_orig=>$p->{foreign_orig},quantity=>$p->{quantity},hour=>$p->{hour},station=>$p->{station},station_name=>exists$STATION_NAMES->{$p->{station}}?$STATION_NAMES->{$p->{station}}:$p->{station},area=>$p->{area},vat=>0,price=>0,clerk_name=>$p->{clerk_name},clerk_code=>$p->{clerk_code},group_key=>$p->{group_key},product_code=>$p->{product_code},product_name=>$p->{product_name},product_menu=>$p->{product_menu},handy_plu=>$p->{handy_plu},is_extra=>$p->{is_extra},is_menu=>$p->{is_menu},is_message=>$p->{is_message},is_weight=>$p->{is_weight},key=>$p->{key},printing_priority=>$printing_priority,order=>$p->{order},};}}if(scalar@{$p->{notes}}){my$c=1;
foreach my $note(@{$p->{notes}}){push@prod,{key=>$p->{key}.$c++,foreign=>$p->{foreign},foreign_orig=>$p->{foreign_orig},quantity=>1,hour=>$p->{hour},station=>$p->{station},station_name=>exists$STATION_NAMES->{$p->{station}}?$STATION_NAMES->{$p->{station}}:$p->{station},area=>$p->{area},vat=>0,price=>0,clerk_name=>$p->{clerk_name},clerk_code=>$p->{clerk_code},group_key=>$p->{group_key},product_code=>"",product_name=>$note,is_extra=>0,is_message=>exists$PRODUCTS_MESSAGES->{$p->{product_code}}?1:0,is_note=>1,order=>"10".$p->{order},};}}}}else{foreach my $p(reverse sort{$a->{order}<=>$b->{order}}@$products){$products_requested_req_number->set_value($p->{key},$info{request_number});
my$id=$p->{product_code};
my$printing_priority=exists$PRINTING_PRIORITIES->{$id}?$PRINTING_PRIORITIES->{$id}:0;
$have_printing_priority=1 if$printing_priority;
if(!$p->{is_extra}){$last_print_priority=$printing_priority;}else{$printing_priority=0;
$printing_priority=$last_print_priority unless$printing_priority;}$seat_idx=$p->{order};
if(scalar@seats&&$p->{order}<=scalar(@seats)){if(exists$i{$p->{product_code}}->{seats}){$i{$p->{product_code}}->{seats}=$i{$p->{product_code}}->{seats}.",".$seats[$seat_idx]if defined$seats[$seat_idx];}else{$i{$p->{product_code}}->{seats}=$seats[$seat_idx];}}if(exists$PRODUCTS_MESSAGES->{$p->{product_code}}){if(exists$p->{notes}&&scalar@{$p->{notes}}){$p->{product_name}=$p->{notes}->[0];}}$i{$p->{product_code}}->{idx}=$p->{idx};
$i{$p->{product_code}}->{product_info}=$p->{product_info};
$i{$p->{product_code}}->{quantity}+=$p->{quantity};
$i{$p->{product_code}}->{hour}=$p->{hour};
$i{$p->{product_code}}->{is_weight}=$p->{is_weight};
$i{$p->{product_code}}->{station}=$p->{station};
$i{$p->{product_code}}->{station_name}=exists$STATION_NAMES->{$p->{station}}?$STATION_NAMES->{$p->{station}}:$p->{station};
$i{$p->{product_code}}->{area}=$p->{area};
$i{$p->{product_code}}->{vat}=0;
$i{$p->{product_code}}->{price}=0;
$i{$p->{product_code}}->{foreign}=$p->{foreign};
$i{$p->{product_code}}->{foreign_orig}=$p->{foreign_orig};
$i{$p->{product_code}}->{clerk_name}=$p->{clerk_name};
$i{$p->{product_code}}->{clerk_code}=$p->{clerk_code};
$i{$p->{product_code}}->{product_code}=$p->{product_code};
$i{$p->{product_code}}->{product_menu}=$p->{product_menu};
$i{$p->{product_code}}->{product_name}=$p->{product_name};
$i{$p->{product_code}}->{handy_plu}=$p->{handy_plu};
$i{$p->{product_code}}->{order}=$p->{order};
$i{$p->{product_code}}->{key}=$p->{key};
$i{$p->{product_code}}->{printing_priority}=$printing_priority;
$i{$p->{product_code}}->{is_extra}=$p->{is_extra};
$i{$p->{product_code}}->{is_menu}=$p->{is_menu};}@prod=values%i;
@prod=sort{$a->{order}<=>$b->{order}}@prod;}my$a_tmp=$area+1;
my$c=0;
foreach my $printer(@$printers){my($station,$num)=split/#/,$printer;
my$area_tmp=$area+1;
my$station_tmp=$station+1;
my$copies=1;
if(exists$PRINTING_ZONES_FILTERS->{$zone}->{$c}){my$filter=$PRINTING_ZONES_FILTERS->{$zone}->{$c};
$copies=$PRINTING_ZONES_FILTERS->{$zone}->{Copies}->[$c];
if(defined$filter->{FromStation}&&$filter->{FromStation}ne ''&&$filter->{FromStation}!=$hold_station){if($hold_station>80&&$filter->{FromStation}eq '99'){}else{$c++;
next;}}if(defined$filter->{FromArea}&&$filter->{FromArea}ne ''&&$filter->{FromArea}!=$area_tmp){$c++;
next;}}my$rule_number=$c+1;
foreach my $rec(@prod){$rec->{printing_zone}=$zone;
push@{$group_by_printer{$station}->{$num-1}->{$copies}},$rec;
push@{$group_by_pzone{$zone}->{$station}->{$num-1}->{$copies}},$rec;}$c++;}}if($cfg->{GROUP_REQUESTS_BY_PRINTER}||scalar keys%$group_printer_specific){my%to_print=();
foreach my $station(keys%group_by_printer){foreach my $num(keys%{$group_by_printer{$station}}){foreach my $copies(keys%{$group_by_printer{$station}->{$num}}){if(!$cfg->{GROUP_REQUESTS_BY_PRINTER}&&scalar keys%$group_printer_specific){next unless exists$group_printer_specific->{$station}->{$num}}my$prods=$group_by_printer{$station}->{$num}->{$copies};
$info{products}=$prods;
my$individual_products=$grouping_request_products==3||$grouping_request_products==4?1:0;
if($individual_products){my@prints=();
my$cur=[];
my$product_idx=1;
for(reverse@$prods){if($_->{is_extra}){push@$cur,$_;}else{push@$cur,$_;
push@prints,\@{$cur};
$cur=[];}}my$print_count=1;
foreach my $print(@prints){my@p=reverse@$print;
my$template='products_request.tmpl';
my$pzone="";
$pzone=$p[0]->{printing_zone}if scalar@p;
if($pzone){my$pi=$machine->config()->printers_info();
my$printer_num=$num+1;
my$area_num=$area+1;
$pi->{$pzone}->{$station}->{$printer_num}->{Template}||="";
$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}||="";
if($pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}){my$key1=$area_num."#".$hold_station;
my$key2=$area_num."#";
my$key3="#".$hold_station;
my$key4="";
my$key5="";
if($hold_station>80){$key4=$area_num."#99";
$key5="#99";}if(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1};}elsif(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2};}elsif(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3};}elsif($key4&&exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4};}elsif($key5&&exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5};}}elsif($pi->{$pzone}->{$station}->{$printer_num}->{Template}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{Template};}}$info{products}||=[];
my@final=();
my%did_keys=();
my$check_has_menus=0;
for(sort{$a->{foreign}<=>$b->{foreign}}@{$info{products}}){next if exists$did_keys{$_->{key}};
push@final,$_;
$did_keys{$_->{key}}=1;
$check_has_menus=1 if exists$PRODUCTS_MENUS->{$_->{product_code}};}$info{products}=\@final;
$info{operation}="request";
$info{products}=\@p;
$info{products}=_group_products_by_util_name($prods)if$cfg->{GROUP_BY_PRODUCT_UTIL_NAME};
if($cfg->{SORT_REQUESTS_BY_PRINT_PRIORITY}&&$have_printing_priority&&!$check_has_menus){@{$info{products}}=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@{$info{products}};}$info{print_count}=$print_count;
$info{print_total}=scalar@prints;
my$content="";
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template,\%info,\$content)||die$TEMPLATE->error;
if($save_to_hdd){my$printer=App::POS::Printer->new(driver=>'EpsonTM88');
my$p_class=$printer->get_class();
_printer_tags_replace($p_class,undef,\$content);
my$file=$machine->dir_server().'/server/data/tmp/'.$save_to_hdd.'.order';
append_file($file,$content);}my$archive_dir=$machine->dir_server().'/server/data/order_archive/'.$station."/".$num;
&make_path($archive_dir)unless-d$archive_dir;
my$archived=0;
my$zone="";
if(scalar@$prods){$zone=$prods->[0]->{printing_zone};}for(my$i=0;$i<$copies;$i++){my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station."txt";
if(!$archived++){if($cfg->{PRINT_HISTORY_SAVE}){open(F,"> $archive_dir/$fname");
print F$content;
close(F);}}$to_print{$station}->{$num}.=$content;}$print_count++;}}else{$info{products}=$prods;
my$pzone="";
$pzone=scalar@$prods?$prods->[0]->{printing_zone}:"";
my$template='products_request.tmpl';
if($pzone){my$pi=$machine->config()->printers_info();
my$printer_num=$num+1;
my$area_num=$area+1;
$pi->{$pzone}->{$station}->{$printer_num}->{Template}||="";
$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}||="";
if($pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}){my$key1=$area_num."#".$hold_station;
my$key2=$area_num."#";
my$key3="#".$hold_station;
my$key4="";
my$key5="";
if($hold_station>80){$key4=$area_num."#99";
$key5="#99";}if(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1};}elsif(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2};}elsif(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3};}elsif($key4&&exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4};}elsif($key5&&exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5};}}elsif($pi->{$pzone}->{$station}->{$printer_num}->{Template}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{Template};}}$info{products}||=[];
my@final=();
my%did_keys=();
my$check_has_menus=0;
for(sort{$a->{foreign}<=>$b->{foreign}}@{$info{products}}){next if exists$did_keys{$_->{key}};
push@final,$_;
$did_keys{$_->{key}}=1;
$check_has_menus=1 if exists$PRODUCTS_MENUS->{$_->{product_code}};}$info{products}=\@final;
$info{operation}="request";
$info{products}=_group_products_by_util_name($info{products})if$cfg->{GROUP_BY_PRODUCT_UTIL_NAME};
if($grouping_request_products==2){if(exists$group_printer_specific->{$station}->{$num}){$info{products}=_group_products($info{products});}}if($cfg->{SORT_REQUESTS_BY_PRINT_PRIORITY}&&$have_printing_priority&&!$check_has_menus){@{$info{products}}=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@{$info{products}};}$info{print_count}=1;
$info{print_total}=1;
my$content="";
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template,\%info,\$content)||die$TEMPLATE->error;
if($save_to_hdd){my$printer=App::POS::Printer->new(driver=>'EpsonTM88');
my$p_class=$printer->get_class();
_printer_tags_replace($p_class,undef,\$content);
my$file=$machine->dir_server().'/server/data/tmp/'.$save_to_hdd.'.order';
append_file($file,$content);}my$archive_dir=$machine->dir_server().'/server/data/order_archive/'.$station."/".$num;
&make_path($archive_dir)unless-d$archive_dir;
my$archived=0;
my$zone="";
if(scalar@$prods){$zone=$prods->[0]->{printing_zone};}for(my$i=0;$i<$copies;$i++){my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
if(!$archived++){if($cfg->{PRINT_HISTORY_SAVE}){open(F,"> $archive_dir/$fname");
print F$content;
close(F);}}$to_print{$station}->{$num}.=$content;}}}}}if(scalar keys%to_print){my$barcode="";
$cfg->{PRINT_BARCODE_IN_ORDER}//=0;
if($cfg->{PRINT_BARCODE_IN_ORDER}){if($cfg->{PRINT_BARCODE_IN_ORDER}==1){$barcode=$info{request_number};}elsif($cfg->{PRINT_BARCODE_IN_ORDER}==2){$barcode=$info{check_number};}}foreach my $station(keys%to_print){foreach my $num(keys%{$to_print{$station}}){my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
my$f=$machine->server_dir_tmp()."/".$fname;
if($barcode){print STDERR"### barcode1\n";
my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$f;
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,$barcode,5);}_add_custom_top_and_bottom($machine,$check->{header}->[6],\$to_print{$station}->{$num});
open(F,"> $f");
print F$to_print{$station}->{$num};
close(F);}}}}if(!$cfg->{GROUP_REQUESTS_BY_PRINTER}||scalar keys%$group_printer_specific){my%to_print=();
foreach my $zone(keys%group_by_pzone){foreach my $station(keys%{$group_by_pzone{$zone}}){foreach my $num(keys%{$group_by_pzone{$zone}->{$station}}){foreach my $copies(keys%{$group_by_pzone{$zone}->{$station}->{$num}}){if(scalar keys%$group_printer_specific){next if exists$group_printer_specific->{$station}->{$num}}my$prods=$group_by_pzone{$zone}->{$station}->{$num}->{$copies};
$info{products}=$prods;
my$individual_products=$grouping_request_products==3||$grouping_request_products==4?1:0;
if($individual_products){my@prints=();
my$cur=[];
my$product_idx=1;
for(reverse@$prods){if($_->{is_extra}){push@$cur,$_;}else{push@$cur,$_;
push@prints,\@{$cur};
$cur=[];}}my$print_count=1;
foreach my $print(@prints){my@p=reverse@$print;
my$template='products_request.tmpl';
my$pi=$machine->config()->printers_info();
my$printer_num=$num+1;
my$area_num=$area+1;
$pi->{$zone}->{$station}->{$printer_num}->{Template}||="";
$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}||="";
if($pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}){my$key1=$area_num."#".$hold_station;
my$key2=$area_num."#";
my$key3="#".$hold_station;
my$key4="";
my$key5="";
if($hold_station>80){$key4=$area_num."#99";
$key5="#99";}if(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1};}elsif(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2};}elsif(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3};}elsif($key4&&exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4};}elsif($key5&&exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5};}}elsif($pi->{$zone}->{$station}->{$printer_num}->{Template}){$template=$pi->{$zone}->{$station}->{$printer_num}->{Template};}my@final=();
my%did_keys=();
my$check_has_menus=0;
for(sort{$a->{foreign}<=>$b->{foreign}}@p){next if exists$did_keys{$_->{key}};
push@final,$_;
$check_has_menus=1 if exists$PRODUCTS_MENUS->{$_->{product_code}};
$did_keys{$_->{key}}=1;}@p=@final;
$info{operation}="request";
$info{products}=\@p;
my$prods=$info{products};
$info{products}=_group_products_by_util_name($prods)if$cfg->{GROUP_BY_PRODUCT_UTIL_NAME};
if($cfg->{SORT_REQUESTS_BY_PRINT_PRIORITY}&&$have_printing_priority&&!$check_has_menus){@{$info{products}}=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@{$info{products}};}$info{print_count}=$print_count;
$info{print_total}=scalar@prints;
my$content="";
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template,\%info,\$content)||die$TEMPLATE->error;
if($save_to_hdd){my$printer=App::POS::Printer->new(driver=>'EpsonTM88');
my$p_class=$printer->get_class();
_printer_tags_replace($p_class,undef,\$content);
my$file=$machine->dir_server().'/server/data/tmp/'.$save_to_hdd.'.order';
append_file($file,$content);}my$archive_dir=$machine->dir_server().'/server/data/order_archive/'.$station."/".$num;
&make_path($archive_dir)unless-d$archive_dir;
my$archived=0;
my$zone="";
if(scalar@$prods){$zone=$prods->[0]->{printing_zone};}for(my$i=0;$i<$copies;$i++){my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
if(!$archived++){if($cfg->{PRINT_HISTORY_SAVE}){open(F,"> $archive_dir/$fname");
print F$content;
close(F);}}$to_print{$station}->{$num}.=$content;}$print_count++;}}else{$info{products}=_group_products_by_util_name($prods)if$cfg->{GROUP_BY_PRODUCT_UTIL_NAME};
my$check_has_menus=0;
for(@{$info{products}}){if(exists$PRODUCTS_MENUS->{$_->{product_code}}){$check_has_menus=1;
last;}}if($cfg->{SORT_REQUESTS_BY_PRINT_PRIORITY}&&$have_printing_priority&&!$check_has_menus){@{$info{products}}=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@{$info{products}};}my$template='products_request.tmpl';
my$pi=$machine->config()->printers_info();
my$printer_num=$num+1;
my$area_num=$area+1;
$pi->{$zone}->{$station}->{$printer_num}->{Template}||="";
$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}||="";
if($pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}){my$key1=$area_num."#".$hold_station;
my$key2=$area_num."#";
my$key3="#".$hold_station;
my$key4="";
my$key5="";
if($hold_station>80){$key4=$area_num."#99";
$key5="#99";}if(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1};}elsif(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2};}elsif(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3};}elsif($key4&&exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4};}elsif($key5&&exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5};}}elsif($pi->{$zone}->{$station}->{$printer_num}->{Template}){$template=$pi->{$zone}->{$station}->{$printer_num}->{Template};}$info{products}||=[];
my@final=();
my%did_keys=();
for(sort{$a->{foreign}<=>$b->{foreign}}@{$info{products}}){next if exists$did_keys{$_->{key}};
push@final,$_;
$did_keys{$_->{key}}=1;}$info{products}=\@final;
$info{operation}="request";
$info{print_count}=1;
$info{print_total}=1;
my$content="";
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template,\%info,\$content)||die$TEMPLATE->error;
if($save_to_hdd){my$printer=App::POS::Printer->new(driver=>'EpsonTM88');
my$p_class=$printer->get_class();
_printer_tags_replace($p_class,undef,\$content);
my$file=$machine->dir_server().'/server/data/tmp/'.$save_to_hdd.'.order';
append_file($file,$content);}my$archive_dir=$machine->dir_server().'/server/data/order_archive/'.$station."/".$num;
&make_path($archive_dir)unless-d$archive_dir;
my$archived=0;
my$zone="";
if(scalar@$prods){$zone=$prods->[0]->{printing_zone};}for(my$i=0;$i<$copies;$i++){my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
if(!$archived++){if($cfg->{PRINT_HISTORY_SAVE}){open(F,"> $archive_dir/$fname");
print F$content;
close(F);}}$to_print{$station}->{$num}.=$content;}}}}}}if(scalar keys%to_print){my$barcode="";
$cfg->{PRINT_BARCODE_IN_ORDER}//=0;
if($cfg->{PRINT_BARCODE_IN_ORDER}){if($cfg->{PRINT_BARCODE_IN_ORDER}==1){$barcode=$info{request_number};}elsif($cfg->{PRINT_BARCODE_IN_ORDER}==2){$barcode=$info{check_number};}}foreach my $station(keys%to_print){foreach my $num(keys%{$to_print{$station}}){my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
my$f=$machine->server_dir_tmp()."/".$fname;
if($barcode){my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$f;
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,$barcode,5);}_add_custom_top_and_bottom($machine,$check->{header}->[6],\$to_print{$station}->{$num});
open(F,"> $f");
print F$to_print{$station}->{$num};
close(F);}}}}}else{print STDERR"PRINT_REQUESTS IN config.ini IS TURNED OFF...\n";}if($cfg->{PRINT_VOIDS}&&defined$check_number&&$check_number!~/^0\./){my$request_number=$request_numbers_checks->cur_value($check_unique_id);
my$check_number_map=exists$TABLES_TO_CARDS{$check_number}?$TABLES_TO_CARDS{$check_number}:$check_number;
my$area_price=$AREAS_INFO{$my_area}->[2]||"";
if($area_price){$area_price=~s/sell_price//;}if($request_number=~/,/){my@aa=split/,/,$request_number;
$request_number=$aa[-1];}my%info=(in_payment=>exists$ex->{in_payment}?$ex->{in_payment}:'',foreign_dont_list_products=>$cfg->{PRINTING_ZONE_LINK_NAME_ONLY},clerk_open=>$CLERKS_INFO->{$check->{header}->[2]}->{login},clerk_close=>$CLERKS_INFO->{$check->{header}->[3]}->{login},customer_name=>$customer_name,customer_nif=>$customer_nif,custom_table_number=>$ex->{custom_table_number},area_price=>$area_price,area=>$my_area,area_name=>$AREAS_INFO{$my_area}->[0],area_tables=>$AREAS_INFO{$my_area}->[1],persons_number=>$check->{header}->[15]||0,check_number=>$check_number,check_number_map=>$check_number_map,request_number=>$request_number,datetime=>&strftime("%Y-%m-%d %H:%M:%S",localtime),date=>&strftime("%Y-%m-%d",localtime),hour=>&strftime("%H:%M:%S",localtime),total_quantities=>$total_quantities,extra_data=>$check->{extra_data},idx_total=>$prod_idx_total,);
$check->{extra_data}->{queue_name}||="";
if($check->{extra_data}->{queue_name}){$info{queue_number}=_read_queue_number($machine,$check->{extra_data}->{queue_name});}my$name_file=$machine->dir_server()."/server/data/areas/$area/$check_number.name";
if(-e$name_file){local$/=undef;
open(F,"< ".$name_file);
my$cnt=<F>;
chop($cnt);
close(F);
$info{check_name}=$cnt;}$info{check_name}||="";
if(!$info{check_name}){$info{table_desc}=$check_number;}else{if($info{check_name}=~/^\d+$/){$info{table_desc}=$info{check_name};}else{$info{table_desc}=$check_number." (".$info{check_name}.")";}}$info{table_desc}=~s/<br>/ /g;
my@prod_final=();
my%group_by_printer=();
my%group_by_pzone=();
my$have_printing_priority=0;
$check->{extra_data}->{delivery_info}||="";
if($check->{extra_data}->{delivery_info}){my$dinfo=&_decode_delivery_info($db,$check);
$info{delivery_info}=$dinfo;}foreach my $zone(keys%to_print_void){my$printers=$PRINTING_ZONES_CONFIG->{$zone};
my$seat_idx=0;
my$seats=$check_o->{data}->{extra_data}->{seats};
$seats||="";
my@seats=split/#/,$seats;
my$products=$to_print_void{$zone};
my%i=();
my@prod=();
if($grouping_request_products){if($grouping_request_products==1){$group_products=1;}elsif($grouping_request_products==2||$grouping_request_products==4){$group_products=0;}}if(!$group_products){my$idx=0;
foreach my $p(sort{$a->{order}<=>$b->{order}}@$products){$products_requested_req_number->set_value($p->{key},$info{request_number});
my$seats_str="";
$seat_idx=$p->{order};
if(scalar@seats&&$p->{order}<=scalar(@seats)){$seats_str=$seats[$seat_idx];}my$id=$p->{product_code};
my$printing_priority=exists$PRINTING_PRIORITIES->{$id}?$PRINTING_PRIORITIES->{$id}:0;
$have_printing_priority=1 if$printing_priority;
if(!$p->{is_extra}){$last_print_priority=$printing_priority;}else{$printing_priority=0;
$printing_priority=$last_print_priority unless$printing_priority;}$p->{quantity}||=1;
if($grouping_request_products==4&&$p->{quantity}>1&&$p->{quantity}!~/\./){for(my$i=0;$i<$p->{quantity};$i++){$idx++ if!$p->{is_message}&&!$p->{is_extra};
push@prod,{product_info=>$p->{product_info},idx=>$idx,seats=>$seats_str,foreign=>$p->{foreign},foreign_orig=>$p->{foreign_orig},quantity=>1,hour=>$p->{hour},station=>$p->{station},station_name=>exists$STATION_NAMES->{$p->{station}}?$STATION_NAMES->{$p->{station}}:$p->{station},area=>$p->{area},vat=>0,price=>0,clerk_name=>$p->{clerk_name},clerk_code=>$p->{clerk_code},group_key=>$p->{group_key},product_code=>$p->{product_code},product_name=>$p->{product_name},product_menu=>$p->{product_menu},handy_plu=>$p->{handy_plu},is_extra=>$p->{is_extra},is_menu=>$p->{is_menu},is_message=>$p->{is_message},is_weight=>$p->{is_weight},key=>$p->{key},printing_priority=>$printing_priority,order=>$p->{order},};}}else{if(!exists$PRODUCTS_MESSAGES->{$p->{product_code}}){$idx++ if!$p->{is_message}&&!$p->{is_extra};
push@prod,{product_info=>$p->{product_info},idx=>$idx,seats=>$seats_str,foreign=>$p->{foreign},foreign_orig=>$p->{foreign_orig},quantity=>$p->{quantity},hour=>$p->{hour},station=>$p->{station},station_name=>exists$STATION_NAMES->{$p->{station}}?$STATION_NAMES->{$p->{station}}:$p->{station},area=>$p->{area},vat=>0,price=>0,clerk_name=>$p->{clerk_name},clerk_code=>$p->{clerk_code},group_key=>$p->{group_key},product_code=>$p->{product_code},product_name=>$p->{product_name},product_menu=>$p->{product_menu},handy_plu=>$p->{handy_plu},is_extra=>$p->{is_extra},is_menu=>$p->{is_menu},is_message=>$p->{is_message},is_weight=>$p->{is_weight},key=>$p->{key},printing_priority=>$printing_priority,order=>$p->{order},};}}$p->{notes}||=[];
if(scalar@{$p->{notes}}){my$c=1;
foreach my $note(@{$p->{notes}}){push@prod,{key=>$p->{key}.$c++,foreign=>$p->{foreign},foreign_orig=>$p->{foreign_orig},quantity=>1,hour=>$p->{hour},station=>$p->{station},station_name=>exists$STATION_NAMES->{$p->{station}}?$STATION_NAMES->{$p->{station}}:$p->{station},area=>$p->{area},vat=>0,price=>0,clerk_name=>$p->{clerk_name},clerk_code=>$p->{clerk_code},group_key=>$p->{group_key},product_code=>"",product_name=>$note,is_message=>exists$PRODUCTS_MESSAGES->{$p->{product_code}}?1:0,is_note=>1,order=>"10".$p->{order},};}}}}else{foreach my $p(reverse sort{$a->{order}<=>$b->{order}}@$products){$products_requested_req_number->set_value($p->{key},$info{request_number});
my$id=$p->{product_code};
my$printing_priority=exists$PRINTING_PRIORITIES->{$id}?$PRINTING_PRIORITIES->{$id}:0;
$have_printing_priority=1 if$printing_priority;
if(!$p->{is_extra}){$last_print_priority=$printing_priority;}else{$printing_priority=0;
$printing_priority=$last_print_priority unless$printing_priority;}$seat_idx=$p->{order};
if(scalar@seats&&$p->{order}<=scalar(@seats)){if(exists$i{$p->{product_code}}->{seats}){$i{$p->{product_code}}->{seats}=$i{$p->{product_code}}->{seats}.",".$seats[$seat_idx]if defined$seats[$seat_idx];}else{$i{$p->{product_code}}->{seats}=$seats[$seat_idx];}}$i{$p->{product_code}}->{idx}=$p->{idx};
$i{$p->{product_code}}->{product_info}=$p->{product_info};
$i{$p->{product_code}}->{quantity}+=$p->{quantity};
$i{$p->{product_code}}->{hour}=$p->{hour};
$i{$p->{product_code}}->{is_weight}=$p->{is_weight};
$i{$p->{product_code}}->{station}=$p->{station};
$i{$p->{product_code}}->{station_name}=exists$STATION_NAMES->{$p->{station}}?$STATION_NAMES->{$p->{station}}:$p->{station};
$i{$p->{product_code}}->{area}=$p->{area};
$i{$p->{product_code}}->{vat}=0;
$i{$p->{product_code}}->{price}=0;
$i{$p->{product_code}}->{foreign}=$p->{foreign};
$i{$p->{product_code}}->{foreign_orig}=$p->{foreign_orig};
$i{$p->{product_code}}->{clerk_name}=$p->{clerk_name};
$i{$p->{product_code}}->{clerk_code}=$p->{clerk_code};
$i{$p->{product_code}}->{product_code}=$p->{product_code};
$i{$p->{product_code}}->{product_menu}=$p->{product_menu};
$i{$p->{product_code}}->{product_name}=$p->{product_name};
$i{$p->{product_code}}->{handy_plu}=$p->{handy_plu};
$i{$p->{product_code}}->{order}=$p->{order};
$i{$p->{product_code}}->{key}=$p->{key};
$i{$p->{product_code}}->{printing_priority}=$printing_priority;
$i{$p->{product_code}}->{is_extra}=$p->{is_extra};
$i{$p->{product_code}}->{is_menu}=$p->{is_menu};}@prod=values%i;
@prod=sort{$a->{order}<=>$b->{order}}@prod;}my$a_tmp=$area+1;
my$c=0;
foreach my $printer(@$printers){my($station,$num)=split/#/,$printer;
my$area_tmp=$area+1;
my$station_tmp=$station+1;
my$copies=1;
if(exists$PRINTING_ZONES_FILTERS->{$zone}->{$c}){my$filter=$PRINTING_ZONES_FILTERS->{$zone}->{$c};
$copies=$PRINTING_ZONES_FILTERS->{$zone}->{Copies}->[$c];
if(defined$filter->{FromStation}&&$filter->{FromStation}ne ''&&$filter->{FromStation}!=$hold_station){if($hold_station>80&&$filter->{FromStation}eq '99'){}else{$c++;
next;}}if(defined$filter->{FromArea}&&$filter->{FromArea}ne ''&&$filter->{FromArea}!=$area_tmp){$c++;
next;}}my$rule_number=$c+1;
foreach my $rec(@prod){$rec->{printing_zone}=$zone;
push@{$group_by_printer{$station}->{$num-1}->{$copies}},$rec;
push@{$group_by_pzone{$zone}->{$station}->{$num-1}->{$copies}},$rec;}$c++;}}if($cfg->{GROUP_REQUESTS_BY_PRINTER}||scalar keys%$group_printer_specific){my%to_print=();
foreach my $station(keys%group_by_printer){foreach my $num(keys%{$group_by_printer{$station}}){foreach my $copies(keys%{$group_by_printer{$station}->{$num}}){if(!$cfg->{GROUP_REQUESTS_BY_PRINTER}&&scalar keys%$group_printer_specific){next unless exists$group_printer_specific->{$station}->{$num}}my$prods=$group_by_printer{$station}->{$num}->{$copies};
$info{products}=$prods;
my$individual_products=$grouping_request_products==3||$grouping_request_products==4?1:0;
if($individual_products){my@prints=();
my$cur=[];
my$product_idx=1;
for(reverse@$prods){if($_->{is_extra}){push@$cur,$_;}else{push@$cur,$_;
push@prints,\@{$cur};
$cur=[];}}my$print_count=1;
foreach my $print(@prints){my@p=reverse@$print;
$info{products}=\@p;
my$pzone="";
$pzone=scalar@p?$p[0]->{printing_zone}:"";
my$template='products_void.tmpl';
if($pzone){my$pi=$machine->config()->printers_info();
my$printer_num=$num+1;
my$area_num=$area+1;
$pi->{$pzone}->{$station}->{$printer_num}->{Template}||="";
$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}||="";
if($pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}){my$key1=$area_num."#".$hold_station;
my$key2=$area_num."#";
my$key3="#".$hold_station;
my$key4="";
my$key5="";
if($hold_station>80){$key4=$area_num."#99";
$key5="#99";}if(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1};}elsif(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2};}elsif(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3};}elsif($key4&&exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4};}elsif($key5&&exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5};}}elsif($pi->{$pzone}->{$station}->{$printer_num}->{Template}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{Template};}}$info{operation}="void";
$info{products}=_group_products_by_util_name($prods)if$cfg->{GROUP_BY_PRODUCT_UTIL_NAME};
my$check_has_menus=0;
for(@{$info{products}}){if(exists$PRODUCTS_MENUS->{$_->{product_code}}){$check_has_menus=1;
last;}}if($cfg->{SORT_REQUESTS_BY_PRINT_PRIORITY}&&$have_printing_priority&&!$check_has_menus){@{$info{products}}=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@{$info{products}};}my@final=();
my%did_keys=();
for(sort{$a->{foreign}<=>$b->{foreign}}@{$info{products}}){next if exists$did_keys{$_->{key}};
push@final,$_;
$did_keys{$_->{key}}=1;}$info{products}=\@final;
$info{print_count}=$print_count;
$info{print_total}=scalar@prints;
my$content="";
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template,\%info,\$content)||die$TEMPLATE->error;
if($save_to_hdd){my$printer=App::POS::Printer->new(driver=>'EpsonTM88');
my$p_class=$printer->get_class();
_printer_tags_replace($p_class,undef,\$content);
my$file=$machine->dir_server().'/server/data/tmp/'.$save_to_hdd.'.order';
append_file($file,$content);}my$archive_dir=$machine->dir_server().'/server/data/order_archive/'.$station."/".$num;
&make_path($archive_dir)unless-d$archive_dir;
my$archived=0;
my$zone="";
if(scalar@$prods){$zone=$prods->[0]->{printing_zone};}for(my$i=0;$i<$copies;$i++){if($i==0){my$void_monitor_key=$station."-".($num+1);
if(exists$VOIDS_MONITORING{$void_monitor_key}){foreach my $monitor(@{$VOIDS_MONITORING{$void_monitor_key}}){next unless scalar@$monitor==2;
my$num_tmp=$monitor->[1]-1;
$to_print{$monitor->[0]}->{$num_tmp}.=$content}}}my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
if(!$archived++){if($cfg->{PRINT_HISTORY_SAVE}){open(F,"> $archive_dir/$fname");
print F$content;
close(F);}}$to_print{$station}->{$num}.=$content}$print_count++;}}else{$info{products}=$prods;
my$pzone="";
$pzone=scalar@$prods?$prods->[0]->{printing_zone}:"";
my$template='products_void.tmpl';
if($pzone){my$pi=$machine->config()->printers_info();
my$printer_num=$num+1;
my$area_num=$area+1;
$pi->{$pzone}->{$station}->{$printer_num}->{Template}||="";
$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}||="";
if($pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}){my$key1=$area_num."#".$hold_station;
my$key2=$area_num."#";
my$key3="#".$hold_station;
my$key4="";
my$key5="";
if($hold_station>80){$key4=$area_num."#99";
$key5="#99";}if(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1};}elsif(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2};}elsif(exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3};}elsif($key4&&exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4};}elsif($key5&&exists$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5};}}elsif($pi->{$pzone}->{$station}->{$printer_num}->{Template}){$template=$pi->{$pzone}->{$station}->{$printer_num}->{Template};}}$info{operation}="void";
$info{products}=_group_products_by_util_name($prods)if$cfg->{GROUP_BY_PRODUCT_UTIL_NAME};
if($grouping_request_products==2){if(exists$group_printer_specific->{$station}->{$num}){$info{products}=_group_products($info{products});}}my$check_has_menus=0;
for(@{$info{products}}){if(exists$PRODUCTS_MENUS->{$_->{product_code}}){$check_has_menus=1;
last;}}if($cfg->{SORT_REQUESTS_BY_PRINT_PRIORITY}&&$have_printing_priority&&!$check_has_menus){@{$info{products}}=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@{$info{products}};}my@final=();
my%did_keys=();
for(sort{$a->{foreign}<=>$b->{foreign}}@{$info{products}}){next if exists$did_keys{$_->{key}};
push@final,$_;
$did_keys{$_->{key}}=1;}$info{products}=\@final;
$info{print_count}=1;
$info{print_total}=1;
my$content="";
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template,\%info,\$content)||die$TEMPLATE->error;
if($save_to_hdd){my$printer=App::POS::Printer->new(driver=>'EpsonTM88');
my$p_class=$printer->get_class();
_printer_tags_replace($p_class,undef,\$content);
my$file=$machine->dir_server().'/server/data/tmp/'.$save_to_hdd.'.order';
append_file($file,$content);}my$archive_dir=$machine->dir_server().'/server/data/order_archive/'.$station."/".$num;
&make_path($archive_dir)unless-d$archive_dir;
my$archived=0;
my$zone="";
if(scalar@$prods){$zone=$prods->[0]->{printing_zone};}for(my$i=0;$i<$copies;$i++){if($i==0){my$void_monitor_key=$station."-".($num+1);
if(exists$VOIDS_MONITORING{$void_monitor_key}){foreach my $monitor(@{$VOIDS_MONITORING{$void_monitor_key}}){next unless scalar@$monitor==2;
my$num_tmp=$monitor->[1]-1;
$to_print{$monitor->[0]}->{$num_tmp}.=$content}}}my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
if(!$archived++){if($cfg->{PRINT_HISTORY_SAVE}){open(F,"> $archive_dir/$fname");
print F$content;
close(F);}}$to_print{$station}->{$num}.=$content;}}}}}if(scalar keys%to_print){my$barcode="";
$cfg->{PRINT_BARCODE_IN_ORDER}//=0;
if($cfg->{PRINT_BARCODE_IN_ORDER}){if($cfg->{PRINT_BARCODE_IN_ORDER}==1){$barcode=$info{request_number};}elsif($cfg->{PRINT_BARCODE_IN_ORDER}==2){$barcode=$info{check_number};}}foreach my $station(keys%to_print){foreach my $num(keys%{$to_print{$station}}){my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
my$f=$machine->server_dir_tmp()."/".$fname;
if($barcode){my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$f;
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,$barcode,5);}_add_custom_top_and_bottom($machine,$check->{header}->[6],\$to_print{$station}->{$num});
open(F,"> $f");
print F$to_print{$station}->{$num};
close(F);}}}}if(!$cfg->{GROUP_REQUESTS_BY_PRINTER}||scalar keys%$group_printer_specific){my%to_print=();
foreach my $zone(keys%group_by_pzone){foreach my $station(keys%{$group_by_pzone{$zone}}){foreach my $num(keys%{$group_by_pzone{$zone}->{$station}}){foreach my $copies(keys%{$group_by_pzone{$zone}->{$station}->{$num}}){if(scalar keys%$group_printer_specific){next if exists$group_printer_specific->{$station}->{$num}}my$prods=$group_by_pzone{$zone}->{$station}->{$num}->{$copies};
$info{products}=$prods;
my$individual_products=$grouping_request_products==3||$grouping_request_products==4?1:0;
if($individual_products){my@prints=();
my$cur=[];
my$product_idx=1;
for(reverse@$prods){if($_->{is_extra}){push@$cur,$_;}else{push@$cur,$_;
push@prints,\@{$cur};
$cur=[];}}my$print_count=1;
foreach my $print(@prints){my@p=reverse@$print;
$info{products}=\@p;
my$template='products_void.tmpl';
my$pi=$machine->config()->printers_info();
my$printer_num=$num+1;
my$area_num=$area+1;
$pi->{$zone}->{$station}->{$printer_num}->{Template}||="";
$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}||="";
if($pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}){my$key1=$area_num."#".$hold_station;
my$key2=$area_num."#";
my$key3="#".$hold_station;
my$key4="";
my$key5="";
if($hold_station>80){$key4=$area_num."#99";
$key5="#99";}if(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1};}elsif(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2};}elsif(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3};}elsif($key4&&exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4};}elsif($key5&&exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5};}}elsif($pi->{$zone}->{$station}->{$printer_num}->{Template}){$template=$pi->{$zone}->{$station}->{$printer_num}->{Template};}$info{operation}="void";
$info{products}=_group_products_by_util_name($info{products})if$cfg->{GROUP_BY_PRODUCT_UTIL_NAME};
my$check_has_menus=0;
for(@{$info{products}}){if(exists$PRODUCTS_MENUS->{$_->{product_code}}){$check_has_menus=1;
last;}}if($cfg->{SORT_REQUESTS_BY_PRINT_PRIORITY}&&$have_printing_priority&&!$check_has_menus){@{$info{products}}=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@{$info{products}};}my@final=();
my%did_keys=();
for(sort{$a->{foreign}<=>$b->{foreign}}@{$info{products}}){next if exists$did_keys{$_->{key}};
push@final,$_;
$did_keys{$_->{key}}=1;}$info{products}=\@final;
$info{print_count}=$print_count;
$info{print_total}=scalar@prints;
my$content="";
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template,\%info,\$content)||die$TEMPLATE->error;
if($save_to_hdd){my$printer=App::POS::Printer->new(driver=>'EpsonTM88');
my$p_class=$printer->get_class();
_printer_tags_replace($p_class,undef,\$content);
my$file=$machine->dir_server().'/server/data/tmp/'.$save_to_hdd.'.order';
append_file($file,$content);}my$archive_dir=$machine->dir_server().'/server/data/order_archive/'.$station."/".$num;
&make_path($archive_dir)unless-d$archive_dir;
my$archived=0;
my$zone="";
if(scalar@$prods){$zone=$prods->[0]->{printing_zone};}for(my$i=0;$i<$copies;$i++){if($i==0){my$void_monitor_key=$station."-".($num+1);
if(exists$VOIDS_MONITORING{$void_monitor_key}){foreach my $monitor(@{$VOIDS_MONITORING{$void_monitor_key}}){next unless scalar@$monitor==2;
my$num_tmp=$monitor->[1]-1;
$to_print{$monitor->[0]}->{$num_tmp}.=$content}}}my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
if(!$archived++){if($cfg->{PRINT_HISTORY_SAVE}){open(F,"> $archive_dir/$fname");
print F$content;
close(F);}}$to_print{$station}->{$num}.=$content}$print_count++;}}else{$info{products}=$prods;
my$template='products_void.tmpl';
my$pi=$machine->config()->printers_info();
my$printer_num=$num+1;
my$area_num=$area+1;
$pi->{$zone}->{$station}->{$printer_num}->{Template}||="";
$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}||="";
if($pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}){my$key1=$area_num."#".$hold_station;
my$key2=$area_num."#";
my$key3="#".$hold_station;
my$key4="";
my$key5="";
if($hold_station>80){$key4=$area_num."#99";
$key5="#99";}if(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key1};}elsif(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key2};}elsif(exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key3};}elsif($key4&&exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key4};}elsif($key5&&exists$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5}){$template=$pi->{$zone}->{$station}->{$printer_num}->{TemplateFilter}->{$key5};}}elsif($pi->{$zone}->{$station}->{$printer_num}->{Template}){$template=$pi->{$zone}->{$station}->{$printer_num}->{Template};}$info{operation}="void";
$info{products}=_group_products_by_util_name($info{products})if$cfg->{GROUP_BY_PRODUCT_UTIL_NAME};
my$check_has_menus=0;
for(@{$info{products}}){if(exists$PRODUCTS_MENUS->{$_->{product_code}}){$check_has_menus=1;
last;}}if($cfg->{SORT_REQUESTS_BY_PRINT_PRIORITY}&&$have_printing_priority&&!$check_has_menus){@{$info{products}}=reverse sort{$a->{printing_priority}<=>$b->{printing_priority}}@{$info{products}};}my@final=();
my%did_keys=();
for(sort{$a->{foreign}<=>$b->{foreign}}@{$info{products}}){next if exists$did_keys{$_->{key}};
push@final,$_;
$did_keys{$_->{key}}=1;}$info{products}=\@final;
$info{print_count}=1;
$info{print_total}=1;
my$content="";
$info{i18n}=sub{&translate($machine,shift)};
$TEMPLATE->process($template,\%info,\$content)||die$TEMPLATE->error;
if($save_to_hdd){my$printer=App::POS::Printer->new(driver=>'EpsonTM88');
my$p_class=$printer->get_class();
_printer_tags_replace($p_class,undef,\$content);
my$file=$machine->dir_server().'/server/data/tmp/'.$save_to_hdd.'.order';
append_file($file,$content);}my$archive_dir=$machine->dir_server().'/server/data/order_archive/'.$station."/".$num;
&make_path($archive_dir)unless-d$archive_dir;
my$archived=0;
my$zone="";
if(scalar@$prods){$zone=$prods->[0]->{printing_zone};}for(my$i=0;$i<$copies;$i++){if($i==0){my$void_monitor_key=$station."-".($num+1);
if(exists$VOIDS_MONITORING{$void_monitor_key}){foreach my $monitor(@{$VOIDS_MONITORING{$void_monitor_key}}){next unless scalar@$monitor==2;
my$num_tmp=$monitor->[1]-1;
$to_print{$monitor->[0]}->{$num_tmp}.=$content}}}my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
if(!$archived++){if($cfg->{PRINT_HISTORY_SAVE}){open(F,"> $archive_dir/$fname");
print F$content;
close(F);}}$to_print{$station}->{$num}.=$content}}}}}}if(scalar keys%to_print){my$barcode="";
$cfg->{PRINT_BARCODE_IN_ORDER}//=0;
if($cfg->{PRINT_BARCODE_IN_ORDER}){if($cfg->{PRINT_BARCODE_IN_ORDER}==1){$barcode=$info{request_number};}elsif($cfg->{PRINT_BARCODE_IN_ORDER}==2){$barcode=$info{check_number};}}foreach my $station(keys%to_print){foreach my $num(keys%{$to_print{$station}}){my$r=int(rand(10000));
my$fname="print#".$station."#".$num."#0#".time.$r."_".$hold_station.".txt";
my$f=$machine->server_dir_tmp()."/".$fname;
if($barcode){my$memc_b=memcache($machine->server_ip(),'barcodes',\%MEMC_CONNECTIONS);
my@a=split/#/,$f;
my($tmp)=$a[4]=~/^(\d+)/;
$memc_b->set($tmp,$barcode,5);}_add_custom_top_and_bottom($machine,$check->{header}->[6],\$to_print{$station}->{$num});
open(F,"> $f");
print F$to_print{$station}->{$num};
close(F);}}}}}else{print STDERR"PRINT_VOIDS IN config.ini IS TURNED OFF...\n";}}sub _find_file_to_print{my%a=(machine=>undef,station=>undef,station_close=>undef,station_print_checks=>'',@_,);
my$machine=$a{machine};
my$cfg=$machine->config()->load_fixed();
$cfg->{REDIRECT_TO_NETWORK_PRINTERS_BY_STATION}||={};
my$station_close=$a{station_close};
my$station=$a{station};
my$station_to_print=0;
if($a{station_print_checks}ne ''){if($a{station_print_checks}==99){$station_to_print=$station_close;}else{$station_to_print=$a{station_print_checks};}}else{if($cfg->{STATION_PRINT_CHECKS}==99){$station_to_print=$station_close;}else{$station_to_print=$cfg->{STATION_PRINT_CHECKS};}}$station_to_print=$station unless defined$station_to_print;
my$ip_redirect="";
my$redirect_key='REDIRECT_TO_NETWORK_PRINTERS_BY_STATION';
if(exists$cfg->{$redirect_key}->{$station_to_print}){$ip_redirect="_ipredir=".$cfg->{$redirect_key}->{$station_to_print};}if($station_to_print>80){$station_to_print=0;}if(exists$MAIN_PRINTERS{$station_to_print}){my$printer=$MAIN_PRINTERS{$station_to_print};
my$r=int(rand(100000));
return($machine->server_dir_tmp()."/print#$station_to_print#".$printer."#1#".time.$r.$ip_redirect.".txt",$station_to_print);}else{my$main_file=$machine->dir_server()."/server/data/registered_printers/".$station_to_print."/main";
if(!-e$main_file){print STDERR"Can't find main file for station $station_to_print ($main_file)\n";
print STDERR"CHECK IF THAT STATION HAS A 'Main' PRINTER!\n";
return(undef,undef);}else{my$printer=undef;
eval{local$SIG{ALRM}=sub{die"write error\n"};
alarm 1;
{local$/=undef;
open(F,"< $main_file");
$printer=<F>;
chop($printer);
close(F);}alarm(0);};
if($@){print STDERR"ERROR ALARM() OPENING $main_file - $@\n";}if(defined$printer){$MAIN_PRINTERS{$station_to_print}=$printer;
my$r=int(rand(100000));
return($machine->server_dir_tmp()."/print#$station_to_print#".$printer."#1#".time.$r.$ip_redirect.".txt",$station_to_print);}}}return(undef,undef);}sub _get_sign_chars{my%a=(signature=>undef,chars=>[0,10,20,30],@_);
my@chars=split//,$a{signature};
my@c=@{$a{chars}};
return@chars[@c];}sub _decode_card_data{my$str=shift;
my$r={};
my@chars=split//,$str;
my$is_dallas=0;
my$read=0;
my$cur=0;
my$ignore_next=0;
foreach my $c(@chars){my$ord=ord($c);
if($ord==243){$cur=1;
$read=1;}elsif($ord==241){$is_dallas=1;
$cur="read";
$read=1;}elsif($ord==242){$is_dallas=1;
$cur="read";
$read=1;
$r->{$cur}=1;}elsif($ord==244){$ignore_next=1;}elsif($ord==1||$ord==2||$ord==3||$ord==4||$ord==5){$cur=$ord;
$read=1;
$ignore_next=1;}else{if($ignore_next==1){$ignore_next=0;
next;}else{$r->{$cur}.=$c;}}}$r->{type}=$is_dallas?'dallas':'card';
return$r;}sub _get_template_obj{my$machine=shift;
return Template->new({INCLUDE_PATH=>$machine->dir_server().'/common/print_templates',CACHE_SIZE=>0,EVAL_PERL=>1,ABSOLUTE=>1,});}sub _decode_expire{my$ve=shift;
my$expiration="";
my$val;
if($ve){if(($val)=$ve=~/^(\d+)y$/){my($y,$m,$d)=Today();
my$days=365*$val;
my($fy,$fm,$fd)=Add_Delta_Days($y,$m,$d,$days);
$expiration="$fy-".sprintf("%02d",$fm)."-".sprintf("%02d",$fd);}elsif(($val)=$ve=~/^(\d+)m$/){my($y,$m,$d)=Today();
my$days=30*$val;
my($fy,$fm,$fd)=Add_Delta_Days($y,$m,$d,$days);
$expiration="$fy-".sprintf("%02d",$fm)."-".sprintf("%02d",$fd);}elsif(($val)=$ve=~/^(\d+)d$/){my($y,$m,$d)=Today();
my$days=$val;
my($fy,$fm,$fd)=Add_Delta_Days($y,$m,$d,$days);
$expiration="$fy-".sprintf("%02d",$fm)."-".sprintf("%02d",$fd);}else{print STDERR"Invalid voucher expire format... use 1y, 1m, 1d for example...\n";
print STDERR"Defaulting to 1m...\n";
return _decode_expire('1m');}}return$expiration||'2200-01-01';}sub _linestatus{my$p=shift;
my$info={};
my$status=$p->modemlines;
$info->{dtr}=($status& MS_DTR_ON)?"ON ":"OFF";
$info->{cts}=($status& MS_CTS_ON)?"ON ":"OFF";
$info->{rts}=($status& MS_RTS_ON)?"ON ":"OFF";
$info->{dsr}=($status& MS_DSR_ON)?"ON ":"OFF";
$info->{ring}=($status& MS_RING_ON)?"ON ":"OFF";
$info->{rlsd}=($status& MS_RLSD_ON)?"ON ":"OFF";
return$info;}sub _slurp_printing_zone{my($info,$from,$to)=@_;
return unless defined$from&&defined$to;
my$r_orig=exists$info->{$from}?$info->{$from}:undef;
my$r_dest=exists$info->{$to}?$info->{$to}:undef;
if(defined$r_orig&&defined$r_dest){my%dest_keys=();
map{$dest_keys{$_->{key}}=1}@$r_dest;
foreach my $rec(@$r_orig){next if exists$dest_keys{$rec->{key}};
my@notes=();
if(exists$rec->{foreign_orig}&&$rec->{foreign_orig}eq$to){next;}if(exists$rec->{notes}){@notes=@{$rec->{notes}};}my%info=%{$rec};
$info{notes}=\@notes;
$info{foreign}=1;
$info{foreign_orig}=$from;
push@$r_dest,\%info;}}}sub _get_report_lists_info{my$db=shift;
my%info=();
my$r=$db->select(qq{
    SELECT pl.*, l.*, l.kind as list_kind, pl.kind as line_kind
    FROM products_lists pl
    LEFT JOIN lists l ON (pl.list_id = l.id)
    LEFT JOIN product_groups pg ON (pl.product_id = pg.product_id)
    WHERE l.kind = 'L' AND l.for_reporting = 1
  });
foreach my $rec(@$r){$info{lists}->{$rec->{list_id}}->{start_hour}=$rec->{start_hour};
$info{lists}->{$rec->{list_id}}->{end_hour}=$rec->{end_hour};
$info{lists}->{$rec->{list_id}}->{weekdays}=$rec->{weekdays};
my@a=split/,/,$rec->{weekdays};
$info{lists}->{$rec->{list_id}}->{weekdays_as_array}=\@a;
$info{lists}->{$rec->{list_id}}->{name}=$rec->{list_name};
if($rec->{line_kind}eq 'G'){my$prods=$db->select(qq{
        SELECT p.id AS product_id
        FROM product_groups pg
        LEFT JOIN products p ON (pg.product_id = p.id)
        WHERE pg.group_id = ?
      },$rec->{product_id});
for(@$prods){next unless defined$_->{product_id};
$info{lists}->{$rec->{list_id}}->{products}->{$_->{product_id}}=1;
$info{lists_by_products}->{$_->{product_id}}->{$rec->{list_id}}=1;}}else{$info{lists}->{$rec->{list_id}}->{products}->{$rec->{product_id}}=1;
$info{lists_by_products}->{$rec->{product_id}}->{$rec->{list_id}}=1;}}return\%info;}sub get_products_translations{my$db=shift;
if(!defined$PRODUCTS_TRANSLATIONS){my$tmp=$db->select("SELECT * FROM product_names");
map{if($_->{product_id}&&$_->{language}){$PRODUCTS_TRANSLATIONS->{$_->{product_id}}->{$_->{language}}=$_->{value};}}@$tmp;}}sub get_clerks_information{my$db=shift;
if(!defined$CLERKS_INFO){my$tmp=$db->select("SELECT * FROM clerks WHERE deleted = 0");
for(@$tmp){$_->{login}=&my_conv($_->{login});
if($_->{drawer}){$CLERKS_DRAWER->{$_->{id}}=$_->{drawer};}$CLERKS_INFO->{$_->{id}}=$_;
$CLERKS_IDS_BY_LOGIN->{$_->{login}}=$_->{id};}}}sub get_customers_information{my$db=shift;
my$info={};
my$recs=$db->select("SELECT id, name, nif FROM customers ORDER BY deleted");
map{$info->{$_->{id}}=$_}@$recs;
return$info;}sub get_products_information{my$db=shift;
my$q;
if(!defined$PRODUCTS_INFO){$db->do("SET enable_nestloop TO off");
$q=qq{
      SELECT p.*,
      CASE
        WHEN f.id IS NOT NULL THEN COALESCE(f.name,'----------')
        ELSE COALESCE(sf.name,'----------')
      END as family_name, f.print_priority AS f_print_priority,
      CASE
        WHEN sf.id IS NOT NULL THEN COALESCE(sf.name,'----------')
        ELSE '----------'
      END as subfamily_name, sf.print_priority AS sf_print_priority
      FROM products p
      LEFT JOIN product_groups pg ON (p.id = pg.product_id)
      LEFT JOIN groups sf ON (pg.group_id = sf.id)
      LEFT JOIN groups f ON (sf.parent = f.id)
    };
my$sth=$db->dbh()->prepare($q);
$sth->execute();
while(my$row=$sth->fetchrow_hashref){$row->{name}=&my_conv($row->{name});
$row->{button_text}=&my_conv($row->{button_text});
$row->{family_name}=&my_conv($row->{family_name});
$row->{subfamily_name}=&my_conv($row->{subfamily_name});
$PRODUCTS_EXTRAS->{$row->{id}}=1 if$row->{is_extra};
$PRODUCTS_MESSAGES->{$row->{id}}=1 if$row->{is_message};
$PRODUCTS_MENUS->{$row->{id}}=1 if$row->{is_menu};
$PRODUCTS_IDS_BY_HANDY_PLU->{$row->{handy_plu}}=$row->{id}if$row->{handy_plu}&&$row->{deleted}==0;
$PRODUCTS_INFO->{$row->{id}}=$row;
$PRINTING_PRIORITIES->{$row->{id}}=$row->{print_priority};
$PRINTING_PRIORITIES->{$row->{id}}=$row->{sf_print_priority}if$row->{sf_print_priority}&&!$PRINTING_PRIORITIES->{$row->{id}};
$PRINTING_PRIORITIES->{$row->{id}}=$row->{f_print_priority}if$row->{f_print_priority}&&!$PRINTING_PRIORITIES->{$row->{id}};}$q=qq{
      SELECT * FROM products_extra_data
    };
$sth=$db->dbh()->prepare($q);
$sth->execute();
while(my$row=$sth->fetchrow_hashref){$PRODUCTS_EXTRA_DATA->{$row->{product_id}}->{$row->{field_name}}=$row->{field_value};}$db->do("SET enable_nestloop TO on");
my$sons=$db->select(qq{
      SELECT p1.id, p1.parent_product, p1.qtty_factor AS factor, p2.qtty_factor AS factor_parent
      FROM products p1
      LEFT JOIN products p2 ON (p1.parent_product = p2.id)
      WHERE p1.parent_product <> 0 AND p1.deleted = 0 AND p2.deleted = 0
    });
foreach my $son(@$sons){next unless exists$PRODUCTS_INFO->{$son->{parent_product}};
next unless exists$PRODUCTS_INFO->{$son->{id}};
$PRODUCTS_INFO->{$son->{parent_product}}->{sons}->{$son->{id}}=$son->{factor};
$PRODUCTS_INFO->{$son->{id}}->{parent}=$son->{parent_product};}}}sub shrink_words{my$str=shift;
my$cfg=shift;
if(exists$cfg->{DONT_SHRINK_WORDS}&&$cfg->{DONT_SHRINK_WORDS}){return$str;}if(length($str)<20){return$str;}$str=&trim_string($str);
my$new_str="";
my@words=split/\s/,$str;
foreach my $w(@words){my$len=length($w);
if($len>2){if($len>=5){$new_str.=substr($w,0,5)." ";}else{$new_str.=$w." ";}}}$new_str=~s/\s$//;
return$new_str;}sub trim_string{my$w=shift;
$w=~s/^\s+//;
$w=~s/\s+$//;
$w=~s/\s+/ /g;
return$w;}sub lazy_file_create{my($file,$content,$machine)=@_;
my$r;
if(defined$machine){$r=$machine->dir_tmp()."/".$$.int(rand(10000));}else{$r="/tmp/".$$.int(rand(10000));}if(open(F,"> $r")){$content||="";
print F$content;
close(F);
&move($r,$file);}}sub external_print{my$cfg=shift;
my$cnt=shift;
my$prefix=shift;
my$dont_print=shift;
my$station=shift;
my$pdf_only=shift;
my$email_addr=shift;
my$check_num=$prefix;
$prefix||="";
$prefix=~s/\//_/g if$prefix;
my$r="_".$$.int(rand(100));
my$file_html="/tmp/".$prefix.$r.".html";
my$file_pdf="/tmp/".$prefix.$r.".pdf";
$cnt=encode("iso8859-1",decode("utf8",$cnt));
open(F,"> $file_html");
print F$cnt;
close(F);
$file_pdf=~s/\://g;
$file_pdf=~s/\s/_/g;
if(defined$email_addr){_pdf_gen($cfg,$file_html,$file_pdf,$email_addr,$check_num);}else{_pdf_gen($cfg,$file_html,$file_pdf);}if($pdf_only==0){$dont_print||=0;
if(!$dont_print){if($cfg->{STATION_NUMBER}!=$station){print STDERR"Will send PDF $file_pdf to station $station...\n";
my$r=int(rand(10000));
&move($file_pdf,"/opt/pos/server/data/tmp/pdf_to_station_print#$station#".time."#$r.pdf");}else{system("lpr \"$file_pdf\"");}}}unlink($file_html);}sub process_barcode{my($machine,$file)=@_;
my$barcode=read_file($file)||"";
if($barcode){_send_to_ui('BARCODE',$barcode);
unlink$file;}}sub process_msg_to_ui{my($machine,$file)=@_;
my($station,$clerk)=$file=~/msg_to_ui#(\d+)#(\d+)#/;
print STDERR"> process_msg_to_ui - station: $station, clerk: $clerk\n";
my$cfg=$machine->config()->load_fixed();
if($station ne$cfg->{STATION_NUMBER}){print STDERR"File $file was generated for station $station, this is station ".$cfg->{STATION_NUMBER}.", ignoring...\n";
return 2;}my$message=read_file($file)||"";
unlink$file;
if($message){$machine->send_alert_to_ui(&translate($machine,"$message"));}}sub points_customer_statement{my($machine,$file)=@_;
my$cfg=$machine->config()->load_fixed();
return 2 unless$cfg->{STATION_NUMBER}eq '0';
$cfg->{LOYALTY}||=0;
$cfg->{LOYALTY_TOKEN}||="";
$cfg->{LOYALTY_URL}||="";
$cfg->{POINTS_USAGE}||=0;
$cfg->{POINTS_VALUE_PER_POINT}||=0;
$cfg->{POINTS_VALUE_PER_POINT_ON_SALE}||=1;
$cfg->{POINTS_SYNC_TOKEN}||="";
if($cfg->{LOYALTY}){$cfg->{POINTS_USAGE}=0;
$cfg->{POINTS_SYNC_TOKEN}="";}my($station,$clerk,$customer_id)=$file=~/points_customer_statement#(.*?)#(.*?)#(.*?)\.txt$/;
my$db=$machine->database();
&get_clerks_information($db);
my$points=$db->select("SELECT id, contact, name, city, postal_code, address, points, card_number, use_points, app_sign_key FROM customers WHERE id = ?",$customer_id,);
if(!scalar@$points){print STDERR"Can't find customer with id $customer_id... aborting...\n";
return;}my$app_sign_key=$points->[0]->{app_sign_key}||"";
my%info=();
my$content="";
if(!defined$TEMPLATE){$TEMPLATE=&_get_template_obj($machine);}$info{i18n}=sub{&translate($machine,shift)};
if($cfg->{LOYALTY}&&$cfg->{LOYALTY_TOKEN}&&$cfg->{LOYALTY_URL}){my$memc=memcache($machine->server_ip(),'pos_files',\%MEMC_CONNECTIONS);
my$hardlock=$memc->get('hardlock')||"";
$hardlock=`/opt/pos/common/bin/machine_id` unless$hardlock;
my$url=$cfg->{LOYALTY_URL}."/customer/statement?token=".uri_escape($cfg->{LOYALTY_TOKEN})."&app_sign_key=".uri_escape($app_sign_key)."&hardlock=".$hardlock;
my$cnt=LWP::Simple::get($url);
$cnt||="";
return unless$cnt;
$cnt=encode("utf8",$cnt);
my$ref;
eval{$ref=decode_json($cnt)};
if($@){print STDERR"error parsing json - $@\n";
return;}%info=%$ref;
$info{i18n}=sub{&translate($machine,shift)};
$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
my$idx=0;
my$prev;
for(@{$info{points}}){my$p=$_->{points_assigned}?$_->{points}:$_->{points}*-1;
$_->{store_name}=encode("utf8",$_->{store_name});
if($idx>0){$_->{balance}=$prev->{balance}+$p;}else{$_->{balance}=$p;}$idx++;
$prev=$_;}for(@{$info{vouchers}}){$_->{used_store_name}=encode("utf8",$_->{used_store_name});}$TEMPLATE->process('report_loyalty_app.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}return;}my$actual_points=0;
my$actual_points_value=0;
my$name="";
my$contact="";
my$card_number="";
my$city="";
my$postal_code="";
my$address="";
my$sales=[];
$actual_points=$points->[0]->{points}||0;
$actual_points_value=$actual_points*$cfg->{POINTS_VALUE_PER_POINT_ON_SALE};
$address=my_conv($points->[0]->{address})||"";
$city=my_conv($points->[0]->{city})||"";
$postal_code=my_conv($points->[0]->{postal_code})||"";
$name=my_conv($points->[0]->{name})||"";
$card_number=$points->[0]->{card_number}||"";
$contact=$points->[0]->{contact}||"";
if($cfg->{POINTS_USAGE}&&$cfg->{POINTS_SYNC_TOKEN}){$card_number=$app_sign_key if$app_sign_key;
my$cmd="/opt/pos/common/bin/customer_points.sh points ".$card_number." ".$cfg->{POINTS_SYNC_TOKEN};
my$data=`$cmd`;
if($data=~/^ERROR/){return;}$actual_points=$data;
$actual_points_value=$actual_points*$cfg->{POINTS_VALUE_PER_POINT_ON_SALE};
$cmd="/opt/pos/common/bin/customer_points.sh statement ".$card_number." ".$cfg->{POINTS_SYNC_TOKEN};
$data=`$cmd`;
if($data=~/^ERROR/){return;}my@lines=split/\@/,$data;
foreach my $l(@lines){my@r=split/#/,$l;
my($store_name,$date,$hour,$doc_num,$value,$p_assigned,$p_used)=@r;
push@$sales,{points_used=>$p_used,points_assigned=>$p_assigned,hour=>$hour,fo_doc_number=>$doc_num,value=>$value,business_date=>$date,};}}else{$sales=$db->select(qq{
      SELECT sha1.key_value AS points_used, sh.business_date, sh.value, sh.id, sh.hour, sh.fo_doc_number,
        sha2.key_value AS points_assigned
      FROM sales_headers sh
      LEFT JOIN sales_headers_aux sha1 ON (sh.id = sha1.header AND sha1.key_name = 'points_used')
      LEFT JOIN sales_headers_aux sha2 ON (sh.id = sha2.header AND sha2.key_name = 'points_assigned')
      WHERE sh.customer_id = ?
      ORDER BY sh.id DESC
    },$customer_id);}$info{date}=&strftime("%Y-%m-%d",localtime);
$info{hour}=&strftime("%H:%M:%S",localtime);
$info{datetime}=&strftime("%Y-%m-%d %H:%M:%S",localtime);
$info{station}=$station;
$info{clerk_code}=$clerk;
$info{clerk_name}=exists$CLERKS_INFO->{$clerk}?$CLERKS_INFO->{$clerk}->{login}:'<UNKNOWN>';
$info{customer_points}=$actual_points;
$info{customer_contact}=$contact;
$info{customer_points_value}=$actual_points_value;
$info{customer_address}=$address;
$info{customer_name}=$name;
$info{customer_card_number}=$card_number;
$info{customer_city}=$city;
$info{customer_postal_code}=$postal_code;
$info{records}=$sales;
$TEMPLATE->process('report_points_customer.tmpl',\%info,\$content,)||die$TEMPLATE->error;
return unless$REAL_PRINT;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$content;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub sms_report{my($machine,$file)=@_;
my$cfg=$machine->config()->load_fixed();
return 2 unless$cfg->{STATION_NUMBER}eq '0';
my$NIF=$cfg->{SAFT_TaxRegistratioNumber}||0;
my$biz_name=$cfg->{SAFT_BusinessName}||"";
my$SMS_PHONE=$cfg->{SMS_PHONE}||"";
my$USE_SMS=$cfg->{USE_SMS}||0;
return unless$USE_SMS;
my($date,$clerk,$t_total,$nt_total,$m_id)=$file=~/sms_report#(.*?)#(.*?)#(.*?)#(.*?)#(.*?)\.txt$/;
my$sales_details=0;
my$clerk_filter=0;
my%selections=();
my$type='01_daily_simple';
my$info=_data_for_report($machine,$clerk,$cfg->{STATION_NUMBER},$date,$date,$clerk_filter,$sales_details,\%selections,$type);
return unless scalar%$info;
if(!-d"/opt/pos/server/data/sms"){mkdir"/opt/pos/server/data/sms";}my$t_wo_vat=$info->{sales_vat}->{total_wo_vat}||0;
my$t_w_vat=$info->{sales_vat}->{total}||0;
$t_total||=0;
$nt_total||=0;
my$r=rand(10000);
my$str=$SMS_PHONE?"&phone=$SMS_PHONE":"";
open(F,"> /opt/pos/server/data/sms/$r.txt");
print F"http://pos.m.net-bo.com/eod_send?t_wo_vat=$t_wo_vat&t_w_vat=$t_w_vat&t_total=$t_total"."&nt_total=$nt_total&m_id=$m_id&biz_date=$date&biz_name=".uri_escape(my_conv($biz_name))."&nif=$NIF&date_gen=".uri_escape(&strftime("%Y:%m:%d %H:%M:%S",localtime))."$str\n";
close(F);
_flush_msgs("/opt/pos/server/data/sms");}sub _flush_msgs{my$d=shift;
$ua->timeout(15);
&find(sub{return unless-f$File::Find::name;
local$/=undef;
open(F,"< $File::Find::name");
my$cnt=<F>;
$cnt=~s/\n//g;
close(F);
if(!$cnt){unlink$File::Find::name;
return;}print STDERR"WILL TRY: $_\n";
my$res=get($cnt);
$res||="";
print STDERR"SMS URL: $cnt\n";
if($res eq 'OK'){print STDERR"SENT - $File::Find::name\n";
unlink$File::Find::name;}else{print STDERR"$_ - ERROR SENDING, WILL REMOVE IT...\n";
unlink$File::Find::name;}},($d));}sub process_shutdown{my($machine,$file)=@_;
my($station)=$file=~/shutdown#(\d+)\./;
my$cfg=$machine->config()->load_fixed();
if($station!=$cfg->{STATION_NUMBER}){return 2;}if(-e"/opt/bin/shutdown.sh"){system('/opt/bin/shutdown');}else{system('/sbin/shutdown -h now "Power button pressed"');}}sub process_kill_application{my($machine,$file)=@_;
if(-e$file){my@lines=read_file($file);
if(scalar@lines){unlink$file;
system("ps xauf | grep gbr3 | grep -v grep | awk '{print \$2}' | xargs kill -9 2> /dev/null");
system("ps xauf | grep gbr2 | grep -v grep | awk '{print \$2}' | xargs kill -9 2> /dev/null");}}return 2;}sub process_scale{my($machine,$file)=@_;
if(open(F,"< $file")){local$/=undef;
my$barcode=<F>;
close(F);
_send_to_ui('SCALE',$barcode);
unlink$file;}}sub pdf_to_station_print{my($machine,$file)=@_;
my($station)=$file=~/pdf_to_station_print#(\d+)#/;
my$cfg=$machine->config()->load_fixed();
if($station!=$cfg->{STATION_NUMBER}){return 2;}print STDERR"Will print PDF - $file\n";
system("lpr \"$file\"");}sub set_owner_information{my$machine=shift;
my$h=shift;
if(!scalar keys%OWNER_INFO_CACHE){my$config;
if($machine->is_server()){$config=$machine->config();}else{$config=$machine->server_config();}my$cfg=$config->load_fixed();
$cfg->{SAFT_CompanyName}||="SAFT_CompanyName";
$cfg->{SAFT_AddressDetail}||="SAFT_AddressDetail";
$cfg->{SAFT_PostalCode}||="SAFT_PostalCode";
$cfg->{SAFT_City}||="SAFT_City";
$cfg->{SAFT_TaxRegistratioNumber}||="SAFT_TaxRegistratioNumber";
$cfg->{SAFT_CompanyId}||="SAFT_CompanyId";
$cfg->{SAFT_Country}||="SAFT_Country";
$OWNER_INFO_CACHE{owner_company_name}=$cfg->{SAFT_CompanyName};
$OWNER_INFO_CACHE{owner_business_name}=$cfg->{SAFT_BusinessName};
$OWNER_INFO_CACHE{owner_address}=$cfg->{SAFT_AddressDetail};
$OWNER_INFO_CACHE{owner_postal_code}=$cfg->{SAFT_PostalCode};
$OWNER_INFO_CACHE{owner_city}=$cfg->{SAFT_City};
$OWNER_INFO_CACHE{owner_nif}=$cfg->{SAFT_TaxRegistratioNumber};
$OWNER_INFO_CACHE{owner_company_id}=$cfg->{SAFT_CompanyId};
$OWNER_INFO_CACHE{owner_country}=$cfg->{SAFT_Country};}foreach my $k(keys%OWNER_INFO_CACHE){$h->{$k}=$OWNER_INFO_CACHE{$k};}}sub clerk_cashdrawer_decode{my$machine=shift;
my($station,$clerk,$manual,$pnum)=@_;
if(exists$CLERKS_DRAWER->{$clerk}){my$cfg=$machine->config()->load_fixed();
$cfg->{IGNORE_CLERK_DRAWER}||=0;
if($cfg->{IGNORE_CLERK_DRAWER}){return 1;}my@a=split/,/,$CLERKS_DRAWER->{$clerk};
if(scalar@a==3){my($new_station,$new_printer,$new_drawer_num)=@a;
my$dir_server_tmp=$machine->server_dir_tmp();
$new_printer--;
my$date=&strftime("%Y-%m-%d %H:%M:%S",localtime);
my$business_date=&date_based_on_closing_hour($date,$CLOSE_HOUR);
my$r=int(rand(10000));
my$new_file="$dir_server_tmp/drawer#$new_station#$clerk#$manual#$new_printer#$new_drawer_num#1#".$business_date."#".time."$r.txt";
&lazy_file_create($new_file,1,$machine);
return undef;}elsif(scalar@a==2){my($new_station,$new_drawer_num)=@a;
my$dir_server_tmp=$machine->server_dir_tmp();
my$new_printer=9999;
my$date=&strftime("%Y-%m-%d %H:%M:%S",localtime);
my$business_date=&date_based_on_closing_hour($date,$CLOSE_HOUR);
my$r=int(rand(10000));
my$new_file="$dir_server_tmp/drawer#$new_station#$clerk#$manual#$new_printer#$new_drawer_num#1#".$business_date."#".time."$r.txt";
&lazy_file_create($new_file,1,$machine);
return undef;}}return 1;}sub x64_txt_message{my($line1,$line2)=@_;
$line1||="";
$line2||="";
if($line1){$line1=substr($line1,0,16);
$line1=sprintf("%-16s",$line1);}if($line2){$line2=substr($line2,0,16);
$line2=sprintf("%-16s",$line2);}return$line1.$line2;}sub x64_log{my$msg=shift;
my$datetime=&strftime("%Y%m%d_%H%M%S",localtime);
open(FF,">> /root/logs/x64.log");
print FF"[$datetime] $msg\n";
close(FF);}sub running_business_date{my$machine=shift;
my$biz_date_file=$machine->dir_server()."/.business_date";
my$biz_date=undef;
if(-e$biz_date_file){local$/=undef;
open(F,"< $biz_date_file");
$biz_date=<F>;
chop($biz_date);
close(F);}return$biz_date;}sub _add_to_document_history{my($db,$info,$check,$cfg)=@_;
my%control=();
my$h=$info->{headers}||{};
return unless exists$DOCS_TO_HISTORY{$h->{doc_type}};
return if$h->{customer_nif}eq '999999990'||$h->{customer_nif}eq ''||$h->{customer_nif}eq '0';
if($h->{doc_type}==13){$check->{data}->{extra_data}->{to_customer_account}||='0';
if($check->{data}->{extra_data}->{to_customer_account}eq '0'){return;}else{my$invoice_is_payed=$check->{data}->{extra_data}->{invoice_is_payed}||'0';
return if$invoice_is_payed;}}my$date=$h->{business_date};
my$amount_payed=0;
my$doc_value=$h->{value};
$doc_value*=-1 if$h->{doc_type}==8||$h->{doc_type}==35;
if($h->{doc_type}==8||$h->{doc_type}==35){$info->{docs_payed}||=[];
foreach my $doc(@{$info->{docs_payed}}){next if exists$control{$doc->{gb_identifier}};
my$v=$doc->{doc_value}*1;
$db->do(qq{
        UPDATE document_history
        SET amount_payed = amount_payed + $v
        WHERE gbi = ? AND doc_type <> 5
      },$doc->{gb_identifier});
$control{$doc->{gb_identifier}}=1;}}elsif($h->{doc_type}==5||$h->{doc_type}==90){$amount_payed=$doc_value;}elsif($h->{doc_type}==30||$h->{doc_type}==31||$h->{doc_type}==32||$h->{doc_type}==33||$h->{doc_type}==34){$date=$check->{data}->{extra_data}->{supplier_doc_date};}elsif($h->{doc_type}==7){my$invoice_is_payed=$check->{data}->{extra_data}->{invoice_is_payed}||'0';
$amount_payed=$doc_value if$invoice_is_payed;}elsif($h->{doc_type}==13){my$invoice_is_payed=$check->{data}->{extra_data}->{invoice_is_payed}||'0';
$amount_payed=$doc_value if$invoice_is_payed;}my@params=($db->next_id("document_history"),$h->{id},$h->{__unique_id},$h->{customer_nif},$date,$h->{doc_type},$h->{fo_doc_number},&my_round($doc_value,$cfg->{DECIMAL_PLACES}||2),&my_round($amount_payed,$cfg->{DECIMAL_PLACES}||2));
$db->copy_to(qq{
    COPY document_history (id, header, gbi, nif, doc_date, doc_type, fo_doc_number, doc_value, amount_payed)
    FROM stdin
  },@params);}sub _serial_port_status{my$port=shift;
my$status=$port->modemlines;
printf STDERR ("Modem status = 0x%04X (DTR=%s CTS=%s RTS=%s DSR=%s RNG=%s CD=%s)\n",$status,($status& MS_DTR_ON)?"ON ":"off",($status& MS_CTS_ON)?"ON ":"off",($status& MS_RTS_ON)?"ON ":"off",($status& MS_DSR_ON)?"ON ":"off",($status& MS_RING_ON)?"ON ":"off",($status& MS_RLSD_ON)?"ON ":"off",);}sub get_cashier_names{my$machine=shift;
die"Missing 'machine' parameter..." unless defined$machine;
return if scalar%CASHIER_NAMES;
my$cfile=$machine->dir_server().'/etc/config.ini';
open(F,"< $cfile");
while(<F>){chop;
if(/^CASHIERS=/){my$l=$_;
$l=~s/^CASHIERS=//;
my@t=split/,/,$l;
$CASHIER_NAMES{$t[0]}=$t[1];}}}sub string_normalize{my$str=shift;
$str=~tr/áéíóúàèìòùâêîôûãõç/aeiouaeiouaeiouaoc/;
$str=~tr/ÁÉÍÓÚÀÈÌÒÙÂÊÎÔÛÃÕÇ/AEIOUAEIOUAEIOUAOC/;
return$str;}sub calcEAN13CD{my($sTxt)=@_;
my($i,$iSum);
my@aWeight=(1,3,1,3,1,3,1,3,1,3,1,3);
$iSum=0;
for($i=0;$i<12;$i++){$iSum+=substr($sTxt,$i,1)*$aWeight[$i];}$iSum%=10;
$iSum=($iSum==0)?0:(10-$iSum);
return"$iSum";}sub cache_refresh{my($machine,$file)=@_;
my$cfg=$machine->config()->load_fixed();
my$station=$cfg->{STATION_NUMBER}||"0";
my($station_num)=$file=~/cache_refresh#(\d+)\.txt$/;
if($station eq '0'){&lazy_file_create("/opt/pos/common/tmp/server_cache_refresh.txt",1);}if($station eq$station_num){print STDERR"> cache_refresh - $file\n";
my$db=$machine->database();
$PRODUCTS_TRANSLATIONS=undef;
$CLERKS_INFO=undef;
$PRODUCTS_INFO=undef;
$VAT_ISENTIONS=undef;
&get_products_information($db);
&get_clerks_information($db);
&get_products_translations($db);
$STATION_NAMES=_station_names($machine,1);
delete$machine->{__daily_specials};
delete$machine->{__stock_products};
delete$machine->{__products_suggestions};
return 1;}return 2;}sub _decode_delivery_info{my$db=shift;
my$data=shift;
my$info={delivery_notes=>[]};
$data->{extra_data}->{delivery_info}||="";
return$info unless$data->{extra_data}->{delivery_info};
$data->{header}||=[];
if(scalar@{$data->{header}}){my$nif=$data->{header}->[13]||"";
my$id=$data->{header}->[11]||"";
if($id){my$rec=$db->select("SELECT * FROM customers WHERE id = ?",$id);
$info->{customer}=$rec->[0]if scalar@$rec;}}my$str=$data->{extra_data}->{delivery_info};
my@recs=split/»«/,$str;
for(@recs){my@a=split/_/;
if(scalar@a>=2){$a[1]=~s/;/\n/g;
push@{$info->{delivery_notes}},{name=>$a[0],value=>$a[1]};}}return$info;}sub _get_doc_origin{my($machine,$headers,$relations)=@_;
my$r=[];
my$term=&translate($machine,"Origem");
my$db=$machine->database();
my%valid_docs=(1=>1,5=>1,90=>1,7=>1,2=>1,13=>1,33=>1,15=>1,);
my%lines_idx=();
if(%$relations){my@a=values%$relations;
map{$lines_idx{$_->{gbi}}=$_->{idx}}@a;}if(exists$valid_docs{$headers->{doc_type}}){my$recs=$db->select(qq{
      SELECT sh.fo_doc_number, sh.business_date, sh.gb_identifier
      FROM document_operations doco
      LEFT JOIN sales_headers sh ON (sh.gb_identifier = doco.source_gb_identifier)
      WHERE doco.dest_gb_identifier = ? AND sh.doc_type != 3
    },$headers->{gb_identifier});
for(@$recs){my$str=$term.": ".$_->{business_date}." ".$_->{fo_doc_number};
$str.=" [".$lines_idx{$_->{gb_identifier}}."]" if exists$lines_idx{$_->{gb_identifier}};
push@$r,$str;}}return$r;}sub _get_doc_bills{my($machine,$headers)=@_;
my$r=[];
my$biz_date=$headers->{business_date};
my($y,$m,$d)=$biz_date=~/^(\d\d\d\d)\-(\d\d)\-(\d\d)$/;
my$tbl1=$y.$m;
my$tbl2;
if($m*1==1){$y--;
$m=12;
$tbl2=$y.$m;}else{$m=$m*1-1;
$tbl2=$y.sprintf("%02d",$m);}my$term=&translate($machine,"Origem");
my$db=$machine->database();
$db->{show_sql_errors}=0;
my%valid_docs=(1=>1,5=>1,7=>1,90=>1,);
my%did;
if(exists$valid_docs{$headers->{doc_type}}){my$t1=new Benchmark;
my$recs;
$recs=[];
eval{$recs=$db->select(qq{
        SELECT fo_doc_number, business_date
        FROM sales_hashes_$tbl1
        WHERE gb_identifier_header = ? AND (doc_type = 6 OR doc_type = 9)
      },$headers->{gb_identifier});};
for(@$recs){push@$r,$term.": ".$_->{business_date}." - ".$_->{fo_doc_number};}my$t2=new Benchmark;
print STDERR"Handlers> _get_doc_bills> ".timestr(timediff($t2,$t1))." DEBUG 1\n" if$DEBUG_TIMINGS;
$recs=[];
eval{$recs=$db->select(qq{
        SELECT sl.*, sh.fo_doc_number, sh.business_date
        FROM split_docs_link sl
        LEFT JOIN sales_hashes_$tbl1 sh ON (sl.gbi_old = sh.gb_identifier_header)
        WHERE sl.gbi_new = ? AND sh.doc_type = 6
      },$headers->{gb_identifier});};
for(@$recs){my$k=$_->{fo_doc_number}."#".$_->{business_date};
next if exists$did{$k};
push@$r,$term.": ".$_->{fo_doc_number}." - ".$_->{business_date};
$did{$k}=1;}$recs=[];
my$t3=new Benchmark;
print STDERR"Handlers> _get_doc_bills> ".timestr(timediff($t3,$t1))." DEBUG 2\n" if$DEBUG_TIMINGS;
eval{$recs=$db->select(qq{
        SELECT fo_doc_number, business_date
        FROM sales_hashes_$tbl2
        WHERE gb_identifier_header = ? AND (doc_type = 6 OR doc_type = 9)
      },$headers->{gb_identifier});};
for(@$recs){push@$r,$term.": ".$_->{fo_doc_number}." - ".$_->{business_date};}my$t4=new Benchmark;
print STDERR"Handlers> _get_doc_bills> ".timestr(timediff($t4,$t1))." DEBUG 3\n" if$DEBUG_TIMINGS;
$recs=[];
eval{$recs=$db->select(qq{
        SELECT sl.*, sh.fo_doc_number, sh.business_date
        FROM split_docs_link sl
        LEFT JOIN sales_hashes_$tbl2 sh ON (sl.gbi_old = sh.gb_identifier_header)
        WHERE sl.gbi_new = ? AND sh.doc_type = 6
      },$headers->{gb_identifier});};
for(@$recs){my$k=$_->{fo_doc_number}."#".$_->{business_date};
next if exists$did{$k};
push@$r,$term.": ".$_->{fo_doc_number}." - ".$_->{business_date};
$did{$k}=1;}my$t5=new Benchmark;
print STDERR"Handlers> _get_doc_bills> ".timestr(timediff($t5,$t1))." DEBUG 4\n" if$DEBUG_TIMINGS;}$db->{show_sql_errors}=1;
return$r;}sub _print_file{my($machine,$station,$str)=@_;
my($print_file,$station_to_print)=&_find_file_to_print(machine=>$machine,station=>$station,);
if(defined$print_file){open(F,"> $print_file");
print F$str;
close(F);}else{print STDERR"ERROR! CAN'T FIND PRINTER TO USE!\n";}}sub _send_to_ui{my($action,$value)=@_;
$action||='';
return unless$action&&defined$value;
my$sock=IO::Socket::INET->new(PeerAddr=>'127.0.0.1',PeerPort=>'8088',Proto=>'tcp',Timeout=>5,);
if(defined$sock&&$sock){print$sock "#$action#$value\n" if defined$sock&&$sock;
$sock->close();}else{print STDERR"_send_to_ui - $action - error connecting to port 8088\n";}}sub _send_product_request_to_ui{my($machine,$station,$clerk,$area,$table,$product,$qty)=@_;
my$cfg=$machine->config()->load_fixed();
my$sock=IO::Socket::INET->new(PeerAddr=>$machine->is_server()?'127.0.0.1':$cfg->{NETWORK_SERVER_IP},PeerPort=>'8088',Proto=>'tcp',Timeout=>5,);
if(defined$sock&&$sock){print$sock "#PRODUCT_REQUEST#$station#$clerk#$area#$table#$product#$qty\n" if defined$sock&&$sock;
$sock->close();}else{print STDERR"_send_product_request_to_ui - error connecting to port 8088\n";}}sub _read_queue_number{my($machine,$name)=@_;
my$dir_server_data=$machine->server_dir_data();
my$queue_file="$dir_server_data/queues/$name.txt";
return 0 unless-e$queue_file;
if(open(F,"< $queue_file")){local$/=undef;
my$num=<F>;
$num=~s/\n//g;
close(F);
return$num;}return 0;}sub _replace_saft_tags{my$cfg=shift;
my$cnt_ref=shift;
my@saft_tags=qw/
  SAFT_BusinessName SAFT_CompanyName SAFT_CompanyId SAFT_AddressDetail
  SAFT_PostalCode SAFT_City SAFT_TaxRegistratioNumber
  /;
foreach my $tag(@saft_tags){my$v=$cfg->{$tag}||"";
$$cnt_ref=~s/\@$tag\@/$v/g if$v;}}sub _file_md5{my$f=shift;
my$cnt="";
if(open(F,"< $f")){local$/=undef;
$cnt=<F>;
close(F);}return md5_hex($cnt);}sub _pdf_gen{my($cfg,$html_f,$pdf_f,$email,$invoice_num)=@_;
return undef unless defined$html_f&&defined$pdf_f;
my$r1=$$.int(rand(10000));
my$r2=$$.int(rand(10000));
my$pdf_tmp="/opt/pos/common/tmp/$r1.pdf";
system("export DISPLAY=:0.0 ; wkhtmltopdf --footer-center [page]/[topage] --page-size A4 \"$html_f\" \"$pdf_tmp\"");
unlink($html_f);
if(-e"/usr/bin/pdftk"){my$cmd="/usr/bin/pdftk \"".$pdf_tmp."\" output /opt/pos/common/tmp/$r2.pdf owner_pw $r2";
system($cmd);
move("/opt/pos/common/tmp/$r2.pdf",$pdf_f);}else{move($pdf_tmp,$pdf_f);}if(defined$email&&defined$invoice_num){my$LANG=$cfg->{LANG}||"";
my$SAFT_TaxRegistratioNumber=$cfg->{SAFT_TaxRegistratioNumber}||"";
my$SAFT_BusinessName=$cfg->{SAFT_BusinessName}||"";
my$stat=send_mail(to=>$email,subject=>"[$LANG] $SAFT_BusinessName / $invoice_num",content=>"$LANG<br>====<p>$SAFT_BusinessName<p>$SAFT_TaxRegistratioNumber<br>",attachment=>[$pdf_f],);
unlink$pdf_f;}return 1;}sub _group_products{my$products=shift;
my@final=();
my@tmp=();
my%i=();
my$have_printing_priority=0;
my$last_print_priority=0;
my$prev_was_extra=0;
foreach my $p(reverse sort{$a->{order}<=>$b->{order}}@$products){my$id=$p->{product_code};
my$printing_priority=exists$PRINTING_PRIORITIES->{$id}?$PRINTING_PRIORITIES->{$id}:0;
$have_printing_priority=1 if$printing_priority;
if(!$p->{is_extra}){$last_print_priority=$printing_priority;}else{$printing_priority=0;
$printing_priority=$last_print_priority unless$printing_priority;}if($p->{is_extra}){$prev_was_extra=1;
push@tmp,$p;}else{if($prev_was_extra){$prev_was_extra=0;
push@tmp,$p;}else{$i{$p->{product_code}}->{idx}=$p->{idx};
$i{$p->{product_code}}->{product_info}=$p->{product_info};
$i{$p->{product_code}}->{quantity}+=$p->{quantity};
$i{$p->{product_code}}->{hour}=$p->{hour};
$i{$p->{product_code}}->{is_weight}=$p->{is_weight};
$i{$p->{product_code}}->{station}=$p->{station};
$i{$p->{product_code}}->{station_name}=exists$STATION_NAMES->{$p->{station}}?$STATION_NAMES->{$p->{station}}:$p->{station};
$i{$p->{product_code}}->{area}=$p->{area};
$i{$p->{product_code}}->{vat}=0;
$i{$p->{product_code}}->{price}=0;
$i{$p->{product_code}}->{foreign}=$p->{foreign};
$i{$p->{product_code}}->{foreign_orig}=$p->{foreign_orig};
$i{$p->{product_code}}->{clerk_name}=$p->{clerk_name};
$i{$p->{product_code}}->{clerk_code}=$p->{clerk_code};
$i{$p->{product_code}}->{product_code}=$p->{product_code};
$i{$p->{product_code}}->{product_menu}=$p->{product_menu};
$i{$p->{product_code}}->{product_name}=$p->{product_name};
$i{$p->{product_code}}->{handy_plu}=$p->{handy_plu};
$i{$p->{product_code}}->{order}=$p->{order};
$i{$p->{product_code}}->{key}=$p->{key};
$i{$p->{product_code}}->{printing_priority}=$printing_priority;
$i{$p->{product_code}}->{is_extra}=$p->{is_extra};
$i{$p->{product_code}}->{is_menu}=$p->{is_menu};}}}@final=values%i;
push@final,@tmp if scalar@tmp;
@final=sort{$a->{order}<=>$b->{order}}@final;
return\@final;}sub _group_products_by_util_name{my$prods=shift;
my$last_name="";
foreach my $p(@$prods){my$name=$p->{group_key}?$p->{group_key}:"";
if(($p->{is_extra}||$p->{is_menu}||$p->{is_message})&&!$name){$name=$last_name;}$p->{group_key}=$name;
$p->{util_grouping_key}=$name;
$last_name=$name;}my$last_grp="";
my@new_prods=();
my%group_qttys=();
my$idx=0;
foreach my $p(sort{$a->{group_key}cmp$b->{group_key}}@$prods){my$next_p=$prods->[$idx+1];
if($last_grp ne$p->{group_key}){$p->{util_grouping_key_changed}=1;}$last_grp=$p->{group_key};
push@new_prods,$p;
$idx++;}return\@new_prods;}sub _network_printer_decode{my$ip=shift;
if($ip=~/:/){my@a=split/:/,$ip;
my$ip2=trim_string($a[0]);
my$port=9100;
if(scalar@a>1){$port=trim_string($a[1]);}return($ip2,$port);}else{return($ip,9100);}}sub _write_slow_buffer{my($device,$sleep_time,$lines)=@_;
my$c=0;
foreach my $l(@$lines){my$str=$l;
while(my$ss=substr($str,0,10,'')){if(open(Z,"> $device")){print Z$ss;
close(Z);}else{print STDERR"ERROR WRITING TO: $device - $!\n";}}if(open(Z,"> $device")){print Z"\n";
close(Z);}else{print STDERR"ERROR WRITING TO: $device - $!\n";}if($c==10){$c=0;
sleep$sleep_time;}$c++;}if(-e$device){if(open(N,"> $device")){print N"";
close(N);}else{print STDERR"ERROR WRITING TO: $device - $!\n";}}}sub _gen_taxfree_xml{my($machine,$check,$info)=@_;
my$ex=$check->extra_data();
my$refund_total=sprintf("%.2f",$ex->{taxfree_total});
my$fo_doc_number=$info->{check}->{headers}->{fo_doc_number};
my$hash_small=$info->{check}->{headers}->{hash_small};
my$date=$info->{check}->{headers}->{business_date};
my($type)=$fo_doc_number=~/^(.*?) /;
my$cfg=$machine->config()->load_fixed();
my$taxfree_class=$cfg->{TAXFREE_CLASS}||"";
($taxfree_class)=$taxfree_class=~/^(.*?)\-/ if$taxfree_class;
my$store_name=$cfg->{SAFT_BusinessName}||"";
my$store_nif=$cfg->{SAFT_CompanyId}||"";
my$buyer_name=$ex->{taxfree_name}||"";
my$buyer_passport=$ex->{taxfree_passport}||"";
my$buyer_birthdate=$ex->{taxfree_birthdate}||"";
my$buyer_passport_country_tmp=$ex->{taxfree_country_passport}||"";
my$buyer_passport_country="";
if($buyer_passport_country_tmp){my@tmp=split/\-/,$buyer_passport_country_tmp;
$buyer_passport_country=$tmp[-1];}my$buyer_country_tmp=$ex->{taxfree_country}||"";
my$buyer_country="";
if($buyer_country_tmp){my@tmp=split/\-/,$buyer_country_tmp;
$buyer_country=$tmp[-1];}my$atcud=$ex->{atcud}||"";
my$xml1= <<END;
<?xml version="1.0" encoding="utf-8"?>
<TaxFreeFileUpload xmlns="http://servicos.portaldasfinancas.gov.pt/eTaxFree/TaxFreeFileUpload">
  <Seller>
    <TaxRegistrationNumber>$store_nif</TaxRegistrationNumber>
    <BusinessName>$store_name</BusinessName>
  </Seller>
  <Buyer>
    <Name>$buyer_name</Name>
    <IdentityDoc>
      <Type>PASSAPORTE</Type>
      <Number>$buyer_passport</Number>
      <CountryCode>$buyer_passport_country</CountryCode>
    </IdentityDoc>
    <ResidenceCountryCode>$buyer_country</ResidenceCountryCode>
    <BirthDate>$buyer_birthdate</BirthDate>
  </Buyer>
  <Refund>
    <TransactionType>Com Pagamento de Caução</TransactionType>
    <RefundableAmount>$refund_total</RefundableAmount>
  </Refund>
  <Invoice>
    <!--You have a CHOICE of the next 2 items at this level-->
    <ATCUD>$atcud</ATCUD>
    <InvoiceIdentifiers>
      <InvoiceNo>$fo_doc_number</InvoiceNo>
      <HashTermination>$hash_small</HashTermination>
    </InvoiceIdentifiers>
    <InvoiceType>$type</InvoiceType>
    <InvoiceDate>$date</InvoiceDate>
    <CalculatedTaxes>
      <!--1 or more repetitions:-->
END
my$taxes=$ex->{taxfree_vats};
my@recs=split/,/,$ex->{taxfree_vats};
foreach my $r(@recs){my($rate,$amount)=split/=/,$r;
$xml1.="      <CalculatedTax>\n";
$xml1.="        <TaxPercentage>".sprintf("%.2f",$rate)."</TaxPercentage>\n";
$xml1.="        <TaxAmount>".sprintf("%.2f",$amount)."</TaxAmount>\n";
$xml1.="      </CalculatedTax>\n";}my$xml2= <<END;
    </CalculatedTaxes>
    <InvoiceLinesList>
      <!--1 or more repetitions:-->
END
foreach my $d(@{$info->{check}->{details}}){my$amount=$d->{price_wo_vat_taxfree}-$d->{discount_wo_vat_taxfree};
my$name=$PRODUCTS_INFO->{$d->{product}}->{name};
$xml2.="      <InvoiceLine>\n";
$xml2.="        <ProductClass>$taxfree_class</ProductClass>\n";
$xml2.="        <ProductDescription>$name</ProductDescription>\n";
$xml2.="        <Quantity>".sprintf("%.3f",$d->{quantity})."</Quantity>\n";
$xml2.="        <TaxBaseTotal>".sprintf("%.2f",$amount*$d->{quantity})."</TaxBaseTotal>\n";
$xml2.="      </InvoiceLine>\n";}my$xml3= <<END;
    </InvoiceLinesList>
  </Invoice>
</TaxFreeFileUpload>
END
my$xml4=$xml1.$xml2.$xml3;
$xml4=~s/&/&amp;/g;
my$path="/home/pos/TAXFREE/".strftime("%Y/%m/%d",localtime);
make_path($path)unless-d$path;
if(-d$path){my$file=$fo_doc_number;
$file=~s/\//_/g;
$file=~s/\s/_/g;
if(open(F,"> $path/$file.xml")){print F$xml4;
close(F);}else{print STDERR"Error writing to file $path/$file.xml\n";}}else{print STDERR"Error creating folder $path\n";}return 1;}sub _load_printer_errors_config{my$machine=shift;
return$machine->{__printer_errors_config}if exists$machine->{__printer_errors_config};
my$cfg=$machine->config->load();
my$ref={};
$cfg->{ORDER_PRINTER_ERRORS_CONFIG}//=[];
if(scalar@{$cfg->{ORDER_PRINTER_ERRORS_CONFIG}}){foreach my $rule(@{$cfg->{ORDER_PRINTER_ERRORS_CONFIG}}){my@a=split/,/,$rule;
next if scalar@a<4;
my($printer,$st_origin,$st_destination,$to_printer)=($a[0],$a[1],$a[2],$a[3]);
$printer//=0;
$st_origin//=0;
$st_destination//=0;
$to_printer//=0;
$machine->{__printer_errors_config}->{$printer}->{$st_origin}={station=>$st_destination,printer=>$to_printer,};}}else{$machine->{__printer_errors_config}={};}return$machine->{__printer_errors_config};}sub _get_socket{my($printer_ip,$printer_port)=@_;
my$sock=IO::Socket::INET->new(PeerAddr=>$printer_ip,PeerPort=>$printer_port,Proto=>'tcp',Timeout=>2,);
if(defined$sock&&$sock){return$sock;}return undef;}sub _add_custom_top_and_bottom{my($machine,$area,$cnt_ref)=@_;
my$dir=$machine->dir_server()."/common/tmp";
if(-e"$dir/print_top.txt"){my$cnt=read_file("$dir/print_top.txt");
$$cnt_ref=$cnt.$$cnt_ref;}if(-e"$dir/print_top#$area.txt"){my$cnt=read_file("$dir/print_top#$area.txt");
$$cnt_ref=$cnt.$$cnt_ref;}if(-e"$dir/print_bottom.txt"){my$cnt=read_file("$dir/print_bottom.txt");
$$cnt_ref=$$cnt_ref.$cnt;}if(-e"$dir/print_bottom#$area.txt"){my$cnt=read_file("$dir/print_bottom#$area.txt");
$$cnt_ref=$$cnt_ref.$cnt;}}sub _fix_sequences{my$db=shift;
$db->{show_sql_errors}=0;
my$business_date=&date_based_on_closing_hour(strftime("%Y-%m-%d %H:%M:%S",localtime),$CLOSE_HOUR);
my@date_split=split/\-/,$business_date;
my$seqs=$db->select("SELECT sequence_name FROM information_schema.sequences");
foreach my $seq(@$seqs){my($table)=$seq->{sequence_name}=~/^(.*?)_id_seq$/;
($table)=$seq->{sequence_name}=~/^(.*?)_seq$/ unless defined$table;
next unless defined$table;
my$table_name=$table."_".$date_split[0].$date_split[1];
my$id=[];
eval{$id=$db->select("SELECT MAX(id) AS id FROM $table_name")};
if($@){eval{$id=$db->select("SELECT MAX(id) AS id FROM $table")};}next unless scalar@$id;
next unless defined$id->[0]->{id};
$db->do("ALTER SEQUENCE ".$seq->{sequence_name}." RESTART WITH ".($id->[0]->{id}+1));}$db->{show_sql_errors}=1;}sub _station_names{my$machine=shift;
my$ignore_cache=shift||0;
my$cfg=$machine->config()->load_fixed();
return$machine->{__station_names}if exists$machine->{__station_names}&&!$ignore_cache;
my%station_names=();
if($cfg->{STATION_NUMBER}==0){$station_names{0}=$cfg->{STATION_NAME}?$cfg->{STATION_NAME}:"0";}else{my$cfg=App::POS::Config->new(file=>$machine->dir_server()."/etc/config.ini");
my$info=$cfg->load_fixed();
$station_names{0}=$info->{STATION_NAME}?$info->{STATION_NAME}:"0";}find(sub{my$station_num=$_;
return unless$station_num=~/^\d+$/;
open(F,"< $File::Find::name");
my@lines=<F>;
my$station_name=$lines[1]||"";
if($station_name){$station_name=~s/\n//g;
$station_name=~s/\r//g;
$station_name=$station_num unless$station_name;
$station_names{$station_num}=$station_name;}else{$station_names{$station_num}=$station_num;}close(F);},($machine->dir_server().'/server/data/registered_stations'));
$machine->{__station_names}=\%station_names;
return\%station_names;}sub _apply_qrcode_to_doc{my($cfg,$cnt2,$tag,$qrcode,$ip_redirect)=@_;
$cfg->{INVOICE_QRCODES_AS_BMP}//=0;
$cfg->{INVOICE_QRCODES_TURN_OFF}//=0;
if($cfg->{INVOICE_QRCODES_TURN_OFF}){$qrcode="";}else{if($cfg->{INVOICE_QRCODES_AS_BMP}){if($cfg->{INVOICE_QRCODES_AS_BMP}==1){open(F,"> /tmp/qrcode.$$.png");
binmode(F);
print F$$qrcode;
close(F);
system("convert /tmp/qrcode.$$.png ppm:- | convert - BMP3:/tmp/qrcode.$$.bmp");
my$o=App::POS::Hardware::PrinterLogo->new(image=>"/tmp/qrcode.$$.bmp",use_cache=>0,);
$$qrcode=$o->image_data();
unlink"/tmp/qrcode.$$.png";
unlink"/tmp/qrcode.$$.bmp";}else{open(TT,"> /opt/pos/common/tmp/qrcode.png");
binmode(TT);
print TT$$qrcode;
close(TT);
my$prog="/opt/pos/common/bin/raster-bmp";
$prog="/opt/pos/common/bin/raster-bmp.php" if-e"/usr/bin/php";
$$qrcode=`$prog /opt/pos/common/tmp/qrcode.png`;}}}if($$qrcode){if($ip_redirect!~/android/){my$qr=$$qrcode;
$$cnt2=~s/$tag[\n]?/\@justify_center\@$qr/mg;}else{open(F,"> /tmp/qrcode.png");
binmode(F);
print F$$qrcode;
close(F);
$$cnt2=~s/\@qrcode\@[\n]?/#qrcode#/mg;}}else{$$cnt2=~s/\@qrcode\@[\n]?//mg;}}sub _atcud_checks{my$machine=shift;
my$cfg=shift;
$cfg->{SAFT_SERIES_CODES}||=[];
$cfg->{SAFT_SERIES_AUTO_REGISTER}||=0;
my$cur_serie=$cfg->{DOC_TYPE_SERIE}||"";
return unless$cur_serie;
if($machine->software_version>=202101){$cur_serie=~s/\@YEAR\@//g;}else{my$year=&strftime("%Y",localtime);
$cur_serie=~s/\@YEAR\@/$year/g;}my%exists;
my$need_register_series=0;
foreach my $atcud(@{$cfg->{SAFT_SERIES_CODES}}){my@a=split/,/,$atcud;
next unless scalar@a;
my$at_code=$a[1]||'';
if(!$need_register_series&&!$at_code){$need_register_series=1;}if($a[0]=~/ $cur_serie$/){$exists{$a[0]}=$at_code;}elsif($a[0]=~/ ${cur_serie}M$/){$exists{$a[0]}=$at_code;}elsif($a[0]=~/ ${cur_serie}D$/){$exists{$a[0]}=$at_code;}}if(!scalar@{$cfg->{SAFT_SERIES_CODES}}){$need_register_series=1;}if(!$cfg->{SAFT_SERIES_AUTO_REGISTER}){$need_register_series=0;}$cfg->{SAFT_TaxRegistratioNumber}||="";
$cfg->{SAFT_AT_LOGIN}||="";
$cfg->{SAFT_AT_PASSWD}||="";
if(!$cfg->{SAFT_TaxRegistratioNumber}||!$cfg->{SAFT_AT_LOGIN}||!$cfg->{SAFT_AT_PASSWD}){$need_register_series=0;}else{my@a=split/\//,$cfg->{SAFT_AT_LOGIN};
if($a[0]ne$cfg->{SAFT_TaxRegistratioNumber}){print STDERR"NIF $cfg->{SAFT_TaxRegistratioNumber} DOESNT MATCH AT LOGIN NIF $a[0], aborting...\n";
return;}}my@lines=read_file("/opt/pos/etc/config.ini");
my$added=0;
if(!exists$exists{"NC ".$cur_serie."M"}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=NC ".$cur_serie."M,\n";}if(!exists$exists{"FR ".$cur_serie."M"}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=FR ".$cur_serie."M,\n";}if(!exists$exists{"FT ".$cur_serie."M"}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=FT ".$cur_serie."M,\n";}if(!exists$exists{"FS ".$cur_serie."M"}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=FS ".$cur_serie."M,\n";}if(!exists$exists{"NC ".$cur_serie."D"}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=NC ".$cur_serie."D,\n";}if(!exists$exists{"FR ".$cur_serie."D"}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=FR ".$cur_serie."D,\n";}if(!exists$exists{"FT ".$cur_serie."D"}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=FT ".$cur_serie."D,\n";}if(!exists$exists{"FS ".$cur_serie."D"}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=FS ".$cur_serie."D,\n";}if(!exists$exists{"RG ".$cur_serie}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=RG ".$cur_serie.",\n";}if(!exists$exists{"CM ".$cur_serie}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=CM ".$cur_serie.",\n";}if(!exists$exists{"GT ".$cur_serie}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=GT ".$cur_serie.",\n";}if(!exists$exists{"NC ".$cur_serie}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=NC ".$cur_serie.",\n";}if(!exists$exists{"FR ".$cur_serie}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=FR ".$cur_serie.",\n";}if(!exists$exists{"FT ".$cur_serie}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=FT ".$cur_serie.",\n";}if(!exists$exists{"FS ".$cur_serie}){$added=1;
unshift@lines,"SAFT_SERIES_CODES=FS ".$cur_serie.",\n";}if($added){my$new_config=join("",@lines);
write_file("/opt/pos/etc/config.ini",$new_config);}if($need_register_series){my$cmd='';
if($machine->is_64_bits()){$cmd="/opt/pos/common/addons/at_comms/app.64bits series";}else{$cmd="/opt/pos/common/addons/at_comms/app series";}print STDERR"Will execute - $cmd\n";
system("$cmd");}if($added||$need_register_series){system('ps xa | grep "/opt/pos/common/bin/pos" | grep -v grep | awk \'{ print $1 }\' | sudo xargs kill -9 2> /dev/null');}}1;
