inclusief mislukt en chargeback

This commit is contained in:
ErikHommel 2022-02-07 11:07:30 +01:00
parent 1e68031968
commit b38cfe13c5
10 changed files with 333 additions and 31 deletions

View File

@ -20,8 +20,12 @@ class CRM_Bij1migratie_Bij1MigratieService {
protected $_vrouwGeslacht = NULL;
protected $_contributieFinancialTypeId = NULL;
protected $_donatieFinancialTypeId = NULL;
protected $_chargeBackContributionStatusId = NULL;
protected $_completedContributionStatusId = NULL;
protected $_pendingContributionStatusId = NULL;
protected $_failedContributionStatusId = NULL;
protected $_cancelledContributionStatusId = NULL;
protected $_overdueContributionStatusId = NULL;
protected $_migratieGroupId = NULL;
/**
@ -155,6 +159,34 @@ class CRM_Bij1migratie_Bij1MigratieService {
return $this->_donatieFinancialTypeId;
}
/**
* @param int $id
*/
public function setCancelledContributionStatusId(int $id) {
$this->_cancelledContributionStatusId = $id;
}
/**
* @return null
*/
public function getCancelledContributionStatusId() {
return $this->_cancelledContributionStatusId;
}
/**
* @param int $id
*/
public function setChargeBackContributionStatusId(int $id) {
$this->_chargeBackContributionStatusId = $id;
}
/**
* @return null
*/
public function getChargeBackContributionStatusId() {
return $this->_chargeBackContributionStatusId;
}
/**
* @param int $id
*/
@ -169,6 +201,34 @@ class CRM_Bij1migratie_Bij1MigratieService {
return $this->_completedContributionStatusId;
}
/**
* @param int $id
*/
public function setFailedContributionStatusId(int $id) {
$this->_failedContributionStatusId = $id;
}
/**
* @return null
*/
public function getFailedContributionStatusId() {
return $this->_failedContributionStatusId;
}
/**
* @param int $id
*/
public function setOverdueContributionStatusId(int $id) {
$this->_overdueContributionStatusId = $id;
}
/**
* @return null
*/
public function getOverdueContributionStatusId() {
return $this->_overdueContributionStatusId;
}
/**
* @param int $id
*/
@ -244,6 +304,9 @@ class CRM_Bij1migratie_Bij1MigratieService {
case "B":
$type = "betalingen";
break;
case "C":
$type = "chargebacks";
break;
default:
$type = "contacten";
break;
@ -390,4 +453,28 @@ class CRM_Bij1migratie_Bij1MigratieService {
return $this->_contributieFinancialTypeId;
}
/**
* Method om bijdrage status te bepalen aan de hand van de status in Mollie
*
* @param string $mollieStatus
* @return null
*/
public function haalBijdrageStatusId(string $mollieStatus) {
switch (strtolower($mollieStatus)) {
case "expired":
$civiStatus = $this->getOverdueContributionStatusId();
break;
case "failed":
$civiStatus = $this->getFailedContributionStatusId();
break;
case "pending":
$civiStatus = $this->getPendingContributionStatusId();
break;
default:
$civiStatus = $this->getCompletedContributionStatusId();
break;
}
return $civiStatus;
}
}

View File

@ -0,0 +1,82 @@
<?php
use CRM_Bij1migratie_ExtensionUtil as E;
/**
* Class voor BIJ1 migratie chargebacks
*
* @author Erik Hommel (CiviCooP) <erik.hommel@civicoop.org>
* @date 7 feb 2022
* @license AGPL-3.0
*/
class CRM_Bij1migratie_ChargeBack {
private $_chargeBackStatusId;
/**
* CRM_Bij1migratie_ChargeBack constructor
*/
public function __construct() {
$this->_chargeBackStatusId = Civi::service('bij1Migratie')->getChargeBackContributionStatusId();
}
/**
* Haal contributionId met transaction Id
*
* @param string $transactionId
* @return false|int
*/
public function haalContributionId(string $transactionId) {
if (!empty($transactionId)) {
try {
$contributions = \Civi\Api4\Contribution::get()
->addSelect('id')
->addWhere('trxn_id', '=', $transactionId)
->execute();
$contribution = $contributions->first();
if ($contribution['id']) {
return (int) $contribution['id'];
}
}
catch (API_Exception $ex) {
}
}
return FALSE;
}
/**
* Verwerk chargeback
*
* @param int $contributionId
* @param array $data
* @return void
* @throws API_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*/
public function verwerk(int $contributionId, array $data) {
$chargeBack = \Civi\Api4\Contribution::update()
->addValue('contribution_status_id',$this->_chargeBackStatusId)
->addValue('trxn_id', $data['transactie_id'] . "/" . $data['chargeback_id'])
->addWhere('id', '=', $contributionId);
if (!empty($data['chargeback_datum'])) {
$chargeBackDatum = new DateTime($data['chargeback_datum']);
}
else {
$chargeBackDatum = new DateTime('now');
}
$reden = NULL;
if (!empty($data['reden_code'])) {
$reden = $data['reden_code'];
}
if (!empty($data['reden_omschrijving'])) {
if ($reden) {
$reden .= " - " . $data['reden_omschrijving'];
}
else {
$reden = $data['reden_omschrijving'];
}
}
if ($reden) {
$chargeBack->addValue('cancel_reason', $reden);
}
$chargeBack->addValue('cancel_date', $chargeBackDatum->format("Y-m-d H:i:s"));
$chargeBack->execute();
}
}

View File

@ -196,4 +196,28 @@ class CRM_Bij1migratie_Contact {
return FALSE;
}
/**
* Method om te bekijken of 3 betalingen mislukt
*
* @param int $contactId
* @param DateTime $vanDatum
* @param DateTime $totDatum
* @return bool
*/
public function heeftMislukt(int $contactId, DateTime $vanDatum, DateTime $totDatum) {
if (!empty($contactId)) {
$query = "SELECT COUNT(*) FROM civicrm_contribution WHERE contact_id = %1 AND contribution_status_id = %2 AND receive_date BETWEEN %3 AND %4";
$aantalMislukt = CRM_Core_DAO::singleValueQuery($query, [
1 => [$contactId, "Integer"],
2 => [Civi::service('bij1Migratie')->getFailedContributionStatusId(), "Integer"],
3 => [$vanDatum->format("Y-m-d"), "String"],
4 => [$totDatum->format("Y-m-d"), "String"],
]);
if ($aantalMislukt >= 3) {
return TRUE;
}
}
return FALSE;
}
}

View File

@ -124,7 +124,10 @@ class CRM_Bij1migratie_CsvFile {
$this->sanitizeAirtable($name, $data);
break;
case "B":
$this->sanitizeBetalingen($name, $key, $data);
$this->sanitizeBetalingen($name, $data);
break;
case "C":
$this->sanitizeChargebacks($name, $data);
break;
case "P":
$this->sanitizePersonen($name, $data);
@ -213,7 +216,7 @@ class CRM_Bij1migratie_CsvFile {
* @param array $data
* @throws
*/
private function sanitizeBetalingen(string $name, string $key, array &$data) {
private function sanitizeBetalingen(string $name, array &$data) {
switch ($name) {
case "transactie_id":
$data[$name] = $data[0];
@ -272,6 +275,46 @@ class CRM_Bij1migratie_CsvFile {
}
}
/**
* Method om data voor chargebacks samen te stellen
*
* @param string $name
* @param string $key
* @param array $data
* @throws
*/
private function sanitizeChargebacks(string $name, array &$data) {
switch ($name) {
case "chargeback_id":
$data[$name] = $data[0];
break;
case "chargeback_datum":
if (!empty($data[1])) {
$donatieDatum = new DateTime($data[1]);
$data[$name] = $donatieDatum->format('d-m-Y');
}
break;
case "transactie_id":
$data[$name] = $data[2];
break;
case "reden_code":
$data[$name] = $data[3];
break;
case "reden_omschrijving":
$data[$name] = $data[4];
break;
case "bedrag":
$data[$name] = $data[5];
break;
case "mollie_customer_id":
$data[$name] = $data[7];
break;
case "chargeback_bedrag":
$data[$name] = abs($data[6]);
break;
}
}
/**
* Method om data voor personen samen te stellen
*

View File

@ -20,7 +20,7 @@ class CRM_Bij1migratie_Form_CsvSelect extends CRM_Core_Form {
$this->addRule('csv_file', E::ts('Er moet een geldig bestand geladen worden.'), 'uploadedfile');
$this->add('advcheckbox', 'first_row_headers', E::ts('Bevat de eerste rij kolomkoppen?'));
$separatorList = Civi::service('bij1Migratie')->getSeparatorList();
$this->add('select', 'migratie_type', E::ts('Soort migratie'), ['A' => 'Airtable', 'P' => 'Mollie klanten', 'B' => 'Betalingen'] , TRUE);
$this->add('select', 'migratie_type', E::ts('Soort migratie'), ['A' => 'Airtable', 'P' => 'Mollie klanten', 'B' => 'Betalingen', 'C' => 'ChargeBacks'] , TRUE);
$this->add('select', 'separator_id', E::ts('Scheidingsteken voor velden'), $separatorList , TRUE);
$this->addButtons([
['type' => 'next', 'name' => E::ts('Next'), 'isDefault' => TRUE],

View File

@ -5,11 +5,13 @@ use CRM_Bij1migratie_ExtensionUtil as E;
* Verwerking van migratie
*/
class CRM_Bij1migratie_Migratie {
/**
* Method om data te migreren
*
* @param string $migratieType
* @param string $tableName
* @throws
*/
public static function migreer(string $migratieType, string $tableName) {
set_time_limit(0);
@ -26,6 +28,9 @@ class CRM_Bij1migratie_Migratie {
case "B":
self::migreerBetaling($migratieType, $migratieDatum, $rij);
break;
case "C":
self::migreerChargebacks($migratieType, $migratieDatum, $rij);
break;
case "P":
self::migreerContact($migratieType, $migratieDatum, $rij);
break;
@ -35,6 +40,47 @@ class CRM_Bij1migratie_Migratie {
}
}
/**
* Migreer chargebacks
*
* @param string $migratieType
* @param DateTime $migratieDatum
* @param array $rij
* @return void
* @throws API_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*/
private static function migreerChargebacks(string $migratieType, DateTime $migratieDatum, array $rij) {
// eerst checken of volledige betaling teruggevorderd is, anders fout
$restant = $rij['bedrag'] - $rij['chargeback_bedrag'];
if ($restant == 0) {
$chargeBack = new CRM_Bij1migratie_ChargeBack();
// bijdrage vinden met transactie ID
$contributionId = $chargeBack->haalContributionId($rij['transactie_id']);
if ($contributionId) {
$chargeBack->verwerk($contributionId, $rij);
}
else {
\Civi\Api4\MigratieLog::create()
->addValue('mollie_customer_id', $rij['mollie_customer_id'])
->addValue('migratiedatum', $migratieDatum->format("y-m-d"))
->addValue('type_migratie', $migratieType)
->addValue('type_melding', 'fout')
->addValue('melding', 'FOUT, geen bijdrage gevonden met transaction ID, verwerk handmatig: ' . json_encode($rij))
->execute();
}
}
else {
\Civi\Api4\MigratieLog::create()
->addValue('mollie_customer_id', $rij['mollie_customer_id'])
->addValue('migratiedatum', $migratieDatum->format("y-m-d"))
->addValue('type_migratie', $migratieType)
->addValue('type_melding', 'fout')
->addValue('melding', 'FOUT, chargeback is niet het volledige bedrag van de oorspronkelijke betaling, verwerk handmatig: ' . json_encode($rij))
->execute();
}
}
/**
* Method om betalingen te migreren
*
@ -47,8 +93,9 @@ class CRM_Bij1migratie_Migratie {
private static function migreerBetaling(string $migratieType, DateTime $migratieDatum, array $rij) {
$mollieCustomerId = NULL;
$email = NULL;
// alleen als paid of pending
if ($rij['status'] == "paid" || $rij['status'] == "pending") {
$geldig = ["expired", "failed", "paid", "pending"];
// alleen als status geldig
if (in_array($rij['status'], $geldig)) {
if (isset($rij['mollie_customer_id']) && !empty($rij['mollie_customer_id'])) {
$mollieCustomerId = trim($rij['mollie_customer_id']);
}
@ -62,11 +109,7 @@ class CRM_Bij1migratie_Migratie {
$contactId = $contact->haalContactIdMetEmail($rij['email']);
}
if ($contactId) {
if ($rij['status'] == "paid") {
$rij['status'] = Civi::service('bij1Migratie')->getCompletedContributionStatusId();
} else {
$rij['status'] = Civi::service('bij1Migratie')->getPendingContributionStatusId();
}
$rij['status'] = Civi::service('bij1Migratie')->haalBijdrageStatusId($rij['status']);
$rij['financial_type_id'] = Civi::service('bij1Migratie')->haalFinancieelType($rij);
if (!self::bestaatBetaling($contactId, $rij)) {
try {

View File

@ -22,7 +22,7 @@ class Bij1MigratieContainer implements CompilerPassInterface {
$this->setAanhef($definition);
$this->setGeslacht($definition);
$this->setFinancieelTypes($definition);
$this->setContributionStatus($definition);
$this->setContributionStatusIds($definition);
$definition->setPublic(TRUE);
$container->setDefinition('bij1Migratie', $definition);
}
@ -32,21 +32,29 @@ class Bij1MigratieContainer implements CompilerPassInterface {
*
* @param $definition
*/
private function setContributionStatus(&$definition) {
private function setContributionStatusIds(&$definition) {
$query = "SELECT cov.value, cov.name
FROM civicrm_option_group AS cog JOIN civicrm_option_value AS cov ON cog.id = cov.option_group_id
WHERE cog.name = %1 AND cov.name IN (%2, %3);";
$dao = \CRM_Core_DAO::executeQuery($query, [
1 => ["contribution_status", "String"],
2 => ["Completed", "String"],
3 => ["Pending", "String"],
]);
WHERE cog.name = %1";
$dao = \CRM_Core_DAO::executeQuery($query, [1 => ["contribution_status", "String"]]);
while ($dao->fetch()) {
switch ($dao->name) {
case "Cancelled":
$definition->addMethodCall('setCancelledContributionStatusId', [(int) $dao->value]);
break;
case "Chargeback":
$definition->addMethodCall('setChargeBackContributionStatusId', [(int) $dao->value]);
break;
case "Completed":
$definition->addMethodCall('setCompletedContributionStatusId', [(int) $dao->value]);
break;
case "Pending";
case "Failed":
$definition->addMethodCall('setFailedContributionStatusId', [(int) $dao->value]);
break;
case "Overdue":
$definition->addMethodCall('setOverdueContributionStatusId', [(int) $dao->value]);
break;
case "Pending";
$definition->addMethodCall('setPendingContributionStatusId', [(int) $dao->value]);
break;
}

View File

@ -45,23 +45,28 @@ function civicrm_api3_lid_Indelen($params) {
}
$nogInTeDelenGroepId = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_group WHERE name = %1", [1 => ["bij1_nog_in_te_delen", "String"]]);
$returnValues = [];
$lid = CRM_Core_DAO::executeQuery("SELECT DISTINCT(contact_id) FROM civicrm_contribution WHERE contact_id >= %1 ORDER BY contact_id LIMIT 500", [1 => [$min, "Integer"]]);
$lid = CRM_Core_DAO::executeQuery("SELECT DISTINCT(contact_id) FROM civicrm_contribution WHERE contact_id >= %1 ORDER BY contact_id", [1 => [$min, "Integer"]]);
while ($lid->fetch()) {
$inschrijvingsDatum = $contact->haalInschrijvingsDatum((int) $lid->contact_id);
if ($contact->heeftBasisBetaald((int) $lid->contact_id, $vanDatum, $totDatum)) {
$groep->plaatsInGroep((int) $lid->contact_id, Civi::service('bij1Algemeen')->getStandaardLedenGroepId(), $vanDatum);
$groep->verwijderUitGroep((int) $lid->contact_id, Civi::service('bij1Algemeen')->getNieuweLedenGroepId(), TRUE);
$returnValues[] = "Contact met ID " . $lid->contact_id . " in standaard leden geplaatst.";
}
elseif ($contact->heeftRecentBetaald((int) $lid->contact_id)) {
$groep->plaatsInGroep((int) $lid->contact_id, Civi::service('bij1Algemeen')->getNieuweLedenGroepId(), $inschrijvingsDatum);
$returnValues[] = $lid->contact_id . " in nieuwe.";
}
elseif ($contact->heeftMislukt((int) $lid->contact_id, $vanDatum, $totDatum)) {
$groep->plaatsInGroep((int) $lid->contact_id, Civi::service('bij1Algemeen')->getStandaardLedenGroepId(), $vanDatum);
$groep->plaatsInGroep((int) $lid->contact_id, Civi::service('bij1Algemeen')->getBetalingGemistLedenGroepId(), $inschrijvingsDatum);
$groep->verwijderUitGroep((int) $lid->contact_id, Civi::service('bij1Algemeen')->getNieuweLedenGroepId(), TRUE);
$returnValues[] = $lid->contact_id . " in betaling gemist en standaard.";
}
else {
if ($contact->heeftRecentBetaald((int) $lid->contact_id)) {
$groep->plaatsInGroep((int) $lid->contact_id, Civi::service('bij1Algemeen')->getNieuweLedenGroepId(), $contact->haalInschrijvingsDatum((int) $lid->contact_id));
$returnValues[] = "Contact met ID " . $lid->contact_id . " in nieuwe leden geplaatst.";
}
else {
$groep->plaatsInGroep((int) $lid->contact_id, $nogInTeDelenGroepId);
$groep->verwijderUitGroep((int) $lid->contact_id, Civi::service('bij1Algemeen')->getNieuweLedenGroepId(), TRUE);
}
$groep->plaatsInGroep((int) $lid->contact_id, $nogInTeDelenGroepId);
$groep->verwijderUitGroep((int) $lid->contact_id, Civi::service('bij1Algemeen')->getNieuweLedenGroepId(), TRUE);
}
}
$returnValues[] = ", de rest in standaard of nog in te delen";
return civicrm_api3_create_success($returnValues, $params, 'Lid', 'Indelen');
}

View File

@ -14,8 +14,8 @@
<url desc="Support">https://civicoop.org</url>
<url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
</urls>
<releaseDate>2021-12-01</releaseDate>
<version>1.0</version>
<releaseDate>2022-02-07</releaseDate>
<version>1.1</version>
<develStage>alpha</develStage>
<compatibility>
<ver>5.0</ver>

10
mapping/chargebacks.json Executable file
View File

@ -0,0 +1,10 @@
{
"transactie_id": "paymentId",
"chargeback_id": "id",
"chargeback_datum": "createdAt",
"reden_code": "reason.code",
"reden_omschrijving": "reason.description",
"bedrag": "amount.value",
"mollie_customer_id": "payment.customerId",
"chargeback_bedrag": "settlementAmount.value"
}