{"action":"created","comment":{"url":"https://api.github.com/repos/dodoas/lodo/pulls/comments/113163814","pull_request_review_id":34529087,"id":113163814,"diff_hunk":"@@ -0,0 +1,1120 @@\n+<?\n+class reconciliation {\n+\n+    // Total sum\n+    public  $Sum;\n+    // Sum by account\n+    public  $SumAccountH = array();\n+    // All open vouchers\n+    public  $VoucherH = array();\n+    // All matches\n+    public  $MatchH = array();\n+    // Accounts that should not be included\n+    public  $HiddingAccounts = array();\n+\n+\n+    // Matched vouchers - reconcilable\n+    private $Matched = array();\n+    // HTML for Highlight links for each voucher\n+    private $VoucherHighlightLinksHTML = array();\n+    // Map of all vouchers reconciled against each other (ie. md5($VoucherID1 . \"#\" . $VoucherID2)\n+    private $CloseAgainst = array();\n+    // Vouchers closed while running the close algorithm\n+    private $ClosedVouchers = array();\n+\n+    // Input paramters\n+    public $AccountPlanID = 0;\n+    public $ReskontroFromAccount = 0;\n+    public $ReskontroToAccount = 0;\n+    public $DepartmentID = 0;\n+    public $ProjectID = 0;\n+\n+    function __construct($args)\n+    {\n+        foreach ($args as $key => $value) {\n+            $this->{$key} = $value;\n+        }\n+    }\n+\n+    /**\n+     * Find unreconciled vouchers with specified KID number\n+     * Does not include the voucher with specified voucher id\n+     */\n+    function findOpenPostKid($KID, $VoucherID) {\n+      global $_lib;\n+\n+      $OpenVouchers = array();\n+      $Count = 0;\n+      $Status = 0;\n+\n+      if ($KID > 0) {\n+        $OpenVouchersQuery = \"\n+          SELECT\n+            v.JournalID,\n+            v.VoucherID,\n+            v.AmountIn,\n+            v.AmountOut,\n+            v.AccountPlanID,\n+            v.VoucherDate,\n+            v.KID,\n+            v.InvoiceID\n+          FROM\n+            voucher v\n+            JOIN\n+            (\n+              SELECT *\n+              FROM accountplan\n+              WHERE\n+              EnablePostPost = 1 AND\n+              EnableReskontro = 0\n+            ) ap\n+            ON v.AccountPlanID = ap.AccountPlanID\n+          WHERE\n+            v.KID = $KID AND\n+            v.VoucherID != $VoucherID AND\n+            v.VoucherReconciliationID IS NULL AND\n+            v.Active = 1\n+          ORDER BY ap.AccountName ASC\";\n+        $OpenVouchersResult = $_lib['db']->db_query($OpenVouchersQuery);\n+        while ($Voucher = $_lib['db']->db_fetch_assoc($OpenVouchersResult)) {\n+          $OpenVouchers[$Voucher['JournalID']][$Voucher['VoucherID']] = $Voucher;\n+          $Count++;\n+        }\n+\n+        if ($Count == 0) {\n+          $_lib['message']->add('Ingen KID funnet p&aring; noen &aring;pne bilag');\n+        } elseif ($Count == 1) {\n+          $_lib['message']->add('Fant eksakt KID matchKIDH p&aring; bilag');\n+          $Status = 1;\n+        } else {\n+          $_lib['message']->add('Fant flere matchKIDHende KID p&aring; ulike bilag');\n+        }\n+      } else {\n+        $_lib['message']->add('Ingen KID oppgitt');\n+      }\n+      return array($Status, $OpenVouchers);\n+    }\n+\n+    /**\n+     * Get counter voucher data for the supplied open voucher(s)\n+     */\n+    function getKIDInfo($OpenVouchers) {\n+      global $_lib, $accounting;\n+\n+      $Count = 0;\n+      $Status = 0;\n+      $AccountPlanID = 0;\n+      $AmountIn = 0;\n+      $AmountOut = 0;\n+\n+      foreach ($OpenVouchers as $JournalID => $Journal) {\n+        foreach ($Journal as $VoucherID => $Voucher) {\n+          $AccountPlan = $accounting->get_accountplan_object($Voucher['AccountPlanID']);\n+          if ($AccountPlan->AccountPlanType == 'customer'\n+            || $AccountPlan->AccountPlanType == 'supplier')\n+          {\n+            $Count++;\n+            $AccountPlanID = $Voucher['AccountPlanID'];\n+            // Switch the amount sides to make a counter voucher\n+            $AmountIn = $Voucher['AmountOut'];\n+            $AmountOut = $Voucher['AmountIn'];\n+          }\n+        }\n+      }\n+\n+      if ($Count == 0) {\n+        $_lib['message']->add('Ingen kundereskontro funnet p&aring; den oppgitte KID referansen');\n+      } elseif ($Count == 1) {\n+        $_lib['message']->add('En kundereskontro funnet p&aring; den oppgitte KID referansen');\n+        $Status = 1;\n+      } else {\n+        $_lib['message']->add('Flere kundereskontro funnet p&aring; den oppgitte KID referansen. Tilfeldig valg ble gjort');\n+      }\n+      return array($AccountPlanID, $AmountIn, $AmountOut, $JournalID, $VoucherID, $Status);\n+    }\n+\n+    /**\n+     * Gathers all unreconciled vouchers\n+     */\n+    function getopenpost($OnlyAccountPlanID = 0) {\n+      global $_lib, $accounting;\n+\n+      // Clean old totals\n+      $this->total['total']->Name = 'Total';\n+      $this->total['total']->AmountIn = 0;\n+      $this->total['total']->AmountOut = 0;\n+      $this->total['total']->FAmountIn = 0;\n+      $this->total['total']->FAmountOut = 0;\n+      $this->VoucherH = array();\n+      $this->MatchH = array();\n+      $this->SumAccountH = array();\n+      $this->HiddingAccounts = array();\n+\n+      if ($this->AccountPlanID) {\n+        $AccountPlan = $accounting->get_accountplan_object($this->AccountPlanID);\n+        $Where = '';\n+        if ($this->DepartmentID > 0) {\n+          $WhereExtra = ' v.DepartmentID = ' . (int) $this->DepartmentID . ' AND ';\n+        }\n+        if ($this->ProjectID > 0) {\n+          $WhereExtra .= ' v.ProjectID = ' . (int) $this->ProjectID . ' AND ';\n+        }\n+\n+        if ($this->ReskontroFromAccount) {\n+          $Where .= \" v.AccountPlanID >= '\" . $this->ReskontroFromAccount . \"' AND \";\n+        }\n+        if ($this->ReskontroToAccount) {\n+          $Where .= \" v.AccountPlanID <= '\" . $this->ReskontroToAccount . \"' AND \";\n+        }\n+        $Where .= \" ap.AccountPlanType = '\" . $AccountPlan->ReskontroAccountPlanType . \"' AND \";\n+\n+        $VoucherQuery = \"\n+          SELECT\n+            v.VoucherID,\n+            v.JournalID,\n+            v.AccountPlanID,\n+            v.InvoiceID,\n+            v.KID,\n+            v.MatchNumber,\n+            v.AmountIn,\n+            v.AmountOut,\n+            v.ForeignAmount,\n+            v.matched_by,\n+            v.VoucherType,\n+            v.VoucherDate,\n+            v.VoucherPeriod,\n+            v.Vat as VAT,\n+            v.Quantity,\n+            v.DepartmentID,\n+            v.ProjectID,\n+            v.DueDate,\n+            v.DescriptionID,\n+            v.Description,\n+            v.ForeignCurrencyID,\n+            v.ForeignConvRate\n+          FROM\n+            (\n+              SELECT\n+                *\n+              FROM\n+                voucher\n+              WHERE\n+                Active = 1 AND\n+                VoucherReconciliationID IS NULL\n+            ) v,\n+            accountplan ap\n+          WHERE\n+            v.AccountPlanID = ap.AccountPlanID AND\n+            $Where\n+            $WhereExtra\n+            ap.EnablePostPost = 1\n+          ORDER BY\n+            v.AccountPlanID ASC,\n+            v.VoucherPeriod ASC,\n+            v.VoucherDate ASC,\n+            v.VoucherID ASC\";\n+\n+        // Building VoucherQuery, the most time consuming part starts here(tested with benchmark)\n+        if ($OnlyAccountPlanID) {\n+          // Populate HiddingAccounts\n+          $HiddingAccountsQuery = \"\n+            SELECT\n+              ap.AccountName,\n+              ap.AccountPlanID\n+            FROM\n+              accountplan ap,\n+              (\n+                SELECT\n+                  AccountPlanID,\n+                  VoucherID\n+                FROM voucher\n+                WHERE\n+                  Active = 1 AND\n+                  VoucherReconciliationID IS NULL\n+              ) v\n+            WHERE\n+              v.AccountPlanID = ap.AccountPlanID AND\n+              $Where\n+              $WhereExtra\n+              ap.EnablePostPost = 1\n+            GROUP BY ap.AccountPlanID\";\n+\n+          $HiddingAccountsResult = $_lib['db']->db_query($HiddingAccountsQuery);\n+          while (($Account = $_lib['db']->db_fetch_assoc($HiddingAccountsResult))) {\n+            $this->HiddingAccounts[] = $Account;\n+          }\n+          $VoucherQuery = \"\n+            SELECT\n+              v.VoucherID,\n+              v.JournalID,\n+              v.AccountPlanID,\n+              v.InvoiceID,\n+              v.KID,\n+              v.MatchNumber,\n+              v.AmountIn,\n+              v.AmountOut,\n+              v.ForeignAmount,\n+              v.matched_by,\n+              v.VoucherType,\n+              v.VoucherDate,\n+              v.VoucherPeriod,\n+              v.Vat as VAT,\n+              v.Quantity,\n+              v.DepartmentID,\n+              v.ProjectID,\n+              v.DueDate,\n+              v.DescriptionID,\n+              v.Description,\n+              v.ForeignCurrencyID,\n+              v.ForeignConvRate\n+            FROM\n+              (\n+                SELECT\n+                  *\n+                FROM\n+                  voucher\n+                WHERE\n+                  Active = 1 AND\n+                  VoucherReconciliationID IS NULL\n+              ) v,\n+              accountplan ap\n+            WHERE\n+              v.AccountPlanID = ap.AccountPlanID AND\n+              v.AccountPlanID = $OnlyAccountPlanID AND\n+              $Where\n+              $WhereExtra\n+              ap.EnablePostPost = 1\n+            ORDER BY\n+              v.AccountPlanID ASC,\n+              v.VoucherPeriod ASC,\n+              v.VoucherDate ASC,\n+              v.VoucherID ASC\";\n+        }\n+        $VoucherResult = $_lib['db']->db_query($VoucherQuery);\n+        $accounting->cache_all_accountplans();\n+\n+        // Loop and calculate all data\n+        $VouchersProcessed = array();\n+        while ($Voucher = $_lib['db']->db_fetch_object($VoucherResult)) {\n+          if (in_array($Voucher->VoucherID, $VouchersProcessed)) {\n+            continue;\n+          }\n+          $VouchersProcessed[] = $Voucher->VoucherID;\n+\n+          $AccountPlanID = $Voucher->AccountPlanID;\n+\n+          // Initialize for possible groups for KID, InvoiceID and MatchNumber\n+          if ($Voucher->KID\n+            && !isset($this->MatchH[$AccountPlanID]['KID'][$Voucher->KID]))\n+          {\n+            $this->MatchH[$AccountPlanID]['KID'][$Voucher->KID] = 0;\n+            $this->MatchH[$AccountPlanID]['KIDJournals'][$Voucher->KID] = array();\n+          }\n+          if ($Voucher->InvoiceID\n+            && !isset($this->MatchH[$AccountPlanID]['InvoiceID'][$Voucher->InvoiceID]))\n+          {\n+            $this->MatchH[$AccountPlanID]['InvoiceID'][$Voucher->InvoiceID] = 0;\n+            $this->MatchH[$AccountPlanID]['InvoiceIDJournals'][$Voucher->InvoiceID] = array();\n+          }\n+          // If there is no MatchNumber, set to 0\n+          if (!$Voucher->MatchNumber) {\n+            $Voucher->MatchNumber = 0;\n+          }\n+          if ($Voucher->MatchNumber\n+            && !isset($this->MatchH[$AccountPlanID]['MatchNumber'][$Voucher->MatchNumber]))\n+          {\n+            $this->MatchH[$AccountPlanID]['MatchNumber'][$Voucher->MatchNumber] = 0;\n+            $this->MatchH[$AccountPlanID]['MatchNumberJournals'][$Voucher->MatchNumber] = array();\n+          }\n+\n+\n+          // Sum up for the total\n+          $this->total['total']->AmountIn += $Voucher->AmountIn;\n+          $this->total['total']->AmountOut += $Voucher->AmountOut;\n+          if ($Voucher->AmountIn > 0) {\n+            $this->total['total']->FAmountIn += $Voucher->ForeignAmount;\n+          }\n+          if ($Voucher->AmountOut > 0) {\n+            $this->total['total']->FAmountOut += $Voucher->ForeignAmount;\n+          }\n+\n+          // Initialize for each new account plan\n+          if (!isset($this->SumAccountH[$AccountPlanID]->Name)) {\n+            $TmpAccountPlan = $accounting->get_accountplan_object($Voucher->AccountPlanID);\n+            $this->SumAccountH[$AccountPlanID]->Name = $TmpAccountPlan->AccountPlanID.\" - \".$TmpAccountPlan->AccountName;\n+          }\n+          $this->SumAccountH[$AccountPlanID]->AmountIn += $Voucher->AmountIn;\n+          $this->SumAccountH[$AccountPlanID]->AmountOut += $Voucher->AmountOut;\n+          if ($Voucher->AmountIn > 0) {\n+            $this->SumAccountH[$AccountPlanID]->FAmountIn += $Voucher->ForeignAmount;\n+          }\n+          if ($Voucher->AmountOut > 0) {\n+            $this->SumAccountH[$AccountPlanID]->FAmountOut += $Voucher->ForeignAmount;\n+          }\n+\n+          // Automatic matching\n+          // Try and match by KID\n+          if ($Voucher->KID\n+            && ($Voucher->matched_by == '0'\n+            || $Voucher->matched_by == 'kid'))\n+          {\n+            $this->MatchH[$AccountPlanID]['KID'][$Voucher->KID] =\n+              round($this->MatchH[$AccountPlanID]['KID'][$Voucher->KID], 3) +\n+              ($Voucher->AmountIn - $Voucher->AmountOut);\n+            $this->MatchH[$AccountPlanID]['KIDJournals'][$Voucher->KID][] =\n+              array(\n+                'JournalID' => $Voucher->JournalID,\n+                'VoucherID' => $Voucher->VoucherID\n+              );\n+          }\n+          // Try to match by InvoiceID\n+          if ($Voucher->InvoiceID\n+            && ($Voucher->matched_by == '0'\n+            || $Voucher->matched_by == 'invoice'))\n+          {\n+            $this->MatchH[$AccountPlanID]['InvoiceID'][$Voucher->InvoiceID] =\n+              round($this->MatchH[$AccountPlanID]['InvoiceID'][$Voucher->InvoiceID], 3) +\n+              ($Voucher->AmountIn - $Voucher->AmountOut);\n+            $this->MatchH[$AccountPlanID]['InvoiceIDJournals'][$Voucher->InvoiceID][] =\n+              array(\n+                'JournalID' => $Voucher->JournalID,\n+                'VoucherID' => $Voucher->VoucherID\n+              );\n+          }\n+          // Try to match by MatchNumber, if MatchNumber is 0 this will be skipped\n+          if ($Voucher->MatchNumber\n+            && ($Voucher->matched_by == '0'\n+            || $Voucher->matched_by == 'match'))\n+          {\n+            $this->MatchH[$AccountPlanID]['MatchNumber'][$Voucher->MatchNumber] =\n+              round($this->MatchH[$AccountPlanID]['MatchNumber'][$Voucher->MatchNumber], 3) +\n+              ($Voucher->AmountIn - $Voucher->AmountOut);\n+            $this->MatchH[$AccountPlanID]['MatchNumberJournals'][$Voucher->MatchNumber][] =\n+              array(\n+                'JournalID' => $Voucher->JournalID,\n+                'VoucherID' => $Voucher->VoucherID\n+              );\n+          }\n+\n+          // Data from each voucher\n+          $this->VoucherH[$AccountPlanID][$Voucher->VoucherID] = $Voucher;\n+          if ($Voucher->AmountIn > 0) {\n+            $this->VoucherH[$AccountPlanID][$Voucher->VoucherID]->ForeignAmountIn = $Voucher->ForeignAmount;\n+          }\n+          if ($Voucher->AmountOut > 0) {\n+            $this->VoucherH[$AccountPlanID][$Voucher->VoucherID]->ForeignAmountOut = $Voucher->ForeignAmount;\n+          }\n+        }\n+\n+        // Account sum, what is actually recorded on the general ledger account\n+        $SaldoQuery = \"\n+          SELECT \n+            SUM(v.AmountIn) AS SumIn,\n+            SUM(v.AmountOut) AS SumOut,\n+            SUM(IF(v.AmountIn > 0, v.ForeignAmount, 0)) AS FSumIn,\n+            SUM(IF(v.AmountOut > 0, v.ForeignAmount, 0)) AS FSumOut\n+          FROM\n+            voucher v\n+          WHERE\n+            v.AccountPlanID = '\" . $this->AccountPlanID . \"' AND\n+            $WhereExtra\n+            v.Active = 1\";\n+        $Saldo = $_lib['storage']->get_row(array('query' => $SaldoQuery));\n+\n+        $this->total['account']->Name = $AccountPlan->AccountPlanID.\" - \".$AccountPlan->AccountName;\n+        $SumSaldo  = $Saldo->SumIn  - $Saldo->SumOut;\n+        $SumFSaldo = $Saldo->FSumIn - $Saldo->FSumOut;\n+\n+        if ($SumSaldo > 0) {\n+          $this->total['account']->AmountIn = $SumSaldo;\n+          $this->total['account']->AmountOut = 0;\n+        } elseif ($SumSaldo < 0) {\n+          $this->total['account']->AmountOut = abs($SumSaldo);\n+          $this->total['account']->AmountIn = 0;\n+        }\n+        if ($SumFSaldo > 0) {\n+          $this->total['account']->FAmountIn = $SumFSaldo;\n+        } elseif ($SumFSaldo < 0) {\n+          $this->total['account']->FAmountOut = abs($SumFSaldo);\n+        }\n+\n+        foreach ($this->SumAccountH as $AccountPlanID => $Account) {\n+          $this->SumAccountH[$AccountPlanID]->Diff = $Account->AmountIn  - $Account->AmountOut;\n+          $this->SumAccountH[$AccountPlanID]->FDiff = $Account->FAmountIn - $Account->FAmountOut;\n+        }\n+\n+        $this->total['total']->Diff = $this->total['total']->AmountIn - $this->total['total']->AmountOut;\n+        $this->total['total']->FDiff = $this->total['total']->FAmountIn - $this->total['total']->FAmountOut;\n+        $this->calculateDiff();\n+      } else {\n+        print \"Ingen kontoer er valgt<br>\";\n+      }\n+      $this->markAsUsed();\n+    }\n+\n+    /**\n+     * This is to find out if there is a difference between ledger and general ledger sum\n+     */\n+    function calculateDiff() {\n+      $this->total['diff']->Name = 'Differanse';\n+      $this->total['diff']->AmountIn =\n+        $this->total['account']->AmountIn - $this->total['total']->AmountIn;\n+      $this->total['diff']->AmountOut =\n+        $this->total['account']->AmountOut - $this->total['total']->AmountOut;\n+      $this->total['diff']->FAmountIn =\n+        $this->total['account']->FAmountIn - $this->total['total']->FAmountIn;\n+      $this->total['diff']->FAmountOut =\n+        $this->total['account']->FAmountOut - $this->total['total']->FAmountOut;\n+\n+      $this->total['diff']->Diff =\n+        round($this->total['diff']->AmountIn - $this->total['diff']->AmountOut, 2);\n+      $this->total['diff']->FDiff =\n+        round($this->total['diff']->FAmountIn - $this->total['diff']->FAmountOut, 2);\n+\n+      $this->total['account']->Diff =\n+        round($this->total['account']->AmountIn - $this->total['account']->AmountOut, 2);\n+      $this->total['account']->FDiff =\n+        round($this->total['account']->FAmountIn - $this->total['account']->FAmountOut, 2);\n+    }\n+\n+    /**\n+     * Goes through the potential matches and if the sum of the match group is 0 and\n+     * all of the vouchers for this group are not marked as used, mark them\n+     */\n+    function markAsUsed() {\n+      $MatchOrder = array(\"InvoiceID\", \"KID\", \"MatchNumber\");\n+\n+      // for each account\n+      foreach ($this->MatchH as $AccountPlanID => $Matches) {\n+        foreach ($MatchOrder as $MatchedBy) {\n+          // If nothing set for this group, skip it\n+          if (!isset($this->MatchH[$AccountPlanID][$MatchedBy])) {\n+            continue;\n+          }\n+\n+          foreach ($Matches[$MatchedBy] as $MatchKey => $MatchKeySum) {\n+            // If the sum is set and it is 0\n+            if (isset($this->MatchH[$AccountPlanID][$MatchedBy][$MatchKey])\n+              && round($MatchKeySum, 2) == 0)\n+            {\n+              // Check if any of the vouchers were used already and set their highlight link HTML string\n+              $Used = false;\n+              $HighlightLinkHTML = array();\n+              foreach ($Matches[$MatchedBy.\"Journals\"][$MatchKey] as $VoucherListEntry) {\n+                $JournalID = $VoucherListEntry['JournalID'];\n+                $VoucherID = $VoucherListEntry['VoucherID'];\n+                if (in_array($VoucherID, $this->Matched)) {\n+                  $Used = true;\n+                }\n+                $HighlightLinkHTML[] = \"<span class=\\\"navigate to\\\" id=\\\"$VoucherID\\\">$JournalID</span>\";\n+              }\n+              // None of the vouchers in the group were used previously\n+              if ($Used === false) {\n+                // Mark all the vouchers in the group as used\n+                foreach ($Matches[$MatchedBy.\"Journals\"][$MatchKey] as $VoucherListEntry) {\n+                  $JournalID = $VoucherListEntry['JournalID'];\n+                  $VoucherID = $VoucherListEntry['VoucherID'];\n+                  $this->Matched[] = $VoucherID;\n+                  $this->VoucherHighlightLinksHTML[$VoucherID] .= \"$MatchedBy ( \" . implode(' ', $HighlightLinkHTML) . \" )\";\n+                  $this->CloseAgainst[$VoucherID] = $Matches[$MatchedBy.\"Journals\"][$MatchKey];\n+                }\n+                // Some of the vouchers were previously used so add a double match info and links\n+              } else {\n+                foreach ($Matches[$MatchedBy.\"Journals\"][$MatchKey] as $VoucherListEntry) {\n+                  $JournalID = $VoucherListEntry['JournalID'];\n+                  $VoucherID = $VoucherListEntry['VoucherID'];\n+                  if (!in_array($VoucherID, $this->Matched)) {\n+                    $this->VoucherHighlightLinksHTML[$VoucherID] .= \"Dobbelmatch$MatchedBy ( \" . implode(' ', $HighlightLinkHTML) . \" )\";\n+                  }\n+                }\n+              }\n+            }\n+          }\n+        }\n+      }\n+    }\n+\n+    /**\n+     * Returns true if the group determined by any of the supplied parameters is closable/matchable\n+     */\n+    function isClosable($AccountPlanID, $KID, $InvoiceID, $MatchNumber = NULL, $Voucher = NULL) {\n+      $Success = false;\n+      $KID = trim($KID);\n+      $InvoiceID = trim($InvoiceID);\n+      $MatchNumber = trim($MatchNumber);\n+\n+      // If the sum for the supplied InvoiceID is set and 0 the group is closable by InvoiceID\n+      if ($InvoiceID && !$Success) {\n+        $Success = isset($this->MatchH[$AccountPlanID]['InvoiceID'][$InvoiceID])\n+          && round($this->MatchH[$AccountPlanID]['InvoiceID'][$InvoiceID], 2) == 0;\n+      }\n+      // If the sum for the supplied KID is set and 0 the group is closable by KID\n+      if ($KID && !$Success) {\n+        $Success = isset($this->MatchH[$AccountPlanID]['KID'][$KID])\n+          && round($this->MatchH[$AccountPlanID]['KID'][$KID], 2) == 0;\n+      }\n+      // If the sum for the supplied MatchNumber is set and 0 the group is closable by MatchNumber\n+      if ($MatchNumber && !$Success) {\n+        $Success = isset($this->MatchH[$AccountPlanID]['MatchNumber'][$MatchNumber])\n+          && round($this->MatchH[$AccountPlanID]['MatchNumber'][$MatchNumber], 2) == 0;\n+      }\n+\n+      // If the Voucher is supplied it is closeable if its matched_by field is any\n+      // of the following values ('invoice', 'kid', 'match')\n+      if (!is_null($Voucher)) {\n+        $Success = in_array($Voucher->matched_by, array('invoice', 'kid', 'match'));\n+      }\n+\n+      return $Success;\n+    }\n+\n+    /**\n+     * Returns a number if the group determined by any of the supplied parameters is closable/matchable\n+     * 1 for InvoiceID\n+     * 2 for KID\n+     * 3 for MatchNumber\n+     */\n+    function isMatchable($AccountPlanID, $KID, $InvoiceID, $MatchNumber, $Voucher) {\n+      $Success = 0;\n+      $KID = trim($KID);\n+      $InvoiceID = trim($InvoiceID);\n+      $MatchNumber = trim($MatchNumber);\n+\n+      // If Voucher's matched_by field is set it will override any automatic match\n+      // to the selected matched_by's return value\n+      if (in_array($Voucher->matched_by, array('invoice', 'kid', 'match'))) {\n+        switch ($Voucher->matched_by) {\n+          case 'invoice':\n+            $Success = 1;\n+            break;\n+          case 'kid':\n+            $Success = 2;\n+            break;\n+          case 'match':\n+            $Success = 3;\n+            break;\n+        }\n+      // Else check if there is a match by any of the types\n+      } else {\n+        // If the sum for the supplied InvoiceID is set and 0 the group is closable by InvoiceID\n+        if ($InvoiceID && !$Success) {\n+          if (isset($this->MatchH[$AccountPlanID]['InvoiceID'][$InvoiceID])\n+            && round($this->MatchH[$AccountPlanID]['InvoiceID'][$InvoiceID], 2) == 0)\n+          {\n+            $Success = 1;\n+          }\n+        }\n+        // If the sum for the supplied KID is set and 0 the group is closable by KID\n+        if ($KID && !$Success) {\n+          if (isset($this->MatchH[$AccountPlanID]['KID'][$KID])\n+            && round($this->MatchH[$AccountPlanID]['KID'][$KID], 2) == 0)\n+          {\n+            $Success = 2;\n+          }\n+        }\n+        // If the sum for the supplied MatchNumber is set and 0 the group is closable by MatchNumber\n+        if ($MatchNumber && !$Success) {\n+          if (isset($this->MatchH[$AccountPlanID]['MatchNumber'][$MatchNumber])\n+            && round($this->MatchH[$AccountPlanID]['MatchNumber'][$MatchNumber], 2) == 0)\n+          {\n+            $Success = 3;\n+          }\n+        }\n+      }\n+\n+      return $Success;\n+    }\n+\n+    /**\n+     * Is the Voucher already matched\n+     */\n+    function isClosableVoucher($VoucherID) {\n+      return in_array($VoucherID, $this->Matched);\n+    }\n+\n+    /**\n+     * Get all highlight links for the VoucherID\n+     */\n+    function voucherMessage($VoucherID) {\n+      return $this->VoucherHighlightLinksHTML[$VoucherID];\n+    }\n+\n+\n+    /**\n+     * Get diff for a group with the supplied paramters\n+     */\n+    function getDiff($AccountPlanID, $KID, $InvoiceID, $MatchNumber = NULL, $Voucher = NULL) {\n+      $KID = trim($KID);\n+      $InvoiceID = trim($InvoiceID);\n+      $MatchNumber = trim($MatchNumber);\n+\n+      // If a Voucher is supplied get the diff for only the selected matched_by option if set,\n+      // if not set just get the amount diff for that voucher\n+      if (!is_null($Voucher)) {\n+        if ($KID && $Voucher->matched_by == 'kid') {\n+          $value = $this->MatchH[$AccountPlanID]['KID'][$KID];\n+        } elseif ($InvoiceID && $Voucher->matched_by == 'invoice') {\n+          $value = $this->MatchH[$AccountPlanID]['InvoiceID'][$InvoiceID];\n+        } elseif ($MatchNumber && $Voucher->matched_by == 'match') {\n+          $value = $this->MatchH[$AccountPlanID]['MatchNumber'][$MatchNumber];\n+        } else {\n+          $value = $Voucher->AmountIn - $Voucher->AmountOut;\n+        }\n+      }\n+\n+      return $value;\n+    }\n+\n+    /**\n+     * Unreconcile the voucher with the supplied VoucherID and all other vouchers it is reconciled against\n+     */\n+    function openPost($VoucherID) {\n+      global $_lib;\n+\n+      if ($VoucherID > 0) {\n+        $query_select_reconciliation_id = \"\n+          SELECT\n+            VoucherReconciliationID\n+          FROM\n+            voucher\n+          WHERE\n+            VoucherID = $VoucherID\";\n+        $result_reconciliation_id = $_lib['db']->db_query($query_select_reconciliation_id);\n+        $row = $_lib['db']->db_fetch_object($result_reconciliation_id);\n+        $reconciliation_id = $row->VoucherReconciliationID;\n+        if ($reconciliation_id) {\n+          // Remove reconciliation links on all vouchers in this reconciliation\n+          $update_Voucher_reconciliation_query = \"\n+            UPDATE\n+              voucher\n+            SET\n+              VoucherReconciliationID = NULL\n+            WHERE\n+              VoucherReconciliationID = $reconciliation_id\";\n+          $_lib['db']->db_update($update_Voucher_reconciliation_query);\n+          // Remove the reconciliation\n+          $query_delete_reconciliation = \"\n+            DELETE FROM\n+              voucherreconciliation\n+            WHERE\n+              ID = $reconciliation_id\";\n+          $_lib['db']->db_delete($query_delete_reconciliation);\n+        }\n+      } else {\n+        // print \"openPost linjenummer ikke angitt\";\n+      }\n+    }\n+\n+    /**\n+     * Unreconcile all vouchers that belong to a journal and the vouchers they are reconciled against\n+     */\n+    function openPostJournal($JournalID, $VoucherType) {\n+      global $_lib;\n+\n+      $VoucherQuery = \"\n+        SELECT\n+          VoucherID\n+        FROM\n+          voucher\n+        WHERE\n+          JournalID = $JournalID AND\n+          VoucherType = '$VoucherType' AND\n+          Active = 1\";\n+      $VoucherResult = $_lib['db']->db_query($VoucherQuery);\n+      while ($Voucher = $_lib['db']->db_fetch_object($VoucherResult)){\n+        $this->openPost($Voucher->VoucherID);\n+      }\n+    }\n+\n+    /**\n+     * Reconcile a voucher only if its group sums up to 0\n+     */\n+    function closePost($MatchAccountPlanID, $KID, $InvoiceID) {\n+      global $_lib;\n+      // Set to anything different from 0 (here we chose 1) since if Balance is 0 the group can be reconciled\n+      $Balance = 1;\n+\n+      if ($KID && $Balance != 0) {\n+        $VoucherQuery = \"\n+          SELECT\n+            *\n+          FROM\n+            voucher\n+          WHERE\n+            AccountPlanID = $MatchAccountPlanID AND\n+            KID = '$KID' AND\n+            VoucherReconciliationID IS NULL AND\n+            Active = 1\n+          ORDER BY\n+            VoucherDate DESC\";\n+        // Order by date to always choose the newest if there is more than one\n+        list($Balance, $AmountInH, $AmountOutH) = $this->closeDataStructure($VoucherQuery);\n+      }\n+\n+      // If balance is 0 we don't need to try to close it based on InvoiceID since it is closed with KID already\n+      if ($InvoiceID && $Balance != 0) {\n+        $VoucherQuery = \"\n+          SELECT\n+            *\n+          FROM\n+            voucher\n+          WHERE\n+            AccountPlanID = $MatchAccountPlanID AND\n+            VoucherReconciliationID IS NULL AND\n+            InvoiceID = '$InvoiceID' AND\n+            Active = 1\n+          ORDER BY\n+            VoucherDate DESC\";\n+        // Order by date to always choose the newest if there is more than one\n+        list($Balance, $AmountInH, $AmountOutH) = $this->closeDataStructure($VoucherQuery);\n+      }\n+\n+      // If no KID or InvoiceID, add error message\n+      if (!$KID && !$InvoiceID) {\n+        $_lib['message']->add('Ingen KID eller fakturanummer');\n+        return false;\n+      }\n+\n+      if ($Balance == 0 &&\n+          (count($AmountInH) + count($AmountOutH)) > 1)\n+      {\n+        if (count($AmountInH) >= count($AmountOutH)) {\n+          foreach ($AmountOutH as $VoucherID1 => $ValueOut) {\n+            foreach ($AmountInH as $VoucherID2 => $ValueIn) {\n+              $this->closePostSQL($VoucherID1, $VoucherID2);\n+            }\n+          }\n+        } elseif (count($AmountInH) < count($AmountOutH)) {\n+          foreach ($AmountInH as $VoucherID1 => $ValueIn) {\n+            foreach ($AmountOutH as $VoucherID2 => $ValueOut)\n+            {\n+              $this->closePostSQL($VoucherID1, $VoucherID2);\n+            }\n+          }\n+        }\n+      }\n+    }\n+\n+    /**\n+     * Reconcile voucher against its matches\n+     */\n+    function closeVoucher($AccountPlanID, $VoucherID) {\n+      // If no info in CloseAgainst, get all unreconciled vouchers\n+      if (count($this->CloseAgainst) == 0) {\n+        $this->getopenpost();\n+      }\n+\n+      foreach ($this->CloseAgainst[$VoucherID] as $VoucherListEntry) {\n+        $VoucherID2 = $VoucherListEntry['VoucherID'];\n+\n+        // Don't close against itself\n+        if ($VoucherID2 == $VoucherID) {\n+          continue;\n+        }\n+\n+        $hash1 = md5($VoucherID . \"#\" . $VoucherID2);\n+        $hash2 = md5($VoucherID2 . \"#\" . $VoucherID);\n+\n+        // Don't reclose/rereconcile vouchers\n+        if (isset($this->ClosedVouchers[$hash1])\n+          || isset($this->ClosedVouchers[$hash2]))\n+        {\n+          continue;\n+        }\n+\n+        // Register as closed/reconciled\n+        $this->ClosedVouchers[$hash1] = true;\n+        $this->ClosedVouchers[$hash2] = true;\n+\n+        $this->closePostSQL($VoucherID, $VoucherID2);\n+        switch ($VoucherListEntry['Match']) {\n+          case 'KID':\n+            $ClosedWith = 'kid';\n+            break;\n+          case 'InvoiceID':\n+            $ClosedWith = 'invoice';\n+            break;\n+          case 'MatchNumber':\n+            $ClosedWith = 'match';\n+            break;\n+          default:\n+            $ClosedWith = '0';\n+            break;\n+        }\n+        $this->updateClosedWith($VoucherID, $VoucherID2, $ClosedWith);\n+      }\n+    }\n+\n+    /**\n+     * Get balance and in and out amounts for vouchers fetched by the supplied query\n+     */\n+    private function closeDataStructure($VoucherQuery) {\n+      global $_lib;\n+\n+      $VoucherResult = $_lib['db']->db_query($VoucherQuery);\n+      $AmountInH  = array();\n+      $AmountOutH = array();\n+      while ($Voucher = $_lib['db']->db_fetch_object($VoucherResult)) {\n+        if ($Voucher->AmountIn != 0) {\n+          $AmountInH[$Voucher->VoucherID]  += $Voucher->AmountIn;\n+        }\n+        if ($Voucher->AmountOut != 0) {\n+          $AmountOutH[$Voucher->VoucherID] += $Voucher->AmountOut;\n+        }\n+        $Balance += ($Voucher->AmountIn - $Voucher->AmountOut);\n+      }\n+\n+      // We have to round the amount because of imprecision in PHP calculations\n+      $Balance = round($Balance, 3);\n+      return array($Balance, $AmountInH, $AmountOutH);\n+    }\n+\n+    /**\n+     * Run SQL to close/reconcile two vouchers\n+     */\n+    private function closePostSQL($VoucherID1, $VoucherID2) {\n+      global $_lib;\n+\n+      $VoucherQuery = \"\n+        SELECT\n+          *\n+        FROM\n+          voucher\n+        WHERE\n+          VoucherID IN ($VoucherID1, $VoucherID2) AND\n+          VoucherReconciliationID IS NOT NULL\";\n+      $VoucherResult = $_lib['storage']->db_query($VoucherQuery);\n+      if ($_lib['storage']->db_numrows($VoucherResult) < 2) {\n+        if ($_lib['storage']->db_numrows($VoucherResult) == 0) {\n+          $ReconciliationInsertQuery = \"\n+            INSERT INTO\n+              voucherreconciliation(CreatedAt, CreatedBy)\n+            VALUES\n+              (NOW(), \" . $_lib['sess']->get_person('PersonID') . \")\";\n+          $ReconciliationID = $_lib['db']->db_insert($ReconciliationInsertQuery);\n+        } else {\n+          $ReconciliationIDQuery = \"\n+            SELECT\n+              VoucherReconciliationID\n+            FROM\n+              voucher\n+            WHERE\n+              VoucherID IN ($VoucherID1, $VoucherID2) AND\n+              VoucherReconciliationID IS NOT NULL\";\n+          $ReconciliationIDRow = $_lib['storage']->get_row(array('query' => $ReconciliationIDQuery));\n+          $ReconciliationID = $ReconciliationIDRow->VoucherReconciliationID;\n+        }\n+        $UpdateVoucherReconciliationIDQuery = \"\n+          UPDATE\n+            voucher\n+          SET\n+            VoucherReconciliationID = $ReconciliationID\n+          WHERE\n+            VoucherID IN ($VoucherID1, $VoucherID2)\";\n+        $_lib['db']->db_update($UpdateVoucherReconciliationIDQuery);\n+      } else {\n+        // $_lib['message']->add(\"Duplikatlukking: $VoucherID1:$VoucherID2\");\n+      }\n+    }\n+\n+    /**\n+     * Unreconciles all vouchers, in all periods for all account plans\n+     */\n+    function openAllPosts() {\n+      global $_lib;\n+\n+      $DeleteReconciliationsQuery = \"DELETE voucherreconciliation\";\n+      $_lib['db']->db_delete($DeleteReconciliationsQuery);\n+      $UpdateVouchersQuery = \"\n+        UPDATE\n+          voucher\n+        SET\n+          v.VoucherReconciliationID = NULL\n+        WHERE\n+          v.Active = 1\";\n+      $_lib['db']->db_update($UpdateVouchersQuery);\n+    }\n+\n+    /**\n+     * Unreconcile all vouchers that are in open peroids.\n+     */\n+    public function openAllPostsForOpenPeriods() {\n+      global $_lib;\n+\n+      $SelectReconciliationsQuery = \"\n+        SELECT\n+          v.VoucherReconciliationID\n+        FROM\n+          voucher v\n+          JOIN (\n+            SELECT\n+              Period\n+            FROM\n+              accountperiod\n+            WHERE\n+            Status < 4\n+          ) ap\n+          ON ap.Period = v.VoucherPeriod\n+        WHERE Active = 1\";\n+        $SelectReconciliationsResult = $_lib['db']->db_query($SelectReconciliationsQuery);\n+        $IDs = array();\n+        while($Voucher = $_lib['db']->db_fetch_assoc($SelectReconciliationsResult)) {\n+          $ID = $Voucher['VoucherReconciliationID'];\n+          if ($ID) array_push($IDs, $ID);\n+        }\n+        if (!empty($IDs)) {\n+          $IDsString = '(' . implode(', ', $IDs) . ')';\n+          $DeleteReconciliationsQuery = \"\n+            DELETE\n+            FROM\n+              voucherreconciliation\n+            WHERE\n+              ID IN $IDsString\";\n+          $_lib['db']->db_delete($DeleteReconciliationsQuery);\n+          $UpdateVouchersQuery = \"\n+            UPDATE\n+              voucher\n+            SET\n+              VoucherReconciliationID = NULL\n+            WHERE\n+              VoucherReconciliationID IN $IDsString\";\n+          $_lib['db']->db_update($UpdateVouchersQuery);\n+        }\n+    }\n+\n+    /**\n+     * Unreconcile all vouchers for a specific account plan.\n+     */\n+    public function openAllPostsAccount($AccountPlanID) {\n+      global $_lib;\n+\n+      $DeleteReconciliationsQuery = \"\n+        DELETE vr\n+        FROM\n+          voucherreconciliation vr\n+          INNER JOIN\n+          voucher v\n+          ON vr.ID = v.VoucherReconciliationID\n+        WHERE\n+          v.Active = 1 AND\n+          v.AccountPlanID = $AccountPlanID\";\n+      $_lib['db']->db_delete($DeleteReconciliationsQuery);\n+      $UpdateVouchersQuery = \"\n+        UPDATE\n+          voucher v\n+        SET\n+          v.VoucherReconciliationID = NULL\n+        WHERE\n+          v.Active = 1 AND\n+          v.AccountPlanID = $AccountPlanID\";\n+      $_lib['db']->db_update($UpdateVouchersQuery);\n+    }\n+\n+    /**\n+     * Reconcile all vouchers for a spacific account plan\n+     */\n+    public function closeAllPostsAccount($AccountPlanID) {\n+      global $_lib;\n+\n+      $this->getopenpost($AccountPlanID);\n+      $ClosableH = array();\n+\n+      $Account = $this->VoucherH[$AccountPlanID];\n+\n+      if (count($Account) > 0){\n+        foreach ($Account as $Voucher) {\n+          if ($this->isClosableVoucher($Voucher->VoucherID)) {\n+            $Close = new stdClass();\n+            $Close->matchAccountPlanID = $Voucher->AccountPlanID;\n+            $Close->matchKid = $Voucher->KID;\n+            $Close->matchInvoiceID = $Voucher->InvoiceID;\n+            $Close->VoucherID = $Voucher->VoucherID;\n+            $Close->AccountPlanID = $this->AccountPlanID;\n+            $ClosableH[] = $Close;\n+          }\n+        }\n+      }\n+\n+      $ClosableCount = count($ClosableH);\n+      $_lib['message']->add(\"Lukker $ClosableCount bilag p&aring; $AccountPlanID som g&aring;r i null\");\n+      if ($ClosableCount) {\n+        foreach ($ClosableH as $Close) {\n+          $this->closeVoucher($Close->matchAccountPlanID, $Close->VoucherID);\n+        }\n+      }\n+    }\n+\n+    /**\n+     * Get all the main bookkeeping acounts for a specified account plan\n+     */\n+    function findMotKonto($AccountPlanID) {\n+      global $_lib;\n+\n+      $MotkontosQuery = \"\n+        SELECT\n+          MotkontoResultat1,\n+          MotkontoResultat2,\n+          MotkontoResultat3,\n+          MotkontoBalanse1,\n+          MotkontoBalanse2,\n+          MotkontoBalanse3\n+        FROM\n+          accountplan\n+        WHERE\n+          AccountPlanID = $AccountPlanID\";\n+      $MotkontosResult = $_lib['db']->db_query($MotkontosQuery);\n+      return $_lib['db']->db_fetch_assoc($MotkontosResult);\n+    }\n+\n+    /**\n+     * Reconcile all vouchers\n+     */\n+    public function closeAllPosts() {\n+      global $_lib;\n+\n+      $this->getopenpost();","path":"modules/reconciliation/model/reconciliation.class.php","position":1075,"original_position":1075,"commit_id":"4fc64cebb8937b9bb785e2b68b85dbef3461a43c","original_commit_id":"4fc64cebb8937b9bb785e2b68b85dbef3461a43c","user":{"login":"mladjo2505","id":3684429,"avatar_url":"https://avatars1.githubusercontent.com/u/3684429?v=3","gravatar_id":"","url":"https://api.github.com/users/mladjo2505","html_url":"https://github.com/mladjo2505","followers_url":"https://api.github.com/users/mladjo2505/followers","following_url":"https://api.github.com/users/mladjo2505/following{/other_user}","gists_url":"https://api.github.com/users/mladjo2505/gists{/gist_id}","starred_url":"https://api.github.com/users/mladjo2505/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mladjo2505/subscriptions","organizations_url":"https://api.github.com/users/mladjo2505/orgs","repos_url":"https://api.github.com/users/mladjo2505/repos","events_url":"https://api.github.com/users/mladjo2505/events{/privacy}","received_events_url":"https://api.github.com/users/mladjo2505/received_events","type":"User","site_admin":false},"body":"done","created_at":"2017-04-25T10:55:59Z","updated_at":"2017-04-25T10:55:59Z","html_url":"https://github.com/dodoas/lodo/pull/439#discussion_r113163814","pull_request_url":"https://api.github.com/repos/dodoas/lodo/pulls/439","_links":{"self":{"href":"https://api.github.com/repos/dodoas/lodo/pulls/comments/113163814"},"html":{"href":"https://github.com/dodoas/lodo/pull/439#discussion_r113163814"},"pull_request":{"href":"https://api.github.com/repos/dodoas/lodo/pulls/439"}}},"pull_request":{"url":"https://api.github.com/repos/dodoas/lodo/pulls/439","id":109685369,"html_url":"https://github.com/dodoas/lodo/pull/439","diff_url":"https://github.com/dodoas/lodo/pull/439.diff","patch_url":"https://github.com/dodoas/lodo/pull/439.patch","issue_url":"https://api.github.com/repos/dodoas/lodo/issues/439","number":439,"state":"open","locked":false,"title":"New reconciliation logic","user":{"login":"mladjo2505","id":3684429,"avatar_url":"https://avatars1.githubusercontent.com/u/3684429?v=3","gravatar_id":"","url":"https://api.github.com/users/mladjo2505","html_url":"https://github.com/mladjo2505","followers_url":"https://api.github.com/users/mladjo2505/followers","following_url":"https://api.github.com/users/mladjo2505/following{/other_user}","gists_url":"https://api.github.com/users/mladjo2505/gists{/gist_id}","starred_url":"https://api.github.com/users/mladjo2505/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mladjo2505/subscriptions","organizations_url":"https://api.github.com/users/mladjo2505/orgs","repos_url":"https://api.github.com/users/mladjo2505/repos","events_url":"https://api.github.com/users/mladjo2505/events{/privacy}","received_events_url":"https://api.github.com/users/mladjo2505/received_events","type":"User","site_admin":false},"body":"Closes #342 \r\n\r\nImplement new and faster reconciliation logic to replace the postmotpost one.\r\n\r\nIncludes some fixes for session class so we do not get so many warning messages when executing any batch task.\r\nAlso implements the task class which can be used to create a task or a migration that is much easier to do using not only plain SQL code but php code as well.\r\n\r\nTo migrate data from postmotpost logic tables to new reconciliation tables just run the migration `'167_migrate_postmotpost_create_reconciliation_tables.sql'` and then run the task for migrating the data with:\r\n\r\n`php batch.php model_task_task.postmotpost_migrate`\r\n\r\n**NOTE!**\r\nThe default.inc file needs a new line for:\r\n`$_SETUP['ACTIVE_INTERFACE'] = \"lodo\";`\r\nThis still needs to be updated after successful testing:\r\n - Remove the old tables and classes\r\n\r\nThis change will require extensive testing since it introduces changes to many parts of the application to do with postmotpost matching/closing and opening.\r\n\r\n**The speedup benchmark numbers:**\r\n_A sufficiently big database used to benchmark on_\r\n\r\n**Postmotpost logic:**\r\n\r\n| # | initial open(s) | show all(s) | open all(s) | close all(s) |\r\n| --- | --- | --- | --- | --- |\r\n| 1st | 3.35 | 2.40 | 22.94 | 17.70 |\r\n| 2nd | 3.47 | 2.43 | 21.14 | 16.28 |\r\n| 3rd | 3.29 | 2.38 | 21.07 | 16.41 |\r\n| --- | --- | --- | --- | --- |\r\n| **avg** | **3.37** | **2.40** | **21.72** | **16.80** |\r\n\r\n**Reconciliation logic:**\r\n\r\n| # | initial open(s) | show all(s) | open all(s) | close all(s) |\r\n| --- | --- | --- | --- | --- |\r\n| 1st | 1.13 | 0.91 | 9.57 | 3.97 |\r\n| 2nd | 1.14 | 0.94 | 9.86 | 5.22 |\r\n| 3rd | 1.17 | 0.99 | 9.32 | 4.21 |\r\n| --- | --- | --- | --- | --- |\r\n| **avg** | **1.15** | **0.95** | **9.58** | **4.47** |\r\n\r\n**Speedup factor**\r\n\r\n| | initial open | show all | open all | close all |\r\n| --- | --- | --- | --- | --- |\r\n| **times faster** | **2.93** | **2.53** | **2.27** | **3.76** |\r\n","created_at":"2017-03-08T12:25:13Z","updated_at":"2017-04-25T10:55:59Z","closed_at":null,"merged_at":null,"merge_commit_sha":"16c75a00ff1d3f8ad2cbe82c888d06a3673b213f","assignee":{"login":"mladjo2505","id":3684429,"avatar_url":"https://avatars1.githubusercontent.com/u/3684429?v=3","gravatar_id":"","url":"https://api.github.com/users/mladjo2505","html_url":"https://github.com/mladjo2505","followers_url":"https://api.github.com/users/mladjo2505/followers","following_url":"https://api.github.com/users/mladjo2505/following{/other_user}","gists_url":"https://api.github.com/users/mladjo2505/gists{/gist_id}","starred_url":"https://api.github.com/users/mladjo2505/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mladjo2505/subscriptions","organizations_url":"https://api.github.com/users/mladjo2505/orgs","repos_url":"https://api.github.com/users/mladjo2505/repos","events_url":"https://api.github.com/users/mladjo2505/events{/privacy}","received_events_url":"https://api.github.com/users/mladjo2505/received_events","type":"User","site_admin":false},"assignees":[{"login":"mladjo2505","id":3684429,"avatar_url":"https://avatars1.githubusercontent.com/u/3684429?v=3","gravatar_id":"","url":"https://api.github.com/users/mladjo2505","html_url":"https://github.com/mladjo2505","followers_url":"https://api.github.com/users/mladjo2505/followers","following_url":"https://api.github.com/users/mladjo2505/following{/other_user}","gists_url":"https://api.github.com/users/mladjo2505/gists{/gist_id}","starred_url":"https://api.github.com/users/mladjo2505/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mladjo2505/subscriptions","organizations_url":"https://api.github.com/users/mladjo2505/orgs","repos_url":"https://api.github.com/users/mladjo2505/repos","events_url":"https://api.github.com/users/mladjo2505/events{/privacy}","received_events_url":"https://api.github.com/users/mladjo2505/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/dodoas/lodo/pulls/439/commits","review_comments_url":"https://api.github.com/repos/dodoas/lodo/pulls/439/comments","review_comment_url":"https://api.github.com/repos/dodoas/lodo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dodoas/lodo/issues/439/comments","statuses_url":"https://api.github.com/repos/dodoas/lodo/statuses/4fc64cebb8937b9bb785e2b68b85dbef3461a43c","head":{"label":"dodoas:new_reconciliation","ref":"new_reconciliation","sha":"4fc64cebb8937b9bb785e2b68b85dbef3461a43c","user":{"login":"dodoas","id":2697200,"avatar_url":"https://avatars1.githubusercontent.com/u/2697200?v=3","gravatar_id":"","url":"https://api.github.com/users/dodoas","html_url":"https://github.com/dodoas","followers_url":"https://api.github.com/users/dodoas/followers","following_url":"https://api.github.com/users/dodoas/following{/other_user}","gists_url":"https://api.github.com/users/dodoas/gists{/gist_id}","starred_url":"https://api.github.com/users/dodoas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dodoas/subscriptions","organizations_url":"https://api.github.com/users/dodoas/orgs","repos_url":"https://api.github.com/users/dodoas/repos","events_url":"https://api.github.com/users/dodoas/events{/privacy}","received_events_url":"https://api.github.com/users/dodoas/received_events","type":"User","site_admin":false},"repo":{"id":7601637,"name":"lodo","full_name":"dodoas/lodo","owner":{"login":"dodoas","id":2697200,"avatar_url":"https://avatars1.githubusercontent.com/u/2697200?v=3","gravatar_id":"","url":"https://api.github.com/users/dodoas","html_url":"https://github.com/dodoas","followers_url":"https://api.github.com/users/dodoas/followers","following_url":"https://api.github.com/users/dodoas/following{/other_user}","gists_url":"https://api.github.com/users/dodoas/gists{/gist_id}","starred_url":"https://api.github.com/users/dodoas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dodoas/subscriptions","organizations_url":"https://api.github.com/users/dodoas/orgs","repos_url":"https://api.github.com/users/dodoas/repos","events_url":"https://api.github.com/users/dodoas/events{/privacy}","received_events_url":"https://api.github.com/users/dodoas/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dodoas/lodo","description":"Lodo Regnskap","fork":false,"url":"https://api.github.com/repos/dodoas/lodo","forks_url":"https://api.github.com/repos/dodoas/lodo/forks","keys_url":"https://api.github.com/repos/dodoas/lodo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dodoas/lodo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dodoas/lodo/teams","hooks_url":"https://api.github.com/repos/dodoas/lodo/hooks","issue_events_url":"https://api.github.com/repos/dodoas/lodo/issues/events{/number}","events_url":"https://api.github.com/repos/dodoas/lodo/events","assignees_url":"https://api.github.com/repos/dodoas/lodo/assignees{/user}","branches_url":"https://api.github.com/repos/dodoas/lodo/branches{/branch}","tags_url":"https://api.github.com/repos/dodoas/lodo/tags","blobs_url":"https://api.github.com/repos/dodoas/lodo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dodoas/lodo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dodoas/lodo/git/refs{/sha}","trees_url":"https://api.github.com/repos/dodoas/lodo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dodoas/lodo/statuses/{sha}","languages_url":"https://api.github.com/repos/dodoas/lodo/languages","stargazers_url":"https://api.github.com/repos/dodoas/lodo/stargazers","contributors_url":"https://api.github.com/repos/dodoas/lodo/contributors","subscribers_url":"https://api.github.com/repos/dodoas/lodo/subscribers","subscription_url":"https://api.github.com/repos/dodoas/lodo/subscription","commits_url":"https://api.github.com/repos/dodoas/lodo/commits{/sha}","git_commits_url":"https://api.github.com/repos/dodoas/lodo/git/commits{/sha}","comments_url":"https://api.github.com/repos/dodoas/lodo/comments{/number}","issue_comment_url":"https://api.github.com/repos/dodoas/lodo/issues/comments{/number}","contents_url":"https://api.github.com/repos/dodoas/lodo/contents/{+path}","compare_url":"https://api.github.com/repos/dodoas/lodo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dodoas/lodo/merges","archive_url":"https://api.github.com/repos/dodoas/lodo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dodoas/lodo/downloads","issues_url":"https://api.github.com/repos/dodoas/lodo/issues{/number}","pulls_url":"https://api.github.com/repos/dodoas/lodo/pulls{/number}","milestones_url":"https://api.github.com/repos/dodoas/lodo/milestones{/number}","notifications_url":"https://api.github.com/repos/dodoas/lodo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dodoas/lodo/labels{/name}","releases_url":"https://api.github.com/repos/dodoas/lodo/releases{/id}","deployments_url":"https://api.github.com/repos/dodoas/lodo/deployments","created_at":"2013-01-14T09:31:19Z","updated_at":"2016-01-23T20:36:17Z","pushed_at":"2017-04-25T10:19:02Z","git_url":"git://github.com/dodoas/lodo.git","ssh_url":"git@github.com:dodoas/lodo.git","clone_url":"https://github.com/dodoas/lodo.git","svn_url":"https://github.com/dodoas/lodo","homepage":"","size":10276,"stargazers_count":3,"watchers_count":3,"language":"PHP","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":16,"forks":1,"open_issues":16,"watchers":3,"default_branch":"master"}},"base":{"label":"dodoas:master","ref":"master","sha":"f2ecf0b4a5fa179b0e792e1c90b738a4fcfb9545","user":{"login":"dodoas","id":2697200,"avatar_url":"https://avatars1.githubusercontent.com/u/2697200?v=3","gravatar_id":"","url":"https://api.github.com/users/dodoas","html_url":"https://github.com/dodoas","followers_url":"https://api.github.com/users/dodoas/followers","following_url":"https://api.github.com/users/dodoas/following{/other_user}","gists_url":"https://api.github.com/users/dodoas/gists{/gist_id}","starred_url":"https://api.github.com/users/dodoas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dodoas/subscriptions","organizations_url":"https://api.github.com/users/dodoas/orgs","repos_url":"https://api.github.com/users/dodoas/repos","events_url":"https://api.github.com/users/dodoas/events{/privacy}","received_events_url":"https://api.github.com/users/dodoas/received_events","type":"User","site_admin":false},"repo":{"id":7601637,"name":"lodo","full_name":"dodoas/lodo","owner":{"login":"dodoas","id":2697200,"avatar_url":"https://avatars1.githubusercontent.com/u/2697200?v=3","gravatar_id":"","url":"https://api.github.com/users/dodoas","html_url":"https://github.com/dodoas","followers_url":"https://api.github.com/users/dodoas/followers","following_url":"https://api.github.com/users/dodoas/following{/other_user}","gists_url":"https://api.github.com/users/dodoas/gists{/gist_id}","starred_url":"https://api.github.com/users/dodoas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dodoas/subscriptions","organizations_url":"https://api.github.com/users/dodoas/orgs","repos_url":"https://api.github.com/users/dodoas/repos","events_url":"https://api.github.com/users/dodoas/events{/privacy}","received_events_url":"https://api.github.com/users/dodoas/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dodoas/lodo","description":"Lodo Regnskap","fork":false,"url":"https://api.github.com/repos/dodoas/lodo","forks_url":"https://api.github.com/repos/dodoas/lodo/forks","keys_url":"https://api.github.com/repos/dodoas/lodo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dodoas/lodo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dodoas/lodo/teams","hooks_url":"https://api.github.com/repos/dodoas/lodo/hooks","issue_events_url":"https://api.github.com/repos/dodoas/lodo/issues/events{/number}","events_url":"https://api.github.com/repos/dodoas/lodo/events","assignees_url":"https://api.github.com/repos/dodoas/lodo/assignees{/user}","branches_url":"https://api.github.com/repos/dodoas/lodo/branches{/branch}","tags_url":"https://api.github.com/repos/dodoas/lodo/tags","blobs_url":"https://api.github.com/repos/dodoas/lodo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dodoas/lodo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dodoas/lodo/git/refs{/sha}","trees_url":"https://api.github.com/repos/dodoas/lodo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dodoas/lodo/statuses/{sha}","languages_url":"https://api.github.com/repos/dodoas/lodo/languages","stargazers_url":"https://api.github.com/repos/dodoas/lodo/stargazers","contributors_url":"https://api.github.com/repos/dodoas/lodo/contributors","subscribers_url":"https://api.github.com/repos/dodoas/lodo/subscribers","subscription_url":"https://api.github.com/repos/dodoas/lodo/subscription","commits_url":"https://api.github.com/repos/dodoas/lodo/commits{/sha}","git_commits_url":"https://api.github.com/repos/dodoas/lodo/git/commits{/sha}","comments_url":"https://api.github.com/repos/dodoas/lodo/comments{/number}","issue_comment_url":"https://api.github.com/repos/dodoas/lodo/issues/comments{/number}","contents_url":"https://api.github.com/repos/dodoas/lodo/contents/{+path}","compare_url":"https://api.github.com/repos/dodoas/lodo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dodoas/lodo/merges","archive_url":"https://api.github.com/repos/dodoas/lodo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dodoas/lodo/downloads","issues_url":"https://api.github.com/repos/dodoas/lodo/issues{/number}","pulls_url":"https://api.github.com/repos/dodoas/lodo/pulls{/number}","milestones_url":"https://api.github.com/repos/dodoas/lodo/milestones{/number}","notifications_url":"https://api.github.com/repos/dodoas/lodo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dodoas/lodo/labels{/name}","releases_url":"https://api.github.com/repos/dodoas/lodo/releases{/id}","deployments_url":"https://api.github.com/repos/dodoas/lodo/deployments","created_at":"2013-01-14T09:31:19Z","updated_at":"2016-01-23T20:36:17Z","pushed_at":"2017-04-25T10:19:02Z","git_url":"git://github.com/dodoas/lodo.git","ssh_url":"git@github.com:dodoas/lodo.git","clone_url":"https://github.com/dodoas/lodo.git","svn_url":"https://github.com/dodoas/lodo","homepage":"","size":10276,"stargazers_count":3,"watchers_count":3,"language":"PHP","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":16,"forks":1,"open_issues":16,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dodoas/lodo/pulls/439"},"html":{"href":"https://github.com/dodoas/lodo/pull/439"},"issue":{"href":"https://api.github.com/repos/dodoas/lodo/issues/439"},"comments":{"href":"https://api.github.com/repos/dodoas/lodo/issues/439/comments"},"review_comments":{"href":"https://api.github.com/repos/dodoas/lodo/pulls/439/comments"},"review_comment":{"href":"https://api.github.com/repos/dodoas/lodo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dodoas/lodo/pulls/439/commits"},"statuses":{"href":"https://api.github.com/repos/dodoas/lodo/statuses/4fc64cebb8937b9bb785e2b68b85dbef3461a43c"}}}}
{"actor":{"display_login":"mladjo2505"}}