<?php

class ModuleContract extends Module
{
  public int $DirectoryId;

  function __construct(System $System)
  {
    parent::__construct($System);
    $this->Name = 'Contract';
    $this->Version = '1.0';
    $this->Creator = 'Chronos';
    $this->License = 'GNU/GPLv3';
    $this->Description = 'Contract documents management';
    $this->Dependencies = array(ModuleDocument::GetName(), ModuleSubject::GetName(), ModuleFile::GetName(), ModuleFinance::GetName());
    $this->Models = array(Contract::GetClassName());

    $this->DirectoryId = 0;
  }

  function DoStart(): void
  {
    $this->DirectoryId = Core::Cast($this->System)->Config['Contract']['DirectoryId'];
    $this->System->RegisterPage(['smlouvy', 'generovat'], 'PageContractGenerate');
    Core::Cast($this->System)->FormManager->RegisterClass('Contract', array(
      'Title' => 'Smlouvy',
      'Table' => 'Contract',
      'Items' => array(
        'DocumentLine' => array('Type' => 'TDocumentLine', 'Caption' => 'Dokladová řada', 'Default' => ''),
        'BillCode' => array('Type' => 'TDocumentLineCode', 'Caption' => 'Kód', 'Default' => '', 'Null' => true),
        'Subject' => array('Type' => 'TSubject', 'Caption' => 'Subjekt', 'Default' => ''),
        'ValidFrom' => array('Type' => 'Date', 'Caption' => 'Platnost od', 'Default' => ''),
        'ValidTo' => array('Type' => 'Date', 'Caption' => 'Platnost do', 'Default' => '', 'Null' => true),
        'File' => array('Type' => 'TFile', 'Caption' => 'Soubor', 'Default' => '', 'Null' => true),
        'Generate' => array('Type' => 'Boolean', 'Caption' => 'Generovat', 'Default' => ''),
        'EmployeeSalaries' => array('Type' => 'TEmployeeSalaryListContract', 'Caption' => 'Výplaty zaměstnanců', 'Default' => ''),
        'Customers' => array('Type' => 'TCustomerListContract', 'Caption' => 'Zákazníci', 'Default' => ''),
      ),
      'BeforeInsert' => array($this, 'BeforeInsertContract'),
      'ItemActions' => array(
        array('Caption' => 'Generovat', 'URL' => '/smlouvy/generovat/?i=#RowId'),
      )
    ));
    Core::Cast($this->System)->FormManager->RegisterFormType('TContract', array(
      'Type' => 'Reference',
      'Table' => 'Contract',
      'Id' => 'Id',
      'Name' => '(SELECT `DocumentLineCode`.`Name` FROM `DocumentLineCode` WHERE `Id`=`Contract`.`BillCode`)',
      'Filter' => '1',
    ));
  }

  function BeforeInsertContract(Form $Form): array
  {
    if (array_key_exists('Time', $Form->Values)) $Year = date("Y", $Form->Values['Time']);
      else $Year = date("Y", $Form->Values['ValidFrom']);
    $Form->Values['BillCode'] = ModuleDocument::Cast($this->System->GetModule('Document'))->GetNextDocumentLineNumberId($Form->Values['DocumentLine'], $Year);
    return $Form->Values;
  }

  static function Cast(Module $Module): ModuleContract
  {
    if ($Module instanceof ModuleContract)
    {
      return $Module;
    }
    throw new Exception('Expected ModuleContract type but '.gettype($Module));
  }
}

class PageContractGenerate extends Page
{
  function __construct(System $System)
  {
    parent::__construct($System);
    $this->Title = 'Generování smlouvy';
    $this->ParentClass = 'PagePortal';
  }

  function GenerateContract($Where = '')
  {
    $DirectoryId = ModuleContract::Cast($this->System->GetModule('Contract'))->DirectoryId;
    $Output = '';
    $DbResult = $this->Database->query('SELECT * FROM `Contract` WHERE (`BillCode` <> "") '.
      'AND (`ValidFrom` IS NOT NULL) AND (`Generate` = 1)'.$Where);
    while ($Row = $DbResult->fetch_assoc())
    {
      if ($Row['File'] == null)
      {
        $this->Database->insert('File', array('Name' => '', 'Size' => 0, 'Directory' => $DirectoryId, 'Time' => 'NOW()',
          'Hash' => 'SHA1(CONCAT(Id,Name,Size,Time))'));
        $FileId = $this->Database->insert_id;
      } else $FileId = $Row['File'];
      $FileName = 'smlouva-'.$FileId.'.pdf';
      $Bill = new BillContract($this->System);
      $Bill->ContractId = $Row['Id'];
      $FullFileName = ModuleFile::Cast($this->System->GetModule('File'))->File->GetDir($DirectoryId).$FileName;
      $Bill->SaveToFile($FullFileName);
      if (file_exists($FullFileName))
      {
        $this->Database->update('File', 'Id='.$FileId, array('Name' => $FileName, 'Size' => filesize($FullFileName), 'Hash' => 'SHA1(CONCAT(Id,Name,Size,Time))'));
        $this->Database->update('Contract', 'Id='.$Row['Id'], array('File' => $FileId));
        $Output .= 'Smlouva '.$Row['Id'].' vygenerována do souboru '.$FileName.'<br/>'."\n";
      } else $Output .= 'Soubor "'.$FullFileName.'" se nepodařilo uložit.';
    }
    return $Output;
  }

  function Show(): string
  {
    if (array_key_exists('i', $_GET) and is_numeric($_GET['i']))
    {
      $Output = $this->GenerateContract(' AND (Id='.($_GET['i'] * 1).')');
    } else $Output = 'Missing contract id.';
    return $Output;
  }
}

class BillContract extends Pdf
{
  public string $ContractId;

  function GenerateHTML(): string
  {
    $this->BorderTop = '0cm';
    $this->BorderLeft = '1cm';
    $this->BorderRight = '1cm';
    $this->BorderBottom = '0cm';
    $this->FontSize = 10;

    $DbResult = $this->Database->select('Subject', '*', '`Id`='.Core::Cast($this->System)->Config['Finance']['MainSubjectId']);
    if ($DbResult->num_rows > 0)
    {
      $Supplier = $DbResult->fetch_assoc();
    } else die('MainSubjectId not found in Subjects.');

    $DbResult = $this->Database->query('SELECT * FROM `Contract` WHERE `Id`='.$this->ContractId);
    if ($DbResult->num_rows > 0)
    {
      $Contract = $DbResult->fetch_array();
      $DbResult = $this->Database->query('SELECT * FROM `Subject` WHERE `Id`='.$Contract['Subject']);
      if ($DbResult->num_rows > 0)
      {
        $Subject = $DbResult->fetch_assoc();
      } else die('Customer Subject not found.');
    } else die('Contract not found.');

    $DbResult = $this->Database->select('DocumentLineCode', '*', '`Id`='.$Contract['BillCode']);
    if ($DbResult->num_rows > 0)
    {
      $SupplierBillCode = $DbResult->fetch_assoc();
    } else die('BillCode not found.');
    $ContractCode = $SupplierBillCode['Name'];

    $DbResult = $this->Database->select('Member', '*', '`Subject`='.$Contract['Subject']);
    if ($DbResult->num_rows > 0)
    {
      $Customer = $DbResult->fetch_assoc();
    } else die('Customer not found.');

    $PrefixMultiplier = new PrefixMultiplier();
    $ServiceType = '';
    $Price = 0;
    $MaxSpeed = '';
    $MinSpeed = '';
    $DbResult = $this->Database->query('SELECT * FROM ServiceCustomerRel '.
      'LEFT JOIN Service ON Service.Id=ServiceCustomerRel.Service '.
      'WHERE `ServiceCustomerRel`.`Customer`='.$Customer['Id'].' ');
    if ($DbResult->num_rows > 0)
    {   
      while ($Service = $DbResult->fetch_assoc())
      {
        if ($ServiceType != '') $ServiceType .= ', ';
        $ServiceType .= $Service['Name'];
        $Price += $Service['Price'];
        if ($Service['InternetSpeedMax'] != 0)
          $MaxSpeed = $PrefixMultiplier->Add($Service['InternetSpeedMax'], 'bit/s');
        if ($Service['InternetSpeedMin'] != 0)
          $MinSpeed = $PrefixMultiplier->Add($Service['InternetSpeedMin'], 'bit/s');
      }
    }

    $DbResult = $this->Database->query('SELECT NetworkInterface.LocalIP, NetworkInterface.MAC FROM NetworkDevice '.
      'LEFT JOIN NetworkInterface ON NetworkInterface.Device=NetworkDevice.Id '.
      'WHERE `NetworkDevice`.`Member`='.$Customer['Id'].' ');
    if ($DbResult->num_rows > 0)
    {
      $NetworkInterface = $DbResult->fetch_assoc();
      $IpAddress = $NetworkInterface['LocalIP'];
      $MacAddress = $NetworkInterface['MAC'];

      $DbResult = $this->Database->query('SELECT * FROM NetworkSubnet '.
      'WHERE CompareNetworkPrefix(INET_ATON("'.$IpAddress.'"), INET_ATON(`AddressRange`), `Mask`)');
      if ($DbResult->num_rows > 0)
      {
        $Subnet = $DbResult->fetch_assoc();
        $DefaultGateway = $Subnet['Gateway'];
        $NetworkAddressIPv4 = new NetworkAddressIPv4();
        $NetworkAddressIPv4->Prefix = $Subnet['Mask'];
        $NetworkAddressIPv4->Address = $NetworkAddressIPv4->GetNetMask();
        $NetMask = $NetworkAddressIPv4->AddressToString();
      } else die('Subnet not found.');
      $Interface = 'Ethernet';
      $PrimaryDNS = '10.145.64.8';
    } else 
    {
      $IpAddress = '';
      $MacAddress = '';
      $DefaultGateway = '';
      $NetMask = '';
      $Interface = '';
      $PrimaryDNS = '';
    }

    $VarSymbol = $Subject['Id'];
    $DbResult = $this->Database->query('SELECT FinanceBankAccount.*, CONCAT(FinanceBankAccount.Number, "/", FinanceBank.Code) AS NumberFull FROM FinanceBankAccount '.
      'JOIN FinanceBank ON FinanceBank.Id=FinanceBankAccount.Bank '.
      'WHERE (FinanceBankAccount.`Subject`='.Core::Cast($this->System)->Config['Finance']['MainSubjectId'].') AND (FinanceBankAccount.`Use`=1)');
    if ($DbResult->num_rows > 0)
    {
      $SupplierBankAccount = $DbResult->fetch_assoc();
    } else die('Bank account not found.');

    $DbResult = $this->Database->select('FinanceBillingPeriod', '*', 'Id='.$Customer['BillingPeriod']);
    if ($DbResult->num_rows > 0)
    {
      $BillingPeriod = $DbResult->fetch_assoc();
    } else die('BillingPeriod not found.');
    $PaymentPeriod = $BillingPeriod['Name'];

    $BankAccount = $SupplierBankAccount['NumberFull'];

    $DbResult = $this->Database->select('Member', '*', '`Subject`='.Core::Cast($this->System)->Config['Finance']['MainSubjectId']);
    if ($DbResult->num_rows > 0)
    {
      $SupplierMember = $DbResult->fetch_assoc();
    } else die('Customer not found.');

    $DbResult = $this->Database->query('SELECT Contact.Value AS Phone FROM User '.
      'LEFT JOIN Contact ON Contact.User=User.ID AND Contact.Category=1 '.
      'WHERE User.Id='.$SupplierMember['ResponsibleUser'].' ORDER BY `Contact`.`Receive` DESC');
    if ($DbResult->num_rows > 0)
    {
      $SupplierUser = $DbResult->fetch_assoc();
      $SupplierPhone = $SupplierUser['Phone'];
    } else $SupplierPhone = '';

    $DbResult = $this->Database->query('SELECT Contact.Value AS Phone FROM User '.
      'LEFT JOIN Contact ON Contact.User=User.ID AND Contact.Category=1 '.
      'WHERE User.Id='.$Customer['ResponsibleUser'].' ORDER BY `Contact`.`Receive` DESC');
    if ($DbResult->num_rows > 0)
    {
      $CustomerUser = $DbResult->fetch_assoc();
      $CustomerPhone = $CustomerUser['Phone'];
      if ($CustomerPhone == null) $CustomerPhone = '';
    } else $CustomerPhone = '';

    $DbResult = $this->Database->query('SELECT Email, Contact.Value AS ContactEmail FROM User '.
      'LEFT JOIN Contact ON Contact.User=User.ID AND Contact.Category=4 '.
      'WHERE User.Id='.$Customer['ResponsibleUser'].' ORDER BY `Contact`.`Receive` DESC');
    if ($DbResult->num_rows > 0)
    {
      $CustomerUser = $DbResult->fetch_assoc();
      $CustomerEmail = $CustomerUser['Email'];
      if ($CustomerEmail == null) 
      {
        $CustomerEmail = $CustomerUser['ContactEmail'];
        if ($CustomerEmail == null) $CustomerEmail = '';
      }
    } else $CustomerEmail = '';

    $SupplierName = $Supplier['Name'];
    $SupplierStreet = $Supplier['AddressStreet'];
    $SupplierTown = $Supplier['AddressTown'];
    $SupplierPsc = $Supplier['AddressPSC'];
    $SupplierIC = $Supplier['IC'];
    if ($Supplier['PayVAT'] == 1) $SupplierDIC = 'plátce DPH';
      else $SupplierDIC = 'neplátce DPH';

    $Web = '<a href="'.$Supplier['WWW'].'">'.$this->SimplifiedLink($Supplier['WWW']).'</a>';

    $CustomerName = $Subject['Name'];
    $CustomerStreet = $Subject['AddressStreet'];
    $CustomerTown = $Subject['AddressTown'];
    $CustomerPsc = $Subject['AddressPSC'];
    $CustomerIC = $Subject['IC'];
    $CustomerDIC = $Subject['DIC'];
    $CustomerPhone = $CustomerPhone;
    $CustomerEmail = $CustomerEmail;

    $SignDate = HumanDate($Contract['ValidFrom']);

    $Output = '<html>
    <head></head>
    <body><table width="100%"><tr><td colspan="2">'.
      '<font size="6"><div align="center">Smlova o připojení k internetu a poskytování datových služeb</font></div>'.
      '<font size="3"><div align="center">Číslo: '.$ContractCode.'</font></div>'.
      '</td></tr>'.
      '</table>'.
      '<br>'.
      '<table width="100%">'.
      '<tr><th width="25%">Poskytovatel:</td><td width="25%"></td><th width="25%">Odběratel:</th><td width="25%"></td></tr>'.
      '<tr><td>'.NotBlank($SupplierName).'</td><td></td><td>'.NotBlank($CustomerName).'</td><td></td></tr>'.
      '<tr><td>'.NotBlank($SupplierStreet).'</td><td></td><td>'.NotBlank($CustomerStreet).'</td><td></td></tr>'.
      '<tr><td>'.NotBlank($SupplierTown).'</td><td></td><td>'.NotBlank($CustomerTown).'</td><td></td></tr>'.
      '<tr><td>'.NotBlank($SupplierPsc).'</td><td></td><td>'.NotBlank($CustomerPsc).'</td><td></td></tr>'.
      '<tr><td colspan="4">&nbsp;</td></tr>'.
      '<tr><td>IČ:</td><td>'.NotBlank($SupplierIC).'</td><td>Telefon:</td><td>'.NotBlank($CustomerPhone).'</td></tr>'.
      '<tr><td>DIČ:</td><td>'.NotBlank($SupplierDIC).'</td><td>E-mail:</td><td>'.NotBlank($CustomerEmail).'</td></tr>'.
      '<tr><td>Telefon:</td><td>'.NotBlank($SupplierPhone).'</td><td>RČ/IČ:</td><td>'.NotBlank($CustomerIC).'</td></tr>'.
      '<tr><td>Web:</td><td>'.NotBlank($Web).'</td><td>OP/DIČ:</td><td>'.NotBlank($CustomerDIC).'</td></tr>'.
      '<tr><td>Bank. spojení:</td><td>'.NotBlank($BankAccount).'</td><td>Přípojné místo:</td><td></td></tr>'.
      '</table>'.
      '<br/>'.
      '<strong>I. Předmět smlouvy:</strong><br/>'.
      'Smlouva se uzavírá mezi Poskytovatelem a Odběratelem a předmětem smlouvy je poskytování datových služeb a připojení k síti internet, umožňující Odběrateli odebírat v koncovém bodě za úplatu internetovou konektivitu prostřednictvím telekomunikační sítě Poskytovatele. Odběratel se zavazuje za tyto služby Poskytovateli platit cenu ve výši a pravidelných intervalech uvedených v této smlouvě. Smlouva se sjednává na dobu neurčitou.<br/>'.
      '<br/>'.
      '<strong>II. Poskytované služby:</strong><br/>'.
      '<table width="100%" border="1">'.
      '<tr><td width="50%">Zvolené služby:</td><td>'.NotBlank($ServiceType).'</td></tr>'.
      '</table>'.
      '<br/>'.
      '<table width="100%" border="1">'.
      '<tr><th colspan="4" align="center">Technické specifikace služby</th></tr>'.
      '<tr><td width="25%">Max. rychlost:</td><td width="25%">'.NotBlank($MaxSpeed).'</td><td width="25%">IP adresa:</td><td width="25%">'.NotBlank($IpAddress).'</td></tr>'.
      '<tr><td>Min. rychlost:</td><td>'.NotBlank($MinSpeed).'</td><td>Maska podsítě:</td><td>'.NotBlank($NetMask).'</td></tr>'.
      '<tr><td>MAC adresa:</td><td>'.NotBlank($MacAddress).'</td><td>Výchozí brána:</td><td>'.NotBlank($DefaultGateway).'</td></tr>'.
      '<tr><td>Předávací rozhraní:</td><td>'.NotBlank($Interface).'</td><td>Primární DNS:</td><td>'.NotBlank($PrimaryDNS).'</td></tr>'.
      '</table>'.
      '<br/>'.
      '<strong>III. Cena a platební podmínky:</strong><br/>'.
      'Poplatky a pravidelné platby budou na základě této smlouvy hrazeny Odběratelem na bankovní účet Poskytovatele bankovním převodem, nebo složenkou, v uvedené výši a s uvedenou frekvencí. Jako VS bude uvedeno přidělené číslo. V případě prodlení s platbou mohou být uplatněny sankce, či služba pozastavena, nebo zrušena (dle Ceníku a Všeobecných smluvních podmínek).<br/>'.
      '<table width="100%">'.
      '<tr><td width="25%">Cena služeb:</td><td width="25%">'.NotBlank($Price).' Kč</td><td width="25%">Číslo účtu:</td><td width="25%">'.NotBlank($BankAccount).'</td></tr>'.
      '<tr><td>Pronájem zařízení:</td><td>0 Kč</td><td>Variabilní symbol:</td><td>'.NotBlank($VarSymbol).'</td></tr>'.
      '<tr><td>Frekvence platby:</td><td>'.NotBlank($PaymentPeriod).'</td><td></td><td></td></tr>'.
      '<tr><td>První platba:</td><td></td><td></td><td></td></tr>'.
      '<tr><td>Splatnost:</td><td>do 21. dne uvedeného měsíce</td><td></td><td></td></tr>'.
      '</table>'.
      '<br/>'.
      '<strong>IV. Závěrečná ustanovení:</strong><br/>'.
      'Odběratel svým podpisem stvrzuje, že se seznámil a souhlasí se Všeobecnými smluvními podmínkami, aktuálním Ceníkem Poskytovatele a výše uvedenými skutečnostmi, které tvoří součást této Smlouvy. Dále stvrzuje, že souhlasí s provedením instalace a umístěním přijímacího zařízení a kabeláže a nemá proti nim námitky, příp., že vlastní veškerá příslušná povolení a souhlas s jejich umístěním. Smlouva je vyhotovena ve dvou provedeních, kdy Odběratel i Poskytovatel obdrží po jedné. Aktivace služby začíná dnem podpisu této Smlouvy. Výpovědní lhůta je 30 dnů. Případné změny této Smlouvy vyžadují formy vzájemně oběma stranami podepsaného dodatku.<br/>'.
      '<br/>'.
      '<table width="100%">'.
      '<tr><td width="20%" aling="left">Ve Zděchově</td><td width="20%">dne '.NotBlank($SignDate).'</td></tr>'.
      '</table>'.
      '<br/><br/><br/><br/>'.
      '<table width="100%">'.
      '<tr><td width="50%" align="center">odběratel</td><td width="50%" align="center">dodavatel</td></tr>'.
      '</table>'.
      '</td></tr>'.
      '</table>'.
      '</body></html>';
    return $Output;
  }

  function SimplifiedLink(string $Link): string
  {
    if (substr($Link, 0, 8) == 'https://') $Link = substr($Link, 8);
    if (substr($Link, 0, 7) == 'http://') $Link = substr($Link, 7);
    return $Link;
  }
}

class Contract extends Model
{
  static function GetModelDesc(): ModelDesc
  {
    $Desc = new ModelDesc(self::GetClassName());
    $Desc->AddReference('DocumentLine', DocumentLine::GetClassName());
    $Desc->AddReference('BillCode', DocumentLineCode::GetClassName());
    $Desc->AddReference('Subject', Subject::GetClassName());
    $Desc->AddDate('ValidFrom');
    $Desc->AddDate('ValidTo');
    $Desc->AddReference('File', File::GetClassName());
    return $Desc;
  }
}