package App::POS::Machine::Server;
use strict;
use warnings;
use Encode qw( from_to decode encode is_utf8 );
use UUID::Tiny;
use File::Slurp qw( read_dir write_file read_file );
use File::Copy qw( copy move );
use File::Path qw( make_path remove_tree );
use File::Remove qw( remove );
use DBI;
use File::Spec;
use File::Find qw( find );
use POSIX qw( strftime floor );
use Date::Calc qw( Date_to_Time Delta_Days Add_Delta_Days );
use Data::Dumper;
use POSIX qw( ceil );
use Number::Format;
use Benchmark ':hireswallclock';
use IO::Socket;
use JSON::XS;
use App::POS::Check;
use App::POS::Counters;
use App::POS::Utils qw(
  translate my_round my_conv date_based_on_closing_hour hardlock_id
  memcache fix_encoding trim
);
my%MEMC_CONNECTIONS;
my%DELIVERY_AREAS=();
my%DID_STOCK_IDS_ALERT=();
my%TRANSLATION_CACHE=();
my$NFORMATER=Number::Format->new();
my$DEBUG_TIMINGS=0;
my$HARDLOCK_ID="";
use base qw( App::POS::Machine );
my%EXTRA_DATA_IGNORE=(has_pzones=>1,internal_print=>1,station_print_checks=>1,was_external_doc=>1,assoc_transport_doc=>1,did_real_hold=>1,custom_table_number=>1,points_total_before=>1,points_current=>1,dont_group_lines=>1,print_to_simplified_invoice=>1,missing_products_to_offer=>1,value_already_offered=>1,vnc_hardware_ignore=>1,points_cloud_error=>1,points_check_total=>1,customer_dont_use_points=>1,service_charge_no_auto=>1,in_payment=>1,delivery_no_invoice=>1,dont_restore_stock=>1,print_in_footer=>1,deliver_after_printed=>1,keep_table_name=>1,dont_apply_vat_isentions=>1,send_to_network_printer=>1,hardcode_product_names=>1,rfid_random=>1,disco_activated=>1,);
my%ATCUD_DOCSTR_BY_TYPE=(7=>'FT',5=>'FR',90=>'FS',13=>'NC',8=>'RG',10=>'GT',6=>'CM',);
sub init{my$self=shift;
$self->debug("BUILD");
my$c=$self->config()->load();
my$network_ip=$self->vfh($c,'NETWORK_IP');
my$gateway=$self->vfh($c,'NETWORK_GATEWAY');
if(!-d$self->dir_tmp()){make_path($self->dir_tmp());}if(!-d$self->dir_registered_stations()){make_path($self->dir_registered_stations());}if(!-d$self->dir_registered_printers()){make_path($self->dir_registered_printers());}if(!-d$self->dir_opened_cashiers()){make_path($self->dir_opened_cashiers());}}sub server_ip{"127.0.0.1"}sub dir_server{my$self=shift;
return$self->dir();}sub dir_registered_stations{my$self=shift;
return$self->dir_server().'/server/data/registered_stations';}sub dir_opened_cashiers{my$self=shift;
return$self->dir_server().'/server/data/opened_cashiers';}sub dir_queues{my$self=shift;
return$self->dir_server().'/server/data/queues';}sub dir_registered_printers{my$self=shift;
return$self->dir_server().'/server/data/registered_stations';}sub dir_database{my$self=shift;
return$self->dir().'/server/data/database';}sub dir_apps{my$self=shift;
return$self->dir().'/server/apps';}sub registered_stations{my$self=shift;
my@stations=&read_dir($self->dir_registered_stations());
return\@stations;}sub database_host{return"127.0.0.1";}sub ip{my$self=shift;
$self->debug("ip");
my$c=$self->config()->load();
return$c->{NETWORK_IP};}sub setup{my$self=shift;
$self->debug("setup");
if($^O eq 'MSWin32'){$self->__setup_win32();}else{$self->__setup_linux();}}sub integrate_check_in_db{my$self=shift;
my$file=shift;
my$products=shift;
my$clerks=shift;
my$check=shift;
my$t1=new Benchmark;
my$cnt=0;
my$db=$self->database();
my$dbh=$db->dbh();
my$db_dir=$self->dir_database();
my$c=$self->config()->load_fixed();
my$child_products=$self->_get_child_products($db);
my$pt_certified_software=-e$self->dir()."/common/skins/".$c->{LANG}."/sign_server"?1:0;
$c->{LOG_POS_DEBUG}||=0;
$c->{VOIDS_REGISTER}||=0;
$c->{PRODUCTS_SUGESTION}||=0;
$c->{PRODUCTS_SUGESTION_EXTRAS_ONLY}||=0;
$c->{DOC_NUMBERS_USE_SERIE}||=0;
$c->{MINIMAL_CONSUMPTION_PRODUCT}||="";
$c->{LOYALTY}||=0;
$c->{POINTS_USAGE}||=0;
$c->{POINTS_SYNC_TOKEN}||="";
$c->{POINTS_VALUE_PER_POINT}||=0;
$c->{POINTS_FOR_APP_ONLY}||=0;
$c->{INTERNET_DASHBOARD}||=0;
$c->{STOCK_MIN_ALLWAYS_WARNING}||=0;
$c->{AUTO_VOUCHER_ABOVE}||=0;
$c->{AUTO_VOUCHER_ASSIGN_WHEN_USE}||=0;
$c->{VOUCHERS_AS_MEDIA_PAYMENT}||=0;
$c->{DECIMAL_PLACES}||=2;
if($c->{LOYALTY}){$c->{POINTS_USAGE}=0;
$c->{POINTS_SYNC_TOKEN}="";}my%auto_voucher_info=();
if($c->{AUTO_VOUCHER_ABOVE}){if(ref($c->{AUTO_VOUCHER_ABOVE})eq 'ARRAY'){foreach my $r(@{$c->{AUTO_VOUCHER_ABOVE}}){my@a=split/,/,$r;
$a[0]=~s/[^\d.]//g;
$a[1]=~s/[^\d.%]//g;
if($a[0]&&$a[1]){$auto_voucher_info{$a[0]}=$a[1];}}}else{my@a=split/,/,$c->{AUTO_VOUCHER_ABOVE};
if(scalar@a==2){$a[0]=~s/[^\d.]//g;
$a[1]=~s/[^\d.%]//g;
if($a[0]&&$a[1]){$auto_voucher_info{$a[0]}=$a[1];}}}}my$areas_info=$self->config()->areas_info();
my$mc_product_id=undef;
if($c->{MINIMAL_CONSUMPTION_PRODUCT}ne ''){if(!exists$self->{__mc_product_id}||-e"/opt/pos/common/tmp/server_cache_refresh.txt"){my$r=$db->select("SELECT id FROM products WHERE name = ? AND deleted = 0",$c->{MINIMAL_CONSUMPTION_PRODUCT});
$self->{__mc_product_id}=$r->[0]->{id}if scalar@$r;
$mc_product_id=$self->{__mc_product_id};}else{$mc_product_id=$self->{__mc_product_id};}}my$daily_specials=$self->daily_specials();
my$query="";
my$media_ignore="Change";
$c->{CHECK_MONEY_CHANGE_TEXT}||="";
if($c->{CHECK_MONEY_CHANGE_TEXT}){$media_ignore=&translate($self,$c->{CHECK_MONEY_CHANGE_TEXT});}my$media_total_not_voucher=0;
my$external_doc="";
my$lang=$self->vfh($c,'LANG');
print STDERR"INTEGRATE: $file\n";
my$data=$check->data();
my$cur_date=$check->business_date();
$cur_date=~s/\-//g;
my$h=$data->{header}||[];
my$details=$data->{details}||[];
my$details_deleted=$data->{details_deleted}||[];
my$footers=$data->{footers}||[];
my$info={};
my$check_value=$h->[19];
if(!scalar@$footers||(scalar@$footers==1&&!scalar@{$footers->[0]})){print STDERR"footers\n";
print STDERR Dumper($footers);
push@{$footers},[&translate($self,"DINHEIRO"),$check_value,'',];}if((!scalar@$details||(scalar@$details==1&&!scalar@{$details->[0]}))&&(!scalar@$details_deleted||(scalar@$details_deleted==1&&!scalar@{$details_deleted->[0]}))){print STDERR"details\n";
print STDERR Dumper($details);
print STDERR Dumper($details_deleted);
return undef;}my@details_extra_data=();
my$check_hour=&strftime("%H:%M:%S",localtime);
my$business_date=$check->business_date();
my$table_number=$check->table_number();
my$extra_data=$check->extra_data();
my$products_extra_data_tmp=$check->products_extra_data();
my$custom_invoice_id=exists$extra_data->{invoice_id}?$extra_data->{invoice_id}:"";
my$cdd=&strftime("%Y-%m-%d",localtime);
my$cdh=$check_hour;
my$close_date="$cdd $cdh";
$extra_data->{keep_table_name}//=0;
$extra_data->{hardcode_product_names}||="";
my%product_hardcoded_names=();
if($extra_data->{hardcode_product_names}){my@a=split/§/,$extra_data->{hardcode_product_names};
foreach my $r(@a){my@b=split/»/,$r;
next unless scalar@b==2;
$product_hardcoded_names{$b[0]}=$b[1];}}my%products_extra_data=();
my@products_extra_data_insert=();
foreach my $gbi(keys%{$products_extra_data_tmp}){$products_extra_data{$gbi}=$products_extra_data_tmp->{$gbi};}$table_number=-1 if$table_number!~/^\d+$/;
$extra_data->{original_table_number}||='';
if($extra_data->{original_table_number}&&$extra_data->{original_table_number}=~/^\d+$/){$table_number=$extra_data->{original_table_number}if$extra_data->{original_table_number};}my$orig_table_number=$table_number;
my$doc_type="";
my$mult=1;
$doc_type=$h->[8];
my$clerk_serie=1;
my$doc_serie=$self->decode_doctype_serie();
if(exists$extra_data->{manualdoc_type}){if($extra_data->{manualdoc_type}eq 'manual'){$doc_serie=$doc_serie."M";}else{$doc_serie=$doc_serie."D";}}else{if($doc_type==7&&$self->software_version<202101){if(!exists$extra_data->{invoice_is_payed}||$extra_data->{invoice_is_payed}==0){$extra_data->{to_customer_account}=1;
$doc_serie.="C";}else{$doc_serie.="P";}}elsif($doc_type==7){$extra_data->{to_customer_account}=1;}}my$t1_1=new Benchmark;
print STDERR"Server> ".timestr(timediff($t1_1,$t1))." INTEGRATE CHECK 1.1:".timestr(timediff($t1_1,$t1))."\n" if$DEBUG_TIMINGS;
my$db_customer_info={};
my$areas_validation=$self->_areas_with_validation_required();
if(scalar@$h){my$products_suggest=App::POS::Counters->new(db=>$db,name=>'products_suggest',);
if(-d$self->dir()."/server/data/day_change"&&-e$self->dir()."/server/data/day_change/$business_date.export"){my($year,$month,$day)=$business_date=~/^(\d+)\-(\d+)\-(\d+)$/;
my($y,$m,$d)=Add_Delta_Days($year,$month,$day,1);
$h->[23]="$y-".sprintf("%02d",$m)."-".sprintf("%02d",$d);}my$t1_2=new Benchmark;
print STDERR"Server> ".timestr(timediff($t1_2,$t1))." INTEGRATE CHECK 1.2:".timestr(timediff($t1_2,$t1_1))."\n" if$DEBUG_TIMINGS;
my@customer=();
my$training_mode=-e$self->dir_server()."/training_mode.txt"?1:0;
my$doc_types=$self->config()->doc_types_by_id();
my$doc_type_str=$doc_types->{$doc_type};
if($h->[8]==2||$h->[8]==13||$h->[8]==14||$h->[8]==33){$mult=-1;}elsif($h->[8]==3){if($c->{VOIDS_REGISTER}==0){my$check_name="/opt/pos/server/data/areas/".$check->area()."/".$orig_table_number.".name";
if(!$extra_data->{keep_table_name}&&-e$check_name&&scalar@$details==0){unlink$check_name}unlink"$file.deleted";
unlink"$file.print.1";
print STDERR"We're not registering VOIDs, returning...\n";
return-1;}}my$fo_doc_number=$doc_type_str;
if($training_mode){$doc_type=99;
$external_doc="999";
$fo_doc_number.="999";}else{if($h->[20]){my$reserved=0;
if(length($h->[20])==4&&$h->[20]=~/^999/){$reserved=1;}if(!$reserved){$external_doc=$h->[20];
$doc_type=99;}}}my$t1_3=new Benchmark;
print STDERR"Server> ".timestr(timediff($t1_3,$t1))." INTEGRATE CHECK 1.3:".timestr(timediff($t1_3,$t1_2))."\n" if$DEBUG_TIMINGS;
my$station_closed=$h->[5];
$doc_serie=~s/\@STATION_NUMBER\@/$station_closed/g;
$doc_serie=~s/\-//g;
$doc_serie.=$external_doc if$external_doc;
my$t1_4=new Benchmark;
print STDERR"Server> ".timestr(timediff($t1_4,$t1))." INTEGRATE CHECK 1.4:".timestr(timediff($t1_4,$t1_3))."\n" if$DEBUG_TIMINGS;
my$doc_counter_key=$doc_type_str.'#'.$external_doc.'#'.$doc_serie;
my$valid=undef;
my$dnumber;
my$previous_hash;
my$new_fo_doc_number;
while(!defined$valid){($dnumber,$previous_hash)=$self->find_next_doc_id($doc_type,$doc_serie,$check->business_date(),$fo_doc_number,$pt_certified_software,);
$new_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;}$fo_doc_number=$new_fo_doc_number;
my$t1_5=new Benchmark;
print STDERR"Server> ".timestr(timediff($t1_5,$t1_4))." INTEGRATE CHECK 1.5:".timestr(timediff($t1_5,$t1_4))."\n" if$DEBUG_TIMINGS;
my$str="";
my@chars=();
my$t2=new Benchmark;
print STDERR"Server> ".timestr(timediff($t2,$t1))." INTEGRATE CHECK ATE SIGN_STRING_0:".timestr(timediff($t2,$t1_5))."\n" if$DEBUG_TIMINGS;
my$hash_check_value=$check_value;
if(exists$extra_data->{manualdoc_total_w_vat}){$extra_data->{manualdoc_total_w_vat}||=0;
$hash_check_value=sprintf("%.02f",$extra_data->{manualdoc_total_w_vat});}if($pt_certified_software){my$str_to_sign=$business_date.";".$cdd."T".$cdh.";".$fo_doc_number.";".sprintf("%.02f",$hash_check_value).";".$previous_hash;
$str=$self->sign_string($str_to_sign);
if(length($str)!=172){$self->send_alert_to_ui(&translate($self,"ASSINATURA DOCUMENTO INVALIDA.<BR>TENTE NOVAMENTE."));
print STDERR"INVALID HASH!!!!! $str, aborting...\n";
return undef;}my$doc_hashes=App::POS::Counters->new(db=>$db,name=>'doc_hashes',);
$doc_hashes->set_value($doc_type."___".$doc_serie,$str);
@chars=&_get_sign_chars(signature=>$str);}my$t3=new Benchmark;
print STDERR"Server> ".timestr(timediff($t3,$t1))." INTEGRATE CHECK DEPOIS SIGN_STRING_0".($cnt++).":".timestr(timediff($t3,$t2))."\n" if$DEBUG_TIMINGS;
my$clerk_open_name=exists$clerks->{$h->[2]}?$clerks->{$h->[2]}->{login}:'UNKNOWN';
my$clerk_close_name=exists$clerks->{$h->[3]}?$clerks->{$h->[3]}->{login}:'UNKNOWN';
if($business_date!~/^\d\d\d\d\-\d\d\-\d\d$/){print STDERR"Invalid business date: $business_date, returning...\n";
return-1;}my$cashier=$self->_decode_sale_cashier($c,$clerks,$h->[5],$h->[3]);
my$t33=new Benchmark;
print STDERR"Server> ".timestr(timediff($t33,$t1))." INTEGRATE CHECK DEPOIS SIGN_STRING_0".($cnt++).":".timestr(timediff($t3,$t2))."\n" if$DEBUG_TIMINGS;
my$new_id=$db->next_id("sales_headers");
my$inserted_id=$new_id;
my$t3_1=new Benchmark;
print STDERR"Server> ".timestr(timediff($t3_1,$t1))." INTEGRATE CHECK DEPOIS SIGN_STRING_1".($cnt++)."\n" if$DEBUG_TIMINGS;
if($custom_invoice_id ne ''){$h->[12]=$custom_invoice_id;}my$service_charge=$h->[17]||0;
$service_charge=0 if$service_charge<0;
my$check_name=$check->name();
if($check_name){$check_name=trim($check_name);
$extra_data->{check_name}=$check_name;}my$customer_id=$h->[11]||0;
my$customer_name=$h->[12];
$customer_name="" if$customer_name=~/^\s+$/;
my$customer_nif=$h->[13]||0;
my$overrided_customer_id=0;
my$overrided_customer_name="";
my$overrided_customer_nif="";
$extra_data->{override_customer_id}||=0;
delete$extra_data->{override_customer_id}unless$extra_data->{override_customer_id};
$extra_data->{kept_in_source}||=0;
delete$extra_data->{kept_in_source}unless$extra_data->{kept_in_source};
$info->{headers}={__unique_id=>$h->[-1],gb_identifier=>$h->[-1],to_print=>$check->to_print(),points_used=>$extra_data->{points_used}||0,id=>$inserted_id,table_number=>$table_number,date=>$h->[0],business_date=>$check->business_date(),date_closed=>$close_date,hour=>$check_hour,doc_type=>$doc_type,fo_doc_number=>$fo_doc_number,value=>my_round($check_value,$c->{DECIMAL_PLACES}),discount=>$h->[10],clerk_open=>$h->[2],clerk_close=>$h->[3],station_open=>$h->[4],station_close=>$h->[5],area=>$h->[7],meals=>$h->[14],hash=>$str,hash_small=>join("",@chars),clerk_open_name=>$clerk_open_name,clerk_close_name=>$clerk_close_name,customer_id=>$customer_id,customer=>$customer_name,customer_nif=>$customer_nif||"",customer_credit=>undef,persons_number=>int($h->[15]*1),tip=>$h->[16]||0,service_charge=>$service_charge,check_name=>$check_name,external_doc=>$external_doc||0,external_doc_name=>exists$c->{EXTERNAL_DOC_BY_CODE}->{$external_doc}?$c->{EXTERNAL_DOC_BY_CODE}->{$external_doc}:"",custom_invoice_id=>$custom_invoice_id,doc_counter_key=>$doc_counter_key,training_mode=>$training_mode,};
my$t3_2=new Benchmark;
print STDERR"Server> ".timestr(timediff($t3_2,$t1))." INTEGRATE CHECK DEPOIS SIGN_STRING_2".($cnt++)."\n" if$DEBUG_TIMINGS;
if($pt_certified_software){if($self->software_version>=202301){my$series_codes=$self->get_series_codes();
my$atcud_serie=exists$ATCUD_DOCSTR_BY_TYPE{$doc_type}?$ATCUD_DOCSTR_BY_TYPE{$doc_type}:"";
if($atcud_serie){$atcud_serie.=" $doc_serie";
if(exists$series_codes->{$atcud_serie}){my$serie_code=$series_codes->{$atcud_serie}||'0';
if(length($serie_code)>10){$serie_code='0';}$data->{extra_data}->{atcud}="$serie_code-$dnumber";}else{$data->{extra_data}->{atcud}="0";}}else{$data->{extra_data}->{atcud}="0";}}else{$data->{extra_data}->{atcud}="0";}}if($info->{headers}->{external_doc}){if($external_doc<9990&&$external_doc!=999){$data->{extra_data}->{external_doc_code}=$info->{headers}->{external_doc};
$data->{extra_data}->{external_doc_name}=$info->{headers}->{external_doc_name};}}my$voucher_discount=0;
my@new_footers=();
if(!$c->{VOUCHERS_AS_MEDIA_PAYMENT}){if(exists$extra_data->{used_voucher}&&$extra_data->{used_voucher}&&$info->{headers}->{value}>0){foreach my $footer(@$footers){next unless scalar@$footer;
if($footer->[0]eq 'VOUCHER'){my$v=$footer->[1]*1;
$voucher_discount+=$v;
$check_value-=$v;
$info->{headers}->{value}-=$v;}else{push@new_footers,$footer;}}}}if(scalar@new_footers){$footers=\@new_footers;}my$t3_3=new Benchmark;
print STDERR"Server> ".timestr(timediff($t3_3,$t1))." INTEGRATE CHECK DEBUG 1\n" if$DEBUG_TIMINGS;
my$update_customer=0;
if(exists$data->{extra_data}->{carwash_customer_plate}){$info->{headers}->{customer_aux}=$data->{extra_data}->{carwash_customer_plate};
$update_customer=1;}else{$info->{headers}->{customer_aux}="";}if(exists$data->{extra_data}->{carwash_customer_contact}){$info->{headers}->{customer_contact}=$data->{extra_data}->{carwash_customer_contact};
$update_customer=1;}else{$info->{headers}->{customer_contact}="";}$extra_data->{delivery_no_invoice}||="";
if($extra_data->{delivery_no_invoice}eq '1'){$info->{headers}->{customer_nif}="";
$info->{headers}->{customer_id}="";
$info->{headers}->{customer}="";}my$cid=exists$info->{headers}->{customer_id}&&$info->{headers}->{customer_id}?$info->{headers}->{customer_id}:0;
my$cexists=0;
if($cid){my$customer_check=$db->select("SELECT * FROM customers WHERE id = ?",$cid,);
if(scalar@$customer_check){if(exists$info->{headers}->{customer_id}&&$info->{headers}->{customer_id}&&$info->{headers}->{customer}){my$db_name=$customer_check->[0]->{name}||"";
if(!$db_name&&$db_name ne '--'){$db->do("UPDATE customers SET name = ? WHERE id = ?",$info->{headers}->{customer},$cid);}}$cexists=1;
$db_customer_info=$customer_check->[0];}}if(!$cexists&&($info->{headers}->{customer}||$info->{headers}->{customer_nif})){my$id=$db->next_id("customers");
if($id<1000000){$id+=1000000;}my$name=$info->{headers}->{customer};
$db->do("INSERT INTO customers (id, name, nif, deleted, contact, aux) VALUES (?, ?, ?, ?, ?, ?)",$id,$name,$info->{headers}->{customer_nif},0,$info->{headers}->{customer_contact},$info->{headers}->{customer_aux});
$info->{headers}->{customer_id}=$id;
my$check=$db->select("SELECT * FROM customers WHERE id = ?",$id,);
if(scalar@$check){$db_customer_info=$check->[0];}}if(scalar keys%$db_customer_info){foreach my $k(keys%$db_customer_info){$info->{headers}->{"customer_".$k}=$db_customer_info->{$k};}}my$dont_apply_vat_isentions="";
if(exists$extra_data->{dont_apply_vat_isentions}&&$extra_data->{dont_apply_vat_isentions}&&$info->{headers}->{customer_id}){$db->do("UPDATE customers SET dont_apply_vat_isentions = 1 WHERE id = ?",$info->{headers}->{customer_id});}my$customer_vat_isention="";
if(exists$extra_data->{customer_vat_isention}&&$extra_data->{customer_vat_isention}){if($extra_data->{customer_vat_isention}=~/^M\d+_/){$customer_vat_isention=$extra_data->{customer_vat_isention};}else{$customer_vat_isention='M07_Isento Artigo 9. do CIVA (ou similar)';
$db->do("UPDATE customers SET vat_isention = ? WHERE id = ?",fix_encoding($customer_vat_isention),$info->{headers}->{customer_id},);}}if($customer_vat_isention&&!$dont_apply_vat_isentions){for(@$details){$_->[6]=0;}for(@$details_deleted){$_->[6]=0;}}my$t3_5=new Benchmark;
print STDERR"Server> ".timestr(timediff($t3_5,$t1))." INTEGRATE CHECK DEBUG 3\n" if$DEBUG_TIMINGS;
$dbh->do(qq{
      COPY sales_headers (
        id, timestamp, date, date_closed, business_date, hour, doc_type, fo_doc_number, value,
        discount, clerk_open, clerk_close, station_open, station_close, area, clerk_open_name,
        clerk_close_name, customer, customer_nif, persons_number, tip, service_charge, cashier,
        table_num, gb_identifier, serie, customer_id, doc_serie
      ) FROM stdin
    });
my@params=($new_id,$close_date,$h->[0],$close_date,$check->business_date(),$check_hour,$doc_type,$fo_doc_number,&my_round($check_value*$mult,$c->{DECIMAL_PLACES}),$h->[9],$h->[2],$h->[3],$h->[4],$h->[5],$h->[7],&my_conv($clerk_open_name),&my_conv($clerk_close_name),$info->{headers}->{customer}||'',$info->{headers}->{customer_nif}||0,$info->{headers}->{persons_number},$h->[16]||0,$service_charge,$cashier,$table_number,$h->[-1],$clerk_serie,$info->{headers}->{customer_id}||0,$doc_serie,);
map{tr/\\\t\n/   /;}@params;
$dbh->pg_putline(join("\t",@params)."\n");
$dbh->pg_endcopy;
my$t3_6=new Benchmark;
print STDERR"Server> ".timestr(timediff($t3_6,$t1))." INTEGRATE CHECK DEBUG 4\n" if$DEBUG_TIMINGS;
if(exists$extra_data->{assoc_transport_doc}&&$extra_data->{assoc_transport_doc}){my%used_prods=map{$_->[1]=>1}@$details;
my@ids=keys%used_prods;
if(scalar@ids){my$where_str=" AND sd.product IN (".join(",",@ids).")";
my$gt_products=$db->select(qq{
          SELECT sd.product
          FROM sales_details sd
          LEFT JOIN sales_headers sh ON (sd.header = sh.id)
          WHERE sh.gb_identifier = ?$where_str
          GROUP BY sd.product
        },$extra_data->{assoc_transport_doc});
if(scalar@$gt_products){$db->do("INSERT INTO document_operations VALUES (?, ?, 0)",$extra_data->{assoc_transport_doc},$h->[-1],);}}}my$t3_7=new Benchmark;
print STDERR"Server> ".timestr(timediff($t3_7,$t1))." INTEGRATE CHECK DEBUG 5\n" if$DEBUG_TIMINGS;
my$t3_8=new Benchmark;
print STDERR"Server> ".timestr(timediff($t3_8,$t1))." INTEGRATE CHECK DEBUG 6\n" if$DEBUG_TIMINGS;
my$points_customer_id=undef;
my$customer_card_number=$db_customer_info->{card_number}||"";
my$customer_app_sign_key=$db_customer_info->{app_sign_key}||"";
if($extra_data->{override_customer_id}){$points_customer_id=$extra_data->{override_customer_id};
my$tmp=$db->select("SELECT card_number, app_sign_key FROM customers WHERE id = ?",$points_customer_id);
$customer_card_number=$tmp->[0]->{card_number}||"";
$customer_app_sign_key=$tmp->[0]->{app_sign_key}||"";}elsif($info->{headers}->{customer_id}){$points_customer_id=$info->{headers}->{customer_id};}my$points_valid=1;
if($c->{POINTS_SYNC_TOKEN}){$points_valid=0 if!$customer_card_number&&!$customer_app_sign_key;}if($points_valid&&$c->{POINTS_USAGE}&&defined$points_customer_id&&$c->{POINTS_VALUE_PER_POINT}&&!exists$extra_data->{customer_dont_use_points}){$HARDLOCK_ID=hardlock_id()unless$HARDLOCK_ID;
$info->{headers}->{points_for_app_only}=$c->{POINTS_FOR_APP_ONLY};
my$hcid=$info->{headers}->{customer_id}||0;
if($points_customer_id!=$hcid){my$cust=$db->select("SELECT id, name, nif, deleted FROM customers WHERE id = ? ORDER BY deleted",$points_customer_id);
if(scalar@$cust&&$cust->[0]->{deleted}==0){$info->{headers}->{points_customer_name}=$cust->[0]->{name};
$info->{headers}->{points_customer_id}=$cust->[0]->{id};
$info->{headers}->{points_customer_nif}=$cust->[0]->{nif};}}else{$info->{headers}->{points_customer_name}=$info->{headers}->{customer};
$info->{headers}->{points_customer_id}=$info->{headers}->{customer_id};
$info->{headers}->{points_customer_nif}=$info->{headers}->{customer_nif};}if($info->{headers}->{doc_type}==1||$info->{headers}->{doc_type}==5||$info->{headers}->{doc_type}==90||$info->{headers}->{doc_type}==7){if(!exists$extra_data->{points_used}){my$points=0;
my$product_no_points=$self->_get_product_no_points($info->{headers},$db);
if(scalar keys%$product_no_points){my$total=0;
foreach my $d(@$details){next unless scalar@$d;
next unless defined$d->[3];
next if exists$product_no_points->{$d->[1]};
$total+=$d->[3]*$d->[4];}$points=int($total/$c->{POINTS_VALUE_PER_POINT});}else{if(exists$extra_data->{points_check_total}){$points=int($extra_data->{points_check_total}/$c->{POINTS_VALUE_PER_POINT});}else{$points=int($info->{headers}->{value}/$c->{POINTS_VALUE_PER_POINT});}}$points||=0;
if($points){$info->{headers}->{points_assigned}=$points;
$extra_data->{points_assigned}=$points;
$extra_data->{points_current}||=0;
$info->{headers}->{points_current}=$extra_data->{points_current}+$extra_data->{points_assigned};
$info->{headers}->{points_missing}=$c->{POINTS_MINIMUM_TO_USE}-$info->{headers}->{points_current};
if($c->{POINTS_FOR_APP_ONLY}){$info->{headers}->{points_value}=floor($info->{headers}->{points_current}/($c->{POINTS_MINIMUM_TO_USE}||1))*($c->{POINTS_MINIMUM_TO_USE}||1)*$c->{POINTS_VALUE_PER_POINT_ON_SALE};
$info->{headers}->{points_current}=$info->{headers}->{points_current}-($info->{headers}->{points_value}/$c->{POINTS_VALUE_PER_POINT_ON_SALE});}else{$info->{headers}->{points_value}=$info->{headers}->{points_current}*$c->{POINTS_VALUE_PER_POINT_ON_SALE};}if($c->{POINTS_SYNC_TOKEN}){my$dir=$self->dir_server().'/server/data/cloud_points';
mkdir$dir unless-d$dir;
my$r=int(rand(10000));
my$f=$dir."/points_assigned#".time."#$r.txt";
my$c_card=$info->{headers}->{customer_card_number}||"";
my$c_nif=$info->{headers}->{customer_nif}||"";
my$c_name=$info->{headers}->{customer}||"";
my$c_email=exists$db_customer_info->{email}?$db_customer_info->{email}:"";
my$c_contact=exists$db_customer_info->{contact}?$db_customer_info->{contact}:"";
my$c_birthdate=exists$db_customer_info->{birthdate}?$db_customer_info->{birthdate}:"";
my$c_address=exists$db_customer_info->{address}?$db_customer_info->{address}:"";
my$c_city=exists$db_customer_info->{city}?$db_customer_info->{city}:"";
my$c_postal_code=exists$db_customer_info->{postal_code}?$db_customer_info->{postal_code}:"";
my$c_app_sign_key=exists$db_customer_info->{app_sign_key}?$db_customer_info->{app_sign_key}:"";
$c_name||="";$c_name=~s/#//g;
$c_email||="";$c_email=~s/#//g;
$c_contact||="";$c_contact=~s/#//g;
$c_birthdate||="";$c_birthdate=~s/#//g;
$c_address||="";$c_address=~s/#//g;
$c_city||="";$c_city=~s/#//g;
$c_postal_code||="";$c_postal_code=~s/#//g;
$c_app_sign_key||="";$c_app_sign_key=~s/#//g;
write_file($f,"$HARDLOCK_ID#$c_nif#$c_card#$c_name#$fo_doc_number#".$check->business_date()."#".my_round($check_value,$c->{DECIMAL_PLACES})."#".$c_birthdate."#$c_address#$c_city#$c_postal_code#$c_contact#$c_email#".$db_customer_info->{id}."#".$info->{headers}->{points_assigned}."#".$c_app_sign_key);}$db->do("UPDATE customers SET points = points + $points WHERE id = ?",$points_customer_id,);}}else{$extra_data->{points_current}||=0;
$info->{headers}->{points_current}=$extra_data->{points_current}-$extra_data->{points_used};
$info->{headers}->{points_current}=0 if$info->{headers}->{points_current}<0;
$info->{headers}->{points_value}=$info->{headers}->{points_current}*$c->{POINTS_VALUE_PER_POINT_ON_SALE};
if($c->{POINTS_FOR_APP_ONLY}){$info->{headers}->{points_value}=floor($info->{headers}->{points_current}/($c->{POINTS_MINIMUM_TO_USE}||1))*($c->{POINTS_MINIMUM_TO_USE}||1)*$c->{POINTS_VALUE_PER_POINT_ON_SALE};
$info->{headers}->{points_current}=$info->{headers}->{points_current}-($info->{headers}->{points_value}/$c->{POINTS_VALUE_PER_POINT_ON_SALE});}else{$info->{headers}->{points_value}=$info->{headers}->{points_current}*$c->{POINTS_VALUE_PER_POINT_ON_SALE};}if($c->{POINTS_SYNC_TOKEN}){my$dir=$self->dir_server().'/server/data/cloud_points';
mkdir$dir unless-d$dir;
my$r=int(rand(10000));
my$f=$dir."/points_used#".time."#$r.txt";
my$c_card=$info->{headers}->{customer_card_number}||"";
my$c_nif=$info->{headers}->{customer_nif}||"";
my$c_name=$info->{headers}->{customer}||"";
my$c_email=exists$db_customer_info->{email}?$db_customer_info->{email}:"";
my$c_contact=exists$db_customer_info->{contact}?$db_customer_info->{contact}:"";
my$c_birthdate=exists$db_customer_info->{birthdate}?$db_customer_info->{birthdate}:"";
my$c_address=exists$db_customer_info->{address}?$db_customer_info->{address}:"";
my$c_city=exists$db_customer_info->{city}?$db_customer_info->{city}:"";
my$c_postal_code=exists$db_customer_info->{postal_code}?$db_customer_info->{postal_code}:"";
my$c_app_sign_key=exists$db_customer_info->{app_sign_key}?$db_customer_info->{app_sign_key}:"";
$c_name||="";$c_name=~s/#//g;
$c_email||="";$c_email=~s/#//g;
$c_contact||="";$c_contact=~s/#//g;
$c_birthdate||="";$c_birthdate=~s/#//g;
$c_address||="";$c_address=~s/#//g;
$c_city||="";$c_city=~s/#//g;
$c_postal_code||="";$c_postal_code=~s/#//g;
$c_app_sign_key||="";$c_app_sign_key=~s/#//g;
write_file($f,"$HARDLOCK_ID#$c_nif#$c_card#$c_name#$fo_doc_number#".$check->business_date()."#".my_round($check_value,$c->{DECIMAL_PLACES})."#".$c_birthdate."#$c_address#$c_city#$c_postal_code#$c_contact#$c_email#".$db_customer_info->{id}."#".$info->{headers}->{points_used}."#".$c_app_sign_key);}}}}if(exists$extra_data->{points_cloud_error}&&$extra_data->{points_cloud_error}eq '1'){$info->{headers}->{points_cloud_error}=1;}else{$info->{headers}->{points_cloud_error}=0;}if($info->{headers}->{discount}){$extra_data->{original_header_discount}=$info->{headers}->{discount};}my$t3_9=new Benchmark;
print STDERR"Server> ".timestr(timediff($t3_9,$t1))." INTEGRATE CHECK DEBUG 7\n" if$DEBUG_TIMINGS;
$c->{CUSTOMER_VAT_ISENTION}||="";
$c->{MENUS_VALUE_BY_COMPONENT}||=0;
my$menu_original_prices={};
my$menu_original_discounts={};
my$product_prices={};
my$had_menus=0;
if($c->{MENUS_VALUE_BY_COMPONENT}){my%menus_info=();
my%menus_products=();
my%new_prices=();
my%new_discounts=();
my$last_product={};
my$last_product_id="";
my$last_product_gbi="";
my$components_will_start=0;
my$is_splited=exists$extra_data->{splited_check}&&$extra_data->{splited_check}?1: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];}$menu_original_prices->{$d->[-1]}=$d->[4];
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$show_alert=0;
$product_prices=$self->_get_product_prices($info->{headers},$db);
foreach my $menu(keys%menus_products){my$h=$menus_products{$menu};
my$menu_price=0;
my$discount_percent=0;
if($h->[11]){if($h->[11]=~/\%$/){($discount_percent)=$h->[11]=~/^(.*?)\%$/;}else{$discount_percent=($h->[11]/$h->[4])*100;}}$menu_price=$h->[4]*$h->[3];
$new_prices{$h->[-1]}=0;
my$total_price=0;
foreach my $component(@{$menus_info{$menu}}){$total_price+=$product_prices->{$component->[1]}*$component->[3];}if($total_price>0){foreach my $component(@{$menus_info{$menu}}){my$vat=$component->[6];
my$ratio=$product_prices->{$component->[1]}/$total_price;
my$price=$menu_price*$ratio;
my$price_wo_vat=$price/(($vat/100)+1);
my$discount=$price_wo_vat*$discount_percent/100;
$new_prices{$component->[-1]}=$price;
$new_discounts{$component->[-1]}=$discount;}}else{$show_alert=1;
delete$new_prices{$h->[-1]};}}if($show_alert){$self->send_alert_to_ui(&translate($self,"O VALOR DE UM MENU NÃO FOI DISTRIBUIDO PELOS SEUS COMPONENTES.<BR>OS COMPONENTES NÃO PODEM TER PREÇO 0"));}}for(@$details){next unless scalar@$_;
next unless defined$_->[3];
next unless exists$new_prices{$_->[-1]};
my$new_price=$new_prices{$_->[-1]};
my$new_discount=$new_discounts{$_->[-1]};
if($_->[13]eq 'MENU'){if($_->[4]*1>0){$_->[4]=$_->[4]+$new_price;
$_->[5]=$_->[5]+$new_discount;}else{$_->[4]=$new_price;
$_->[5]=$new_discount;}}else{$_->[4]=$new_price;
$_->[5]=$new_discount;}}}$extra_data->{obs}||="";
if(-e$self->server_dir_tmp()."/obs_add.txt"){my$obs_add=read_file($self->server_dir_tmp()."/obs_add.txt");
chop($obs_add)if$obs_add;
if($extra_data->{obs}){$extra_data->{obs}.="\n".$obs_add;}else{$extra_data->{obs}=$obs_add;}unlink$self->server_dir_tmp()."/obs_add.txt";}my$t4=new Benchmark;
print STDERR"Server> ".timestr(timediff($t4,$t1))." INTEGRATE CHECK DEPOIS SALES_HEADERS ".($cnt++).":".timestr(timediff($t4,$t3_3))."\n" if$DEBUG_TIMINGS;
$info->{details}=[];
$info->{payments}=[];
my$dbh=$db->dbh();
my%stock_ids=();
my$stock_products=$self->_stock_products();
my$idx=-1;
my$global_discount_percent=0;
my$global_discount=0;
if($h->[10]){if($h->[10]=~/\%$/){$global_discount_percent=1;
($global_discount)=$h->[10]=~/^(.*?)\%$/;}else{$global_discount=$h->[10];}}my$total_discounts_in_lines=0;
if(!$global_discount_percent&&$global_discount>0){foreach my $d(@$details){next unless scalar@$d;
next unless defined$d->[3];
my$discount=$d->[5]||0;
my$vat=$d->[6]||0;
my$qtty=$d->[3]*$mult;
if($vat>0){$total_discounts_in_lines+=($discount*$qtty)*(1+$vat/100);}}}$dbh->do(qq{
      COPY sales_details (
        header, product, hour, quantity, price, discount, offer, vat, clerk, station,
        operation_type, product_name, clerk_name, free_text, price_wo_vat, inside_menu,
        gb_identifier, department, price_used, business_date, had_bill, idx
      ) FROM stdin
    });
if($c->{LOG_POS_DEBUG}){open(NN,">> /opt/pos/server/data/checks_integration.log");
print NN"[".&strftime("%Y%m%d_%H%M%S",localtime)."][DETAILS] ".$check->business_date().",$fo_doc_number,$orig_table_number,$doc_type,".$check_value.",".scalar(@$details).",".$h->[-1]."\n";
close(NN);}my%taxfree_vat_totals=();
my%taxfree_discounts_wo_vat=();
if(exists$extra_data->{taxfree_name}&&$extra_data->{taxfree_name}){my$taxfree_value_net=0;
foreach my $d(@$details){next unless scalar@$d;
next unless defined$d->[3];
my$qtty=$d->[3]*$mult;
my$discount=$d->[5]||0;
my$PIVA=$d->[6];
my$PCI=$d->[4];
my$PSI=$PCI/(1+$PIVA/100);
my$VDU=$discount;
my$PU=$PSI-$VDU;
my$PL=$PU*$qtty;
my$VIVA=$PL*($PIVA/100);
if($global_discount_percent){$discount+=($PSI-$discount)*($global_discount/100);}elsif($global_discount>0){my$check_total2=$info->{headers}->{value}+$global_discount+$voucher_discount;
my$disc_w_vat=$discount*(1+$PIVA/100);
my$racio=$global_discount/$check_total2;
my$disc_com_iva=sprintf("%.8f",($PCI-$disc_w_vat)*$racio);
my$disc_sem_iva=sprintf("%.8f",$disc_com_iva/(1+$PIVA/100));
$discount=$discount+$disc_sem_iva;}if($voucher_discount>0){my$discount_w_vat=0;
if($discount>0){$discount_w_vat=$discount*(1+$PIVA/100);}my$tmp_check_total=$info->{headers}->{value}+$voucher_discount;
my$v_discount_w_vat=(($PCI-$discount_w_vat)/$tmp_check_total)*$voucher_discount;
my$v_discount_wo_vat=$v_discount_w_vat/(1+$PIVA/100);
$discount+=$v_discount_wo_vat;}$taxfree_discounts_wo_vat{$d->[-1]}=$discount;
$d->[4]||=0;
$d->[16]||=1;
my$sem_iva=$qtty*$PSI;
my$desconto_sem_iva=$qtty*$discount;
my$net_v=($sem_iva-$desconto_sem_iva);
if($PIVA*1>0){$taxfree_vat_totals{$PIVA}+=$net_v*($PIVA/100);}$taxfree_value_net+=$net_v;}if(sprintf("%.2f",$taxfree_value_net)<50||!$pt_certified_software){$taxfree_value_net=0;
%taxfree_vat_totals=();
my%new_extra_data=();
foreach my $k(keys%$extra_data){$new_extra_data{$k}=$extra_data->{$k}unless$k=~/^taxfree_/;}$extra_data=\%new_extra_data;
$check->extra_data_update($extra_data);}else{if(scalar%taxfree_vat_totals&&sprintf("%.2f",$taxfree_value_net)>=50){$extra_data->{taxfree_vats}="";
my$taxfree_total=0;
foreach my $vat(keys%taxfree_vat_totals){$taxfree_total+=$taxfree_vat_totals{$vat};
$extra_data->{taxfree_vats}.="$vat=$taxfree_vat_totals{$vat},";}$extra_data->{taxfree_vats}=~s/,$//;
$extra_data->{taxfree_total}=$taxfree_total;}}}my@add_details_aux=();
my$value_net=0;
my$had_vat=0;
my@suggestions_collected=();
my%parent_stock_deduct=();
$extra_data->{dont_restore_stock}||=0;
foreach my $d(@$details){next unless scalar@$d;
next unless defined$d->[3];
$d->[14]||=0;
$idx++;
my$clerk_name=exists$clerks->{$d->[7]}?$clerks->{$d->[7]}->{login}:'UNKNOWN';
my$free_text=$d->[13];
$free_text=~s/\\/\\\\/g;
my$op_type=1;
my$product_name='UNKNOWN';
my$department='UNKNOWN';
if(exists$products->{$d->[1]}){$product_name=$products->{$d->[1]}->{name};
if(defined$mc_product_id&&$mc_product_id==$d->[1]){if($h->[21]){$product_name.=" ".$h->[21]}}$product_name=$product_hardcoded_names{$d->[1]}if exists$product_hardcoded_names{$d->[1]};
my$fam_name;
my$subfam_name;
if($d->[9]=~/^(.*?)___SF___(.*?)$/){$fam_name=$1;
$subfam_name=$2;}else{if(exists$child_products->{$d->[1]}&&(!$fam_name||!$subfam_name||$products->{$d->[1]}->{family_name}eq '----------'||$products->{$d->[1]}->{subfamily_name}eq '----------')){$fam_name=$child_products->{$d->[1]}->{fam_name}||"";
$subfam_name=$child_products->{$d->[1]}->{subfam_name}||"";}else{$fam_name=$products->{$d->[1]}->{family_name}||"";
$subfam_name=$products->{$d->[1]}->{subfamily_name}||"";}}if($fam_name&&$subfam_name){$department=&my_conv($fam_name)."§".&my_conv($subfam_name);}elsif($fam_name&&!$subfam_name){$department=&my_conv($fam_name)."§UNKNOWN";}elsif(!$fam_name&&$subfam_name){$department="UNKNOWN§".&my_conv($subfam_name);}}if($c->{PRODUCTS_SUGESTION}){push@suggestions_collected,$d;}my$price_wo_vat_taxfree=$d->[4]/(1+$d->[6]/100);
my$discount_wo_vat_taxfree=exists$taxfree_discounts_wo_vat{$d->[-1]}?$taxfree_discounts_wo_vat{$d->[-1]}:0;
if(scalar keys%taxfree_vat_totals){$d->[6]=0;}my$discount_orig=0;
my$qtty=$d->[3]*$mult;
my$discount=$d->[5]||0;
my$line_discount=0;
my$disc=0;
my$offer=0;
my$v1=0;
my$gdiscount=0;
my$PIVA=$d->[6];
my$PCI=$d->[4];
my$PSI=$PCI/(1+$PIVA/100);
my$VDU=$discount;
my$PU=$PSI-$VDU;
my$PL=$PU*$qtty;
my$VIVA=$PL*($PIVA/100);
if($global_discount_percent){$discount+=($PSI-$discount)*($global_discount/100);}elsif($global_discount>0){my$check_total2=$info->{headers}->{value}+$global_discount+$voucher_discount;
my$disc_w_vat=$discount*(1+$PIVA/100);
my$racio=$global_discount/$check_total2;
my$disc_com_iva=sprintf("%.8f",($PCI-$disc_w_vat)*$racio);
my$disc_sem_iva=sprintf("%.8f",$disc_com_iva/(1+$PIVA/100));
$discount=$discount+$disc_sem_iva;}if($voucher_discount>0){my$discount_w_vat=0;
if($discount>0){$discount_w_vat=$discount*(1+$PIVA/100);}my$tmp_check_total=$info->{headers}->{value}+$voucher_discount;
my$v_discount_w_vat=(($PCI-$discount_w_vat)/$tmp_check_total)*$voucher_discount;
my$v_discount_wo_vat=$v_discount_w_vat/(1+$PIVA/100);
$discount+=$v_discount_wo_vat;}if(defined$d->[11]&&$d->[11]){my@params=($inserted_id,$d->[1],$d->[-1],"original_line_discount",$d->[11],);
push@details_extra_data,\@params;}$d->[4]||=0;
$d->[16]||=1;
$d->[2]=~s/\-/:/g;
if(exists$stock_products->{$d->[1]}&&!$extra_data->{dont_restore_stock}){if($op_type==1){if($info->{headers}->{doc_type}==1||$info->{headers}->{doc_type}==5||$info->{headers}->{doc_type}==90||$info->{headers}->{doc_type}==2||$info->{headers}->{doc_type}==4||$info->{headers}->{doc_type}==10||$info->{headers}->{doc_type}==11||$info->{headers}->{doc_type}==13||$info->{headers}->{doc_type}==12||$info->{headers}->{doc_type}==15){$stock_ids{$d->[1]}+=$qtty;}elsif($info->{headers}->{doc_type}==7&&!exists$extra_data->{invoiced}){$stock_ids{$d->[1]}+=$qtty;}}}my$sem_iva=$qtty*$PSI;
my$desconto_sem_iva=$qtty*$discount;
my$net_v=($sem_iva-$desconto_sem_iva);
$value_net+=$net_v;
my$gbi=$d->[-1];
my$line_extra_data="";
if(exists$products_extra_data{$gbi}){$line_extra_data=$products_extra_data{$gbi};
push@products_extra_data_insert,{product=>$d->[1],detail_gbi=>$gbi,extra_data=>$products_extra_data{$gbi},};}my$memc=memcache($self->server_ip(),'pos_files',\%MEMC_CONNECTIONS);
my$had_bill=$memc->get('gbi_had_bill#'.$gbi);
$had_bill||=0;
if($discount&&$discount=~/\./){my@a=split/\./,$discount;
if(scalar@a>1){$a[1]=substr($a[1],0,12);
$discount=$a[0].".".$a[1];}}my@params=($inserted_id,$d->[1],$d->[2],$qtty,$d->[4],$discount,$offer,$d->[6],$d->[7],$d->[12]||0,$op_type,&my_conv($product_name),&my_conv($clerk_name),&my_conv($free_text),$PSI,$d->[14],$gbi,$department,$d->[16]||1,$check->business_date(),$had_bill,$idx,);
$had_vat=1 if$d->[6]*1>0;
map{tr/\\\t\n/   /;}@params;
if($c->{MENUS_VALUE_BY_COMPONENT}&&$had_menus&&scalar keys%$product_prices){if(exists$menu_original_prices->{$d->[-1]}){my@params=($inserted_id,$d->[1],$d->[-1],"original_price",$menu_original_prices->{$d->[-1]},);
push@details_extra_data,\@params;}}push@{$info->{details}},{product=>$d->[1],hour=>$d->[2],quantity=>$d->[3],price=>$d->[4],price_wo_vat=>$PSI,discount=>$discount,global_discount=>$gdiscount,offer=>$offer,vat=>$d->[6],clerk=>$d->[7],station=>$d->[12]||0,product_name=>$product_name,clerk_name=>$clerk_name,free_text=>$free_text,product_type=>$d->[13],idx=>$idx,gb_identifier=>$gbi,menu_original_price=>exists$menu_original_prices->{$gbi}?$menu_original_prices->{$gbi}:0,extra_data=>$line_extra_data,};
if(scalar keys%taxfree_vat_totals){$info->{details}->[-1]->{price_wo_vat_taxfree}=$price_wo_vat_taxfree;
$info->{details}->[-1]->{discount_wo_vat_taxfree}=$discount_wo_vat_taxfree;}$dbh->pg_putline(join("\t",@params)."\n");}$dbh->pg_endcopy;
foreach my $top_id(keys%parent_stock_deduct){my$qtty=$parent_stock_deduct{$top_id}||0;
$db->do("UPDATE products SET stock_current = stock_current - $qtty WHERE id = ? AND stock_use = 1",$top_id);}my$atcud=exists$data->{extra_data}->{atcud}?$data->{extra_data}->{atcud}:"";
my$id=&create_UUID_as_string();
$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;
$info->{headers}->{value_net}=$value_net;
my@tmp_arr=split/\s/,$info->{headers}->{date_closed};
$info->{headers}->{value_net}=0 if$info->{headers}->{value}*1==0;
@params=($id,$info->{headers}->{id},$info->{headers}->{gb_identifier},$info->{headers}->{business_date},$tmp_arr[0]."T".$tmp_arr[1],$info->{headers}->{fo_doc_number},$info->{headers}->{value},$info->{headers}->{hash},$info->{headers}->{hash_small},$info->{headers}->{doc_type},$had_vat?$info->{headers}->{value_net}:$info->{headers}->{value},$info->{headers}->{clerk_close},$info->{headers}->{customer_id},$clerk_serie,$table_number,$atcud);
map{tr/\\\t\n/   /;}@params;
$dbh->pg_putline(join("\t",@params)."\n");
$dbh->pg_endcopy();
if($pt_certified_software){if($info->{headers}->{doc_type}==2){$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}}){my@params=($id,$d->{gb_identifier},$info->{headers}->{business_date},$d->{product},$d->{price},$d->{price_wo_vat},$d->{quantity},$d->{discount},$d->{vat},);
map{tr/\\\t\n/   /;}@params;
$dbh->pg_putline(join("\t",@params)."\n");}$dbh->pg_endcopy();}}my$t5=new Benchmark;
print STDERR"Server> ".timestr(timediff($t5,$t1))." INTEGRATE CHECK DEPOIS SALES DETAILS2 ".($cnt++).":".timestr(timediff($t5,$t4))."\n" if$DEBUG_TIMINGS;
if(scalar@suggestions_collected){for(@suggestions_collected){$self->_handle_product_suggestions($products_suggest,$_,$info->{headers}->{area},$details);}}if(scalar@products_extra_data_insert){$dbh->do("COPY sales_details_extra_data (product, detail_gbi, extra_data) FROM stdin");
my%ctrl=();
for(@products_extra_data_insert){next if exists$ctrl{$_->{detail_gbi}};
my@params=($_->{product},$_->{detail_gbi},$_->{extra_data},);
$ctrl{$_->{detail_gbi}}=1;
map{tr/\\\t\n/   /;}@params;
$dbh->pg_putline(join("\t",@params)."\n");}$dbh->pg_endcopy;}$extra_data->{payed_tables}||="";
if($extra_data->{payed_tables}){$extra_data->{original_area}||="";
my$ch_area=$extra_data->{original_area}?$extra_data->{original_area}:$check->area();
my$pc_dir=$self->dir()."/server/data/payed_cards/".$ch_area;
system("mkdir -p $pc_dir")unless-d$pc_dir;
$extra_data->{payed_tables}=~s/,$//;
my@t=split/,/,$extra_data->{payed_tables};
for(@t){my@tmp=split/_/,$_;
if($mult<0){unlink("$pc_dir/".$tmp[0]);}else{open(F,"> $pc_dir/".$tmp[0]);
print F$tmp[1]."\n";
close(F);}}}else{$extra_data->{disco_card}||=0;
if($extra_data->{disco_card}){my$ch_area=$extra_data->{original_area}?$extra_data->{original_area}:$check->area();
my$pc_dir=$self->dir()."/server/data/payed_cards/".$ch_area;
system("mkdir -p $pc_dir")unless-d$pc_dir;
if($mult<0){unlink("$pc_dir/".$orig_table_number);}else{open(F,"> $pc_dir/".$orig_table_number);
print F$check_value."\n";
close(F);}}else{delete$extra_data->{disco_card};}}if(scalar@add_details_aux){my$str=join(",",keys%stock_ids);
my$data=$db->select("SELECT * FROM products_extra_data WHERE product_id IN ($str)");
my%info=();
if(scalar@$data){for(@$data){$info{$_->{product_id}}=[$_->{field_name},$_->{field_value}];}foreach my $rec(@add_details_aux){if(exists$info{$rec->{product_id}}){my@params=($rec->{header},$rec->{product_id},$rec->{gb_identifier},&my_conv("extra_data#".$info{$rec->{product_id}}->[0]),&my_conv($info{$rec->{product_id}}->[1]));
map{tr/\\\t\n/   /;}@params;
push@details_extra_data,\@params;}}}}if($c->{VOIDS_REGISTER}){my$void_idx=-1;
$dbh->do(qq{
        COPY sales_details (
          header, product, hour, quantity, price, discount, offer, vat, clerk, station,
          operation_type, product_name, clerk_name, free_text, price_wo_vat, inside_menu,
          gb_identifier, department, price_used, business_date, had_bill, idx
        ) FROM stdin
      });
foreach my $d(@$details_deleted){next unless scalar@$d;
next unless defined$d->[3];
$d->[14]||=0;
my$qtty=$d->[3]*$mult;
my$discount=$d->[5]||0;
my$offer=0;
my$op_type=$d->[10]ne ''?$d->[10]:4;
$op_type=4 if$op_type eq '1';
my$clerk_name=exists$clerks->{$d->[7]}?$clerks->{$d->[7]}->{login}:'UNKNOWN';
my$free_text=$d->[13];
$free_text=~s/\\/\\\\/g;
my$product_name='UNKNOWN';
my$department='UNKNOWN';
if(exists$products->{$d->[1]}){$product_name=$products->{$d->[1]}->{name};
$product_name=$product_hardcoded_names{$d->[1]}if exists$product_hardcoded_names{$d->[1]};
my$fam_name=$products->{$d->[1]}->{family_name}||"";
my$subfam_name=$products->{$d->[1]}->{subfamily_name}||"";
if($fam_name&&$subfam_name){$department=$fam_name." / ".$subfam_name;}elsif($fam_name&&!$subfam_name){$department=$fam_name." / UNKNOWN";}elsif(!$fam_name&&$subfam_name){$department="UNKNOWN / ".$subfam_name;}}my$PIVA=$d->[6];
my$PCI=$d->[4];
my$PSI=$PCI/(1+$PIVA/100);
my$VDU=$discount;
my$PU=$PSI-$VDU;
my$PL=$PU*$qtty;
my$VIVA=$PL*($PIVA/100);
$d->[4]||=0;
$d->[16]||=1;
$d->[2]=~s/\-/:/g;
my($register_hour,$void_hour)=split/\@/,$d->[2];
$free_text.="§".&my_conv($register_hour);
$void_idx++;
my$gbi=$d->[-1];
my$memc=memcache($self->server_ip(),'pos_files',\%MEMC_CONNECTIONS);
my$had_bill=$memc->get('gbi_had_bill#'.$gbi);
$had_bill||=0;
@params=($inserted_id,$d->[1],$void_hour,$d->[3]*$mult,$d->[4],$discount,$offer,$d->[6],$d->[7],$d->[12]||0,$op_type,&my_conv($product_name),&my_conv($clerk_name),$free_text,$PSI,$d->[14],$gbi,&my_conv($department),$d->[16],$check->business_date(),$had_bill,$void_idx,);
map{tr/\\\t\n/   /;}@params;
$dbh->pg_putline(join("\t",@params)."\n");}$dbh->pg_endcopy;}my$t6=new Benchmark;
print STDERR"Server> ".timestr(timediff($t6,$t1))." INTEGRATE CHECK DEPOIS SALES DETAILS1 ".($cnt++).":".timestr(timediff($t6,$t5))."\n" if$DEBUG_TIMINGS;
my$change_value=0;
foreach my $footer(@$footers){if(defined$media_ignore&&defined$footer->[0]&&$footer->[0]eq$media_ignore){$change_value=$footer->[1]*1;
last;}}my$has_cash=0;
my%tt=();
foreach my $footer(@$footers){next unless scalar@$footer;
my$value=$footer->[1];
my$obs=scalar@$footer==3?$footer->[2]:'0';
if($footer->[0]ne$media_ignore){if(!$c->{VOUCHERS_AS_MEDIA_PAYMENT}){next if$footer->[0]eq 'VOUCHER';}my$v=0;
if($footer->[0]eq&translate($self,"DINHEIRO")){$has_cash=1;
$v=my_round(($value-$change_value)*$mult,$c->{DECIMAL_PLACES});}else{$v=my_round($value*$mult,$c->{DECIMAL_PLACES});}my$id=$db->next_id("sales_payments");
$dbh->do("COPY sales_payments (id, header, payment_media, value, obs, business_date) FROM stdin");
$media_total_not_voucher+=$v;
my@params=($id,$inserted_id,&my_conv($footer->[0]),$v,&my_conv($obs),$check->business_date(),);
map{tr/\\\t\n/   /;}@params;
$dbh->pg_putline(join("\t",@params)."\n");
$dbh->pg_endcopy;}push@{$info->{payments}},{payment_media=>$footer->[0],value=>$value,obs=>$obs,};}my$t7=new Benchmark;
print STDERR"Server> ".timestr(timediff($t7,$t1))." INTEGRATE CHECK DEPOIS SALES PAYMENTS ".($cnt++).":".timestr(timediff($t7,$t6))."\n" if$DEBUG_TIMINGS;
if($c->{LOG_POS_DEBUG}){$info->{payments}||=[];
open(NN,">> /opt/pos/server/data/checks_integration.log");
print NN"[".&strftime("%Y%m%d_%H%M%S",localtime)."][PAYMENTS] ".$check->business_date().",$fo_doc_number,$orig_table_number,$doc_type,".$check_value.",".scalar(@{$info->{payments}}).",".$h->[-1]."\n";
close(NN);}my$header_customer_id=$info->{headers}->{customer_id}||0;
if($extra_data->{override_customer_id}){$header_customer_id=$extra_data->{override_customer_id};}$c->{VOUCHERS_FOR_APP_ONLY}||=0;
my$cfg_voucher_percent=-1;
my$cfg_voucher_above=-1;
my$process_vouchers=1;
if($c->{VOUCHERS_FOR_APP_ONLY}&&!$header_customer_id){$process_vouchers=0;}if(scalar keys%auto_voucher_info){foreach my $k(sort{$a<=>$b}keys%auto_voucher_info){if($media_total_not_voucher>=$k){$cfg_voucher_above=$k;
$cfg_voucher_percent=$auto_voucher_info{$k};}}}if($cfg_voucher_percent ne '-1'&&$cfg_voucher_above!=-1&&$process_vouchers){$extra_data->{used_voucher}||=0;
if(!$extra_data->{used_voucher}||($extra_data->{used_voucher}&&$c->{AUTO_VOUCHER_ASSIGN_WHEN_USE})){if($media_total_not_voucher>$cfg_voucher_above){my$r=int(rand(10000));
my$voucher_value=0;
if($cfg_voucher_percent=~/%/){my$tmp=$cfg_voucher_percent;
$tmp=~s/%//g;
$voucher_value=my_round($media_total_not_voucher*($tmp/100),$c->{DECIMAL_PLACES});}else{$voucher_value=$cfg_voucher_percent;}my$f=$self->server_dir_tmp()."/print_voucher#0#0#".$info->{headers}->{station_close}."#".$info->{headers}->{clerk_close}."#".$header_customer_id.".txt";
my$f_tmp="/opt/pos/common/tmp/voucher#".time."#$r.txt";
write_file($f_tmp,"$voucher_value\n");
move($f_tmp,$f);}}}my%did_extra_data=();
$db->do("DELETE FROM sales_headers_aux WHERE header = ?",$inserted_id);
if(scalar keys%$extra_data){foreach my $k(keys%$extra_data){next if exists$EXTRA_DATA_IGNORE{$k};
next unless defined$extra_data->{$k}&&$extra_data->{$k}ne '';
next if exists$did_extra_data{$k};
next if$k eq 'original_area'&&$extra_data->{$k}eq$h->[7];
next if$k eq 'original_table_number'&&$extra_data->{$k}eq$orig_table_number;
my$id=$db->next_id("sales_headers_aux");
$db->copy_to(qq{
          COPY sales_headers_aux (id, header, key_name, key_value) FROM stdin
        },$id,$inserted_id,my_conv($k),my_conv($extra_data->{$k}));
$did_extra_data{$k}=1;}}}my$ds=$doc_serie;
$ds=~s/ //g;
open(F,"> /opt/pos/server/data/areas/last_integrated.txt");
print F"$business_date $check_hour $ds";
close(F);
open(F,"> /opt/pos/server/data/areas/".$check->area()."/last_integrated.txt");
print F$business_date."\n".$check_hour."\n";
close(F);
if(exists$DELIVERY_AREAS{$check->area()}){&copy($file,"/tmp/$$");}if(exists$areas_validation->{$check->area()}){open(F,"> /opt/pos/server/data/areas/".$check->area()."/".$orig_table_number.".tovalidate");
close(F);
&copy($file,"/opt/pos/server/data/areas/".$check->area()."/".$orig_table_number);
unlink($file);}else{my$areas_table_names=App::POS::Counters->new(db=>$db,name=>'areas_table_names',);
my$kk=$check->area()."#".$orig_table_number;
my$orig_check_file="/opt/pos/server/data/areas/".$check->area()."/".$orig_table_number;
my$name_file=$orig_check_file.".name";
my$move=0;
if($extra_data->{keep_table_name}){$move=1;
&copy($name_file,"/tmp/.pos_name");}if(scalar(@$details_deleted)||!scalar(@$details)){$check->delete(1);}else{$check->delete();}if($move){&copy("/tmp/.pos_name",$name_file);
unlink("/tmp/.pos_name");}}if(exists$DELIVERY_AREAS{$check->area()}){my$tmp_f=$file;
$tmp_f=~s/\.close$/.delivered/;
&move("/tmp/$$",$tmp_f);}if(scalar@details_extra_data){$dbh->do("COPY sales_details_aux (header, product_id, gb_identifier, key_name, key_value) FROM stdin");
foreach my $rec(@details_extra_data){$dbh->pg_putline(join("\t",@$rec)."\n");}$dbh->pg_endcopy;}if(defined$info->{headers}->{customer_id}&&$info->{headers}->{doc_type}==7){$info->{headers}->{customer_credit}=$db_customer_info->{total_credit}+$info->{headers}->{value};
$db->do("UPDATE customers SET total_credit = ? WHERE id = ?",$info->{headers}->{customer_credit},$info->{headers}->{customer_id},);}elsif(defined$info->{headers}->{customer_id}&&$info->{headers}->{doc_type}==8){$info->{headers}->{customer_credit}=$db_customer_info->{total_credit}-$info->{headers}->{value};
$info->{headers}->{customer_credit}=0 if$info->{headers}->{customer_credit}<0;
$db->do("UPDATE customers SET total_credit = ? WHERE id = ?",$info->{headers}->{customer_credit},$info->{headers}->{customer_id},);}elsif(defined$info->{headers}->{customer_id}&&$info->{headers}->{doc_type}==2){$info->{headers}->{customer_credit}=$db_customer_info->{total_credit}-$info->{headers}->{value};
$info->{headers}->{customer_credit}=0 if$info->{headers}->{customer_credit}<0;
$db->do("UPDATE customers SET total_credit = ? WHERE id = ?",$info->{headers}->{customer_credit},$info->{headers}->{customer_id},);}if($c->{INTERNET_DASHBOARD}){open(F,"> /opt/pos/server/data/tmp/dashboard.txt");
print F"1\n";
close(F);}if(-e"/opt/pos/common/tmp/server_cache_refresh.txt"){unlink"/opt/pos/common/tmp/server_cache_refresh.txt"}$c->{RFID_TOKEN}||="";
if($c->{RFID_TOKEN}&&exists$extra_data->{rfid_card}&&exists$extra_data->{rfid_random}){my$r=$db->select("SELECT id FROM rfid_operations WHERE card_id = ? AND gbi = ?",$extra_data->{rfid_card},$extra_data->{rfid_random},);
for(@$r){$db->do("UPDATE rfid_operations SET header = ?, fo_doc_number = ? WHERE id = ?",$info->{headers}->{id},$info->{headers}->{fo_doc_number},$_->{id},);}}my$t8=new Benchmark;
print STDERR"Server> ".timestr(timediff($t8,$t1))." INTEGRATE CHECK TOTAL!!!!! ".($cnt++).":".timestr(timediff($t8,$t1))."\n" if$DEBUG_TIMINGS;
return$info;}sub areas_with_fixed_names{my$self=shift;
return$self->{__areas_with_fixed_names}if exists$self->{__areas_with_fixed_names};
my$r={};
if(-e"/opt/pos/server/data/areas/with_fixed_names.txt"){my$i=$self->hash_from_hdd("/opt/pos/server/data/areas/with_fixed_names.txt");
if(scalar keys%$i){my@a=split/,/,$i->{areas};
map{$r->{$_}=1}@a;}}$self->{__areas_with_fixed_names}=$r;
return$r;}sub _handle_product_suggestions{my$self=shift;
my($sug,$detail,$area,$details)=@_;
my$c=$self->config()->load();
$c->{PRODUCTS_SUGESTION_EXTRAS_ONLY}||=0;
my$id=$detail->[1];
my$station=$detail->[12];
my$price=$detail->[4];
my$valid=0;
foreach my $d(@$details){next unless scalar@$d;
if($d->[1]==$id){$valid=1;
next;}my$is_extra=$d->[13]eq 'EXTRA'?1:0;
if($c->{PRODUCTS_SUGESTION_EXTRAS_ONLY}&&!$is_extra){next;}if($valid==1){my$global_key=$id."#".$d->[1]."#";
$sug->increment($global_key);}}}sub __setup_linux{my$self=shift;
$self->debug("__setup_linux");
die"setup method needs to be run by root...\n" unless$ENV{USER}eq 'root';
if(-d"/shares/server"){system("umount -f /shares/server");
system("rm -rf /shares/server");}my$c=$self->config()->load();
my$ip=$self->ip();
my$gateway=$self->vfh($c,'NETWORK_GATEWAY');
if(open(F,"> /etc/samba/smb.conf")){my$conf=$self->__etc_samba_smb_conf();
print F$conf;
close(F);
system("/etc/init.d/samba restart");}}sub __etc_samba_smb_conf{return <<END;
[global]
workgroup = pos
server string = %h server
dns proxy = no
interfaces = lo eth0
bind interfaces only = true
log file = /var/log/samba/log.%m
max log size = 1000
syslog = 0
panic action = /usr/share/samba/panic-action %d
security = share
encrypt passwords = true
passdb backend = tdbsam
obey pam restrictions = yes
guest account = pos
invalid users = root
passwd program = /usr/bin/passwd %u
passwd chat = *Enter\\snew\\sUNIX\\spassword:* %n\\n *Retype\\snew\\sUNIX\\spassword:* %n\\n *password\\supdated\\ssuccessfully* .

[pos]
comment = pos share
path = /share
browseable = yes
read only = no
guest ok = yes
END
}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 daily_specials{my$self=shift;
return$self->{__daily_specials}if exists$self->{__daily_specials};
my$db=$self->database();
my$i={};
my$rec=$db->select("SELECT * FROM product_daily_specials");
map{$i->{$_->{product_id}}=$_}@$rec;
$self->{__daily_specials}=$i;
return$self->{__daily_specials};}sub _stock_products{my$self=shift;
return$self->{__stock_products}if exists$self->{__stock_products};
my$db=$self->database();
my$sth=$db->dbh()->prepare(qq{
    SELECT id FROM products WHERE deleted = 0 AND stock_use = 1
  });
$sth->execute();
my%c=();
while(my$row=$sth->fetchrow_arrayref){$c{$row->[0]}=1;}$self->{__stock_products}=\%c;
return$self->{__stock_products};}sub _areas_with_validation_required{my$self=shift;
return$self->{__areas_with_validation_required}if exists$self->{__areas_with_validation_required};
my$r={};
my$i={};
my$c=$self->config()->load_fixed();
$c->{AREA}||=[];
$c->{AREA_CHECK_VALIDATION}||=[];
for(my$n=0;$n<scalar@{$c->{AREA}};$n++){if($c->{AREA}->[$n]=~/,delivery/){$DELIVERY_AREAS{$n}=1;}my@k=split/,/,$c->{AREA}->[$n];
$i->{$k[0]}=$n;}foreach(@{$c->{AREA_CHECK_VALIDATION}}){if(exists$i->{$_}){$r->{$i->{$_}}=$_;}}$self->{__areas_with_validation_required}=$r;
return$r;}sub _decode_sale_cashier{my$self=shift;
my($config,$clerks,$station,$clerk)=@_;
if(!exists$config->{CASHIERS}){return"0";}my$cashiers=$self->_machine_cashiers($self,$station);
if(scalar keys%$cashiers>1){return$clerks->{$clerk}->{cashier};}elsif(scalar keys%$cashiers==1){my@a=keys%$cashiers;
return$a[0];}else{return"0";}}sub _dumper_read{my($f)=@_;
my$VAR1={};
if(open(F,"< $f")){local$/=undef;
my$cnt=<F>;
eval"$cnt";
close(F);}return$VAR1;}sub _dumper_save{my($f,$i)=@_;
if(open(F,"> $f")){print F Dumper($i);
close(F);}}sub _machine_cashiers{my$self=shift;
my($machine,$station)=@_;
$station||=0;
return$self->{__cashiers_in_use}->{$station}if exists$self->{__cashiers_in_use}->{$station};
my$cashiers={};
my$tmp="";
my$config_f=$station>0&&$station<80?$machine->dir_server()."/etc/config.ini":'/opt/pos/etc/config.ini';
my$cfg_o=App::POS::Config->new(file=>$config_f);
my$cfg=$cfg_o->load_fixed();
$cfg->{CASHIERS_BY_STATION}||=[];
foreach my $c(@{$cfg->{CASHIERS_BY_STATION}}){my@t=split/,/,$c;
next unless scalar@t==2;
next unless$t[0]=~/^\d+$/;
next unless$t[1]=~/^\d+$/;
if($t[0]==$station){$tmp.=$t[1].",";}}$tmp=~s/,$//;
if($tmp ne ''){my@toks=split/,/,$tmp;
map{$cashiers->{$_}=1}@toks;}$self->{__cashiers_in_use}->{$station}=$cashiers;
return$cashiers;}sub _get_product_no_points{my$self=shift;
my$headers=shift;
my$db=shift;
my$product_prices=$self->_get_product_prices($headers,$db);
return$self->{"__product_no_points"}||{};}sub _get_product_prices{my$self=shift;
my$headers=shift;
my$db=shift;
my$prices={};
if(!exists$self->{"__product_prices#".$headers->{area}}||-e"/opt/pos/common/tmp/server_cache_refresh.txt"){$self->{"__product_no_points"}={};
my$c=$self->config()->load();
my$area=$c->{AREA}->[$headers->{area}];
my@i=split/,/,$area;
my$price_to_use=$i[2];
$db->do("SET enable_nestloop TO off");
my$q=qq{
      SELECT id, $price_to_use AS price, no_points
      FROM products WHERE deleted = 0
    };
my$sth=$db->dbh()->prepare($q);
$sth->execute();
while(my$row=$sth->fetchrow_hashref){$prices->{$row->{id}}=$row->{price};
if($row->{no_points}){$self->{"__product_no_points"}->{$row->{id}}=1;}}$self->{"__product_prices#".$headers->{area}}=$prices;
$db->do("SET enable_nestloop TO on");}else{$prices=$self->{"__product_prices#".$headers->{area}};}return$prices;}sub _get_child_products{my$self=shift;
my$db=shift;
return$self->{__child_products}if exists$self->{__child_products};
my$dbh=$db->dbh();
my$sth=$db->dbh()->prepare(qq{
    -- SELECT p1.id AS child_id, p1.name AS child_name, p2.id AS parent_id,
    --  p2.name AS parent_name, f.name AS fam_name, sf.name AS subfam_name
    SELECT p1.id AS child_id, p2.id AS top_ip, f.name AS fam_name,
      sf.name AS subfam_name, p1.qtty_factor
    FROM products p1
    LEFT JOIN products p2 ON (p1.parent_product = p2.id)
    LEFT JOIN (
      SELECT product_id, MAX(group_id) as group_id
      FROM product_groups
      GROUP BY product_id
    ) pg ON (p2.id = pg.product_id)
    LEFT JOIN groups sf ON (pg.group_id = sf.id)
    LEFT JOIN groups f ON (sf.parent = f.id)
    WHERE p1.parent_product <> 0 AND p1.deleted = 0 AND p2.deleted = 0
  });
$sth->execute();
my%r=();
while(my$rec=$sth->fetchrow_hashref){$r{$rec->{child_id}}=$rec;}$self->{__child_products}=\%r;
return$self->{__child_products};}1;
