1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-21 00:41:34 +02:00

Merge pull request #3834 from turbo124/v2

Authorize .net refund()
This commit is contained in:
David Bomba 2020-06-24 15:22:05 +10:00 committed by GitHub
commit 1a48eb7ac2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 653 additions and 189 deletions

View File

@ -31,7 +31,6 @@ use Illuminate\Support\Facades\Cache;
/**
* Class DesignController
* @package App\Http\Controllers
* @covers App\Http\Controllers\DesignController
*/
class DesignController extends BaseController
{
@ -480,15 +479,11 @@ class DesignController extends BaseController
$action = request()->input('action');
$ids = request()->input('ids');
$designs = Design::withTrashed()->find($this->transformKeys($ids));
info($designs);
info(auth()->user()->id);
info(auth()->user()->getCompany()->id);
$designs->each(function ($design, $key) use ($action) {
if (auth()->user()->can('edit', $design)) {
info("authed");
$this->design_repo->{$action}($design);
}
});

View File

@ -0,0 +1,89 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DocumentController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
public function bulk()
{
}
}

View File

@ -415,4 +415,82 @@ class GroupSettingController extends BaseController
return response()->json([], 200);
}
/**
* Perform bulk actions on the list view
*
* @return Collection
*
* @OA\Post(
* path="/api/v1/group_settings/bulk",
* operationId="bulkGroupSettings",
* tags={"group_settings"},
* summary="Performs bulk actions on an array of group_settings",
* description="",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\RequestBody(
* description="An array of group_settings ids",
* required=true,
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(
* type="array",
* @OA\Items(
* type="integer",
* description="Array of hashed IDs to be bulk 'actioned",
* example="[0,1,2,3]",
* ),
* )
* )
* ),
* @OA\Response(
* response=200,
* description="The Bulk Action response",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*
*/
public function bulk()
{
$action = request()->input('action');
$ids = request()->input('ids');
$group_settings = GroupSetting::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if (!$group_settings) {
return response()->json(['message' => 'No Group Settings Found']);
}
/*
* Send the other actions to the switch
*/
$group_settings->each(function ($group, $key) use ($action) {
if (auth()->user()->can('edit', $group)) {
$this->group_setting_repo->{$action}($group);
}
});
/* Need to understand which permission are required for the given bulk action ie. view / edit */
return $this->listResponse(GroupSetting::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
}
}

View File

@ -0,0 +1,29 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Document;
use App\Http\Requests\Request;
use App\Models\Document;
class CreateDocumentRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('create', Document::class);
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Document;
use App\Http\Requests\Request;
class DestroyDocumentRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->document);
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Document;
use App\Http\Requests\Request;
class EditDocumentRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->document);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Document;
use App\Http\Requests\Request;
class ShowDocumentRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('view', $this->document);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Document;
use App\Http\Requests\Request;
use App\Models\Document;
class StoreDocumentRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('create', Document::class);
}
public function rules()
{
return [
];
}
protected function prepareForValidation()
{
$input = $this->all();
$this->replace($input);
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Document;
use App\Http\Requests\Request;
use App\Models\Document;
use App\Utils\Traits\ChecksEntityStatus;
use Illuminate\Support\Facades\Log;
class UpdateDocumentRequest extends Request
{
use ChecksEntityStatus;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->document);
}
public function rules()
{
return [];
}
protected function prepareForValidation()
{
$input = $this->all();
$this->replace($input);
}
}

View File

@ -25,11 +25,11 @@ class Activity extends StaticModel
const ARCHIVE_INVOICE=8;
const DELETE_INVOICE=9;
const CREATE_PAYMENT=10;
//const UPDATE_PAYMENT=11;
const UPDATE_PAYMENT=11;
const ARCHIVE_PAYMENT=12;
const DELETE_PAYMENT=13;
const CREATE_CREDIT=14;
//const UPDATE_CREDIT=15;
const UPDATE_CREDIT=15;
const ARCHIVE_CREDIT=16;
const DELETE_CREDIT=17;
const CREATE_QUOTE=18;

View File

@ -72,6 +72,7 @@ class Company extends BaseModel
];
protected $fillable = [
'enabled_item_tax_rates',
'fill_products',
'industry_id',
'subdomain',
@ -192,7 +193,7 @@ class Company extends BaseModel
public function activities()
{
return $this->hasMany(Activity::class);
return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(300);
}
/**

View File

@ -11,6 +11,8 @@
namespace App\PaymentDrivers;
use App\Models\Payment;
abstract class AbstractPaymentDriver
{
@ -18,7 +20,7 @@ abstract class AbstractPaymentDriver
abstract public function purchase($amount, $return_client_response = false);
abstract public function refund($amount, $transaction_reference, $return_client_response = false);
abstract public function refund(Payment $payment, $refund_amount, $return_client_response = false);
abstract public function setPaymentMethod($payment_method_id);

View File

@ -12,6 +12,7 @@
namespace App\PaymentDrivers\Authorize;
use App\PaymentDrivers\AuthorizePaymentDriver;
use net\authorize\api\contract\v1\GetTransactionDetailsRequest;
use net\authorize\api\controller\GetTransactionDetailsController;
@ -50,15 +51,15 @@ class AuthorizeTransactions
if (($response != null) && ($response->getMessages()->getResultCode() == "Ok"))
{
echo "SUCCESS: Transaction Status:" . $response->getTransaction()->getTransactionStatus() . "\n";
echo " Auth Amount:" . $response->getTransaction()->getAuthAmount() . "\n";
echo " Trans ID:" . $response->getTransaction()->getTransId() . "\n";
info( "SUCCESS: Transaction Status:" . $response->getTransaction()->getTransactionStatus() );
info( " Auth Amount:" . $response->getTransaction()->getAuthAmount() );
info( " Trans ID:" . $response->getTransaction()->getTransId() );
}
else
{
echo "ERROR : Invalid response\n";
info( "ERROR : Invalid response\n");
$errorMessages = $response->getMessages()->getMessage();
echo "Response : " . $errorMessages[0]->getCode() . " " .$errorMessages[0]->getText() . "\n";
info( "Response : " . $errorMessages[0]->getCode() . " " .$errorMessages[0]->getText() );
}
return $response;

View File

@ -12,7 +12,9 @@
namespace App\PaymentDrivers\Authorize;
use App\Models\Payment;
use App\PaymentDrivers\AuthorizePaymentDriver;
use App\PaymentDrivers\Authorize\AuthorizeTransactions;
use net\authorize\api\contract\v1\CreateTransactionRequest;
use net\authorize\api\contract\v1\CustomerProfilePaymentType;
use net\authorize\api\contract\v1\PaymentProfileType;
@ -26,14 +28,22 @@ use net\authorize\api\controller\CreateTransactionController;
*/
class RefundTransaction
{
public $authorize;
public $authorize_transaction;
public function __construct(AuthorizePaymentDriver $authorize)
{
$this->authorize = $authorize;
$this->authorize_transaction = new AuthorizeTransactions($this->authorize);
}
function refundTransaction($transaction_reference, $amount, $payment_profile_id, $profile_id)
function refundTransaction(Payment $payment, $amount)
{
error_reporting (E_ALL & ~E_DEPRECATED);
$transaction_details = $this->authorize_transaction->getTransactionDetails($payment->transaction_reference);
$this->authorize->init();
@ -41,11 +51,11 @@ class RefundTransaction
$refId = 'ref' . time();
$paymentProfile = new PaymentProfileType();
$paymentProfile->setPaymentProfileId( $payment_profile_id );
$paymentProfile->setPaymentProfileId( $transaction_details->getTransaction()->getProfile()->getCustomerPaymentProfileId() );
// set customer profile
$customerProfile = new CustomerProfilePaymentType();
$customerProfile->setCustomerProfileId( $profile_id );
$customerProfile->setCustomerProfileId( $transaction_details->getTransaction()->getProfile()->getCustomerProfileId() );
$customerProfile->setPaymentProfile( $paymentProfile );
//create a transaction
@ -53,7 +63,7 @@ class RefundTransaction
$transactionRequest->setTransactionType("refundTransaction");
$transactionRequest->setAmount($amount);
$transactionRequest->setProfile($customerProfile);
$transactionRequest->setRefTransId($transaction_reference);
$transactionRequest->setRefTransId($payment->transaction_reference);
$request = new CreateTransactionRequest();
$request->setMerchantAuthentication($this->authorize->merchant_authentication);
@ -68,20 +78,32 @@ class RefundTransaction
{
$tresponse = $response->getTransactionResponse();
if ($tresponse != null && $tresponse->getMessages() != null)
if ($tresponse != null && $tresponse->getMessages() != null)
{
echo " Transaction Response code : " . $tresponse->getResponseCode() . "\n";
echo "Refund SUCCESS: " . $tresponse->getTransId() . "\n";
echo " Code : " . $tresponse->getMessages()[0]->getCode() . "\n";
echo " Description : " . $tresponse->getMessages()[0]->getDescription() . "\n";
return [
'transaction_reference' => $tresponse->getTransId(),
'success' => true,
'description' => $tresponse->getMessages()[0]->getDescription(),
'code' => $tresponse->getMessages()[0]->getCode(),
'transaction_response' => $tresponse->getResponseCode()
];
}
else
{
echo "Transaction Failed \n";
if($tresponse->getErrors() != null)
{
echo " Error code : " . $tresponse->getErrors()[0]->getErrorCode() . "\n";
echo " Error message : " . $tresponse->getErrors()[0]->getErrorText() . "\n";
return [
'transaction_reference' => '',
'transaction_response' => '',
'success' => false,
'description' => $tresponse->getErrors()[0]->getErrorText(),
'code' => $tresponse->getErrors()[0]->getErrorCode(),
];
}
}
}
@ -91,22 +113,50 @@ class RefundTransaction
$tresponse = $response->getTransactionResponse();
if($tresponse != null && $tresponse->getErrors() != null)
{
echo " Error code : " . $tresponse->getErrors()[0]->getErrorCode() . "\n";
echo " Error message : " . $tresponse->getErrors()[0]->getErrorText() . "\n";
return [
'transaction_reference' => '',
'transaction_response' => '',
'success' => false,
'description' => $tresponse->getErrors()[0]->getErrorText(),
'code' => $tresponse->getErrors()[0]->getErrorCode(),
];
}
else
{
echo " Error code : " . $response->getMessages()->getMessage()[0]->getCode() . "\n";
echo " Error message : " . $response->getMessages()->getMessage()[0]->getText() . "\n";
return [
'transaction_reference' => '',
'transaction_response' => '',
'success' => false,
'description' => $response->getMessages()->getMessage()[0]->getText(),
'code' => $response->getMessages()->getMessage()[0]->getCode(),
];
}
}
}
else
{
echo "No response returned \n";
return [
'transaction_reference' => '',
'transaction_response' => '',
'success' => false,
'description' => 'No response returned',
'code' => 'No response returned',
];
}
return $response;
return [
'transaction_reference' => '',
'transaction_response' => '',
'success' => false,
'description' => 'No response returned',
'code' => 'No response returned',
];
}

View File

@ -14,8 +14,10 @@ namespace App\PaymentDrivers;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Payment;
use App\PaymentDrivers\Authorize\AuthorizeCreditCard;
use App\PaymentDrivers\Authorize\AuthorizePaymentMethod;
use App\PaymentDrivers\Authorize\RefundTransaction;
use net\authorize\api\constants\ANetEnvironment;
use net\authorize\api\contract\v1\CreateTransactionRequest;
use net\authorize\api\contract\v1\GetMerchantDetailsRequest;
@ -110,16 +112,12 @@ class AuthorizePaymentDriver extends BaseDriver
public function processPaymentView($data)
{
return $this->payment_method->processPaymentView($data);
}
public function processPaymentResponse($request)
{
return $this->payment_method->processPaymentResponse($request);
}
public function purchase($amount, $return_client_response = false)
@ -127,9 +125,9 @@ class AuthorizePaymentDriver extends BaseDriver
return false;
}
public function refund($amount, $transaction_reference, $return_client_response = false)
public function refund(Payment $payment, $refund_amount, $return_client_response = false)
{
return (new RefundTransaction($this))->refundTransaction($payment, $refund_amount);
}
public function findClientGatewayRecord() :?ClientGatewayToken

View File

@ -82,12 +82,12 @@ class BaseDriver extends AbstractPaymentDriver
/**
* Executes a refund attempt for a given amount with a transaction_reference
*
* @param Payment $payment The Payment Object
* @param float $amount The amount to be refunded
* @param string $transaction_reference The transaction reference
* @param boolean $return_client_response Whether the method needs to return a response (otherwise we assume an unattended payment)
* @return mixed
*/
public function refund($amount, $transaction_reference, $return_client_response = false) {}
public function refund(Payment $payment, $amount, $return_client_response = false) {}
/**
* Set the inbound request payment method type for access.

View File

@ -0,0 +1,16 @@
<?php
namespace App\Policies;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class DocumentPolicy extends EntityPolicy
{
use HandlesAuthorization;
public function create(User $user) : bool
{
return $user->isAdmin() || $user->hasPermission('create_all');
}
}

View File

@ -18,6 +18,7 @@ use App\Models\CompanyGateway;
use App\Models\CompanyToken;
use App\Models\Credit;
use App\Models\Design;
use App\Models\Document;
use App\Models\Expense;
use App\Models\GroupSetting;
use App\Models\Invoice;
@ -38,6 +39,7 @@ use App\Policies\CompanyPolicy;
use App\Policies\CompanyTokenPolicy;
use App\Policies\CreditPolicy;
use App\Policies\DesignPolicy;
use App\Policies\DocumentPolicy;
use App\Policies\ExpensePolicy;
use App\Policies\GroupSettingPolicy;
use App\Policies\InvoicePolicy;
@ -70,6 +72,7 @@ class AuthServiceProvider extends ServiceProvider
CompanyGateway::class => CompanyGatewayPolicy::class,
Credit::class => CreditPolicy::class,
Design::class => DesignPolicy::class,
Document::class => DocumentPolicy::class,
Expense::class => ExpensePolicy::class,
GroupSetting::class => GroupSettingPolicy::class,
Invoice::class => InvoicePolicy::class,

View File

@ -14,7 +14,7 @@ namespace App\Repositories;
use App\Models\GroupSetting;
use App\Utils\Traits\MakesHash;
class GroupSettingRepository
class GroupSettingRepository extends BaseRepository
{
use MakesHash;
/**

View File

@ -12,6 +12,7 @@
namespace App\Services\Ledger;
use App\Factory\CompanyLedgerFactory;
use App\Models\Activity;
use App\Models\CompanyLedger;
class LedgerService
@ -38,6 +39,7 @@ class LedgerService
$company_ledger->adjustment = $adjustment;
$company_ledger->notes = $notes;
$company_ledger->balance = $balance + $adjustment;
$company_ledger->activity_id = Activity::UPDATE_INVOICE;
$company_ledger->save();
$this->entity->company_ledger()->save($company_ledger);
@ -60,6 +62,7 @@ class LedgerService
$company_ledger->client_id = $this->entity->client_id;
$company_ledger->adjustment = $adjustment;
$company_ledger->balance = $balance + $adjustment;
$company_ledger->activity_id = Activity::UPDATE_PAYMENT;
$company_ledger->save();
$this->entity->company_ledger()->save($company_ledger);
@ -80,6 +83,7 @@ class LedgerService
$company_ledger->adjustment = $adjustment;
$company_ledger->notes = $notes;
$company_ledger->balance = $balance + $adjustment;
$company_ledger->activity_id = Activity::UPDATE_CREDIT;
$company_ledger->save();
$this->entity->company_ledger()->save($company_ledger);

View File

@ -57,7 +57,7 @@ class RefundPayment
if ($this->refund_data['gateway_refund'] !== false && $this->total_refund > 0) {
$gateway = CompanyGateway::find($this->company_gateway_id);
$gateway = CompanyGateway::find($this->payment->company_gateway_id);
if ($gateway) {
$response = $gateway->driver($this->payment->client)->refund($this->payment, $this->total_refund);
@ -65,7 +65,7 @@ class RefundPayment
if (!$response) {
throw new PaymentRefundFailed();
}
info(print_r($response,1));
//todo
//need to check the gateway response has successfully be transacted.
@ -290,127 +290,3 @@ class RefundPayment
return $this->payment;
}
}
/*
private function refundPaymentWithNoInvoices(array $data)
{
$this->createActivity($data, $credit_note->id);
//determine if we need to refund via gateway
if ($data['gateway_refund'] !== false) {
//todo process gateway refund, on success, reduce the credit note balance to 0
}
$this->save();
//$this->client->paid_to_date -= $data['amount'];
$this->client->save();
return $this->fresh();
}
private function refundPaymentWithInvoices($data)
{
if ($data['gateway_refund'] !== false && $this->total_refund > 0) {
$gateway = CompanyGateway::find($this->company_gateway_id);
if ($gateway) {
$response = $gateway->driver($this->client)->refund($this, $this->total_refund);
if (!$response) {
throw new PaymentRefundFailed();
}
}
}
if ($this->total_refund > 0) {
$this->refunded += $this->total_refund;
}
$this->save();
$client_balance_adjustment = $this->adjustInvoices($data);
$credit_note->ledger()->updateCreditBalance($client_balance_adjustment, $ledger_string);
$this->client->paid_to_date -= $data['amount'];
$this->client->save();
return $this;
}
private function createActivity(array $data, int $credit_id)
{
$fields = new \stdClass;
$activity_repo = new ActivityRepository();
$fields->payment_id = $this->id;
$fields->user_id = $this->user_id;
$fields->company_id = $this->company_id;
$fields->activity_type_id = Activity::REFUNDED_PAYMENT;
$fields->credit_id = $credit_id;
if (isset($data['invoices'])) {
foreach ($data['invoices'] as $invoice) {
$fields->invoice_id = $invoice->id;
$activity_repo->save($fields, $this);
}
} else {
$activity_repo->save($fields, $this);
}
}
private function buildCreditNote(array $data) :?Credit
{
$credit_note = CreditFactory::create($this->company_id, $this->user_id);
$credit_note->assigned_user_id = isset($this->assigned_user_id) ?: null;
$credit_note->date = $data['date'];
$credit_note->status_id = Credit::STATUS_SENT;
$credit_note->client_id = $this->client->id;
$credit_note->amount = $data['amount'];
$credit_note->balance = $data['amount'];
return $credit_note;
}
private function adjustInvoices(array $data)
{
$adjustment_amount = 0;
foreach ($data['invoices'] as $refunded_invoice) {
$invoice = Invoice::find($refunded_invoice['invoice_id']);
$invoice->service()->updateBalance($refunded_invoice['amount'])->save();
if ($invoice->amount == $invoice->balance) {
$invoice->service()->setStatus(Invoice::STATUS_SENT);
} else {
$invoice->service()->setStatus(Invoice::STATUS_PARTIAL);
}
$client = $invoice->client;
$adjustment_amount += $refunded_invoice['amount'];
$client->balance += $refunded_invoice['amount'];
$client->save();
//todo adjust ledger balance here? or after and reference the credit and its total
}
return $adjustment_amount;
}
}
*/

View File

@ -35,6 +35,7 @@ class CompanyLedgerTransformer extends EntityTransformer
'notes' => (string)$company_ledger->notes ?: '',
'balance' => (float) $company_ledger->balance,
'adjustment' => (float) $company_ledger->adjustment,
'activity_id' => (int)$company_ledger->activity_id,
'created_at' => (int)$company_ledger->created_at,
'updated_at' => (int)$company_ledger->updated_at,
'archived_at' => (int)$company_ledger->deleted_at,

View File

@ -12,9 +12,11 @@
namespace App\Transformers;
use App\Models\Client;
use App\Models\Document;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Paymentable;
use App\Transformers\DocumentTransformer;
use App\Utils\Traits\MakesHash;
class PaymentTransformer extends EntityTransformer
@ -28,7 +30,8 @@ class PaymentTransformer extends EntityTransformer
protected $availableIncludes = [
'client',
'invoices',
'paymentables'
'paymentables',
'documents'
];
public function __construct($serializer = null)
@ -59,6 +62,11 @@ class PaymentTransformer extends EntityTransformer
return $this->includeCollection($payment->paymentables, $transformer, Paymentable::class);
}
public function includeDocuments(Payment $payment)
{
$transformer = new DocumentTransformer($this->serializer);
return $this->includeCollection($payment->documents, $transformer, Document::class);
}
public function transform(Payment $payment)
{

View File

@ -12,8 +12,10 @@
namespace App\Transformers;
use App\Models\Company;
use App\Models\Document;
use App\Models\Product;
use App\Models\User;
use App\Transformers\DocumentTransformer;
use App\Utils\Traits\MakesHash;
class ProductTransformer extends EntityTransformer
@ -28,7 +30,8 @@ class ProductTransformer extends EntityTransformer
*/
protected $availableIncludes = [
'company',
'user'
'user',
'documents',
];
@ -56,6 +59,12 @@ class ProductTransformer extends EntityTransformer
return $this->includeItem($product->company, $transformer, Company::class);
}
public function includeDocuments(Product $product)
{
$transformer = new DocumentTransformer($this->serializer);
return $this->includeCollection($product->documents, $transformer, Document::class);
}
public function transform(Product $product)
{
return [

View File

@ -1120,6 +1120,7 @@ class CreateUsersTable extends Migration
$table->unsignedInteger('company_id');
$table->unsignedInteger('client_id')->nullable();
$table->unsignedInteger('user_id')->nullable();
$table->unsignedInteger('activity_id')->nullable();
$table->decimal('adjustment', 16, 4)->nullable();
$table->decimal('balance', 16, 4)->nullable(); //this is the clients balance carried forward
@ -1167,6 +1168,7 @@ class CreateUsersTable extends Migration
$table->unsignedInteger('user_id')->nullable();
$table->string('name')->nullable();
$table->mediumText('settings')->nullable();
$table->boolean('is_default')->default(0);
$table->softDeletes('deleted_at', 6);
$table->timestamps(6);

View File

@ -6,6 +6,7 @@
<meta name="report_errors" content="{{ $report_errors }}">
<meta name="google-signin-client_id" content="{{ config('services.google.client_id') }}">
<meta name="minimum_client_version" content="{{ config('ninja.minimum_client_version') }}">
<link rel="manifest" href="manifest.json">
</head>
<body style="background-color:#888888;">

View File

@ -69,6 +69,10 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk');
Route::resource('documents', 'DocumentController');// name = (documents. index / create / show / update / destroy / edit
Route::post('documents/bulk', 'DocumentController@bulk')->name('documents.bulk');
Route::resource('client_statement', 'ClientStatementController@statement');// name = (client_statement. index / create / show / update / destroy / edit
Route::resource('payment_terms', 'PaymentTermController');// name = (payments. index / create / show / update / destroy / edit
@ -108,6 +112,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::put('company_users/{user}', 'CompanyUserController@update');
Route::resource('group_settings', 'GroupSettingController');
Route::post('group_settings/bulk', 'GroupSettingController@bulk');
Route::resource('tax_rates', 'TaxRateController');// name = (tasks. index / create / show / update / destroy / edit

View File

@ -49,7 +49,9 @@ Route::group(['middleware' => ['auth:contact','locale'], 'prefix' => 'client', '
Route::resource('payment_methods', 'ClientPortal\PaymentMethodController');// name = (payment_methods. index / create / show / update / destroy / edit
Route::match(['GET', 'POST'], 'quotes/approve', 'ClientPortal\QuoteController@bulk')->name('quotes.bulk');
Route::resource('quotes', 'ClientPortal\QuoteController')->only('index', 'show');
Route::get('quotes', 'ClientPortal\QuoteController@index')->name('quotes.index')->middleware('portal_enabled');
Route::get('quotes/{quote}', 'ClientPortal\QuoteController@show')->name('quote.show');
Route::get('quotes/{quote_invitation}', 'ClientPortal\QuoteController@show')->name('quote.show_invitation');
Route::resource('credits', 'ClientPortal\CreditController')->only('index', 'show');

View File

@ -132,4 +132,80 @@ class DesignApiTest extends TestCase
$this->assertTrue((bool)$design->is_deleted);
$this->assertGreaterThan(0, $design->deleted_at);
}
public function testDesignArchive()
{
$design = [
'body' => 'body',
'includes' => 'includes',
'product' => 'product',
'task' => 'task',
'footer' => 'footer',
'header' => 'header'
];
$data = [
'name' => $this->faker->firstName,
'design' => $design
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->post('/api/v1/designs', $data);
$response->assertStatus(200);
$arr = $response->json();
$this->id = $arr['data']['id'];
$data['ids'][] = $arr['data']['id'];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->post('/api/v1/designs/bulk?action=archive', $data);
$response->assertStatus(200);
$design = Design::where('id', $this->decodePrimaryKey($arr['data']['id']))->withTrashed()->first();
$this->assertNotNull($design->deleted_at);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->post('/api/v1/designs/bulk?action=restore', $data);
$response->assertStatus(200);
$design = Design::where('id', $this->decodePrimaryKey($arr['data']['id']))->withTrashed()->first();
$this->assertNull($design->deleted_at);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->post('/api/v1/designs/bulk?action=delete', $data);
$response->assertStatus(200);
$design = Design::where('id', $this->decodePrimaryKey($arr['data']['id']))->withTrashed()->first();
$this->assertTrue((bool)$design->is_deleted);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token
])->post('/api/v1/designs/bulk?action=restore', $data);
$response->assertStatus(200);
$design = Design::where('id', $this->decodePrimaryKey($arr['data']['id']))->withTrashed()->first();
$this->assertFalse((bool)$design->is_deleted);
$this->assertNull($design->deleted_at);
}
}

View File

@ -2,6 +2,10 @@
namespace Tests\Integration\PaymentDrivers;
use App\Factory\PaymentFactory;
use App\Models\CompanyGateway;
use App\PaymentDrivers\AuthorizePaymentDriver;
use Tests\MockAccountData;
use Tests\TestCase;
use net\authorize\api\constants\ANetEnvironment;
use net\authorize\api\contract\v1 as AnetAPI;
@ -31,10 +35,11 @@ use net\authorize\api\controller\GetMerchantDetailsController;
*/
class AuthorizeTest extends TestCase
{
use MockAccountData;
public $customer_profile_id = 1512191314;
public $customer_profile_id = 1512373273;
public $customer_payment_profile = 1512219932;
public $customer_payment_profile = 1512424103;
public function setUp() :void
{
@ -43,7 +48,8 @@ class AuthorizeTest extends TestCase
if (! config('ninja.testvars.authorize')) {
$this->markTestSkipped('authorize.net not configured');
}
$this->makeTestData();
}
public function testUnpackingVars()
@ -148,15 +154,7 @@ class AuthorizeTest extends TestCase
// Create an array of any shipping addresses
$shippingProfiles[] = $customerShippingAddress;
$refId = 'ref' . time();
$email = "test@gmail.com";
// // Create a new CustomerPaymentProfile object
// $paymentProfile = new AnetAPI\CustomerPaymentProfileType();
// $paymentProfile->setCustomerType('individual');
// $paymentProfile->setBillTo($billTo);
// $paymentProfile->setPayment($paymentCreditCard);
// $paymentProfiles[] = $paymentProfile;
$email = "test12@gmail.com";
// Create a new CustomerProfileType and add the payment profile object
$customerProfile = new CustomerProfileType();
@ -188,6 +186,8 @@ class AuthorizeTest extends TestCase
$errorMessages = $response->getMessages()->getMessage();
info("Response : " . $errorMessages[0]->getCode() . " " .$errorMessages[0]->getText() . "\n");
}
info("the new customer profile id = ". $response->getCustomerProfileId());
$this->assertNotNull($response);
@ -241,16 +241,16 @@ class AuthorizeTest extends TestCase
// Set credit card information for payment profile
$creditCard = new CreditCardType();
$creditCard->setCardNumber("4007000000027");
$creditCard->setExpirationDate("2038-12");
$creditCard->setCardCode("142");
$creditCard->setCardNumber("4111111111111111");
$creditCard->setExpirationDate("2024-01");
$creditCard->setCardCode("100");
$paymentCreditCard = new PaymentType();
$paymentCreditCard->setCreditCard($creditCard);
// Create the Bill To info for new payment type
$billto = new CustomerAddressType();
$billto->setFirstName("Ellen");
$billto->setLastName("Johnson");
$billto->setFirstName("Elas");
$billto->setLastName("Joson");
$billto->setCompany("Souveniropolis");
$billto->setAddress("14 Main Street");
$billto->setCity("Pecan Springs");
@ -315,7 +315,7 @@ class AuthorizeTest extends TestCase
$transactionRequestType = new TransactionRequestType();
$transactionRequestType->setTransactionType( "authCaptureTransaction");
$transactionRequestType->setAmount(400);
$transactionRequestType->setAmount(350);
$transactionRequestType->setProfile($profileToCharge);
$request = new CreateTransactionRequest();
@ -372,6 +372,31 @@ class AuthorizeTest extends TestCase
}
$this->assertNotNull($response);
$this->assertNotNull($tresponse);
/* Testing refunds - need to research more as payments are in a pending state so cannot be 'refunded'*/
// info("transaction reference = " . $tresponse->getTransId());
// $payment = PaymentFactory::create($this->company->id, $this->user->id);
// $payment->amount = 400;
// $payment->client_id = $this->client->id;
// $payment->date = now();
// $payment->transaction_reference = $tresponse->getTransId();
// $payment->company_gateway_id = 1;
// $payment->save();
// $company_gateway = CompanyGateway::where('gateway_key', '3b6621f970ab18887c4f6dca78d3f8bb')->first();
// $authorize_payment_driver = new AuthorizePaymentDriver($company_gateway, $this->client);
// $response = $authorize_payment_driver->refund($payment, 350);
// info(print_r($response,1));
// $this->assertTrue(is_array($response));
}
}