This commit is contained in:
steenrabol 2016-01-06 20:52:09 +01:00
parent d1bef24ede
commit 48ce3f64e6
22 changed files with 868 additions and 4 deletions

View File

@ -0,0 +1,23 @@
<?php namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
class ExpenseWasArchived extends Event {
use SerializesModels;
public $expense;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($espense)
{
$this->expense = $expense;
}
}

View File

@ -0,0 +1,22 @@
<?php namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
class ExpenseWasCreated extends Event {
use SerializesModels;
public $expense;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($expense)
{
$this->expense = $expense;
}
}

View File

@ -0,0 +1,23 @@
<?php namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
class ExpenseWasDeleted extends Event {
use SerializesModels;
public $expense;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($expense)
{
$this->expense = $expense;
}
}

View File

@ -0,0 +1,23 @@
<?php namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
class ExpenseWasRestored extends Event {
use SerializesModels;
public $expense;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($expense)
{
$this->expense = $expense;
}
}

View File

@ -0,0 +1,131 @@
<?php namespace App\Http\Controllers;
use Auth;
use Datatable;
use Utils;
use View;
use URL;
use Validator;
use Input;
use Session;
use Redirect;
use Cache;
use App\Models\Vendor;
use App\Services\ExpenseService;
use App\Ninja\Repositories\ExpenseRepository;
use App\Http\Requests\CreateExpenseRequest;
class ExpenseController extends BaseController
{
protected $expenseRepo;
protected $expenseService;
public function __construct(ExpenseRepository $expenseRepo, ExpenseService $expenseService)
{
parent::__construct();
$this->expenseRepo = $expenseRepo;
$this->expenseService = $expenseService;
}
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
return View::make('list', array(
'entityType' => ENTITY_EXPENSE,
'title' => trans('texts.expenses'),
'sortCol' => '4',
'columns' => Utils::trans([
'checkbox',
'vendor',
'expense_amount',
'expense_balance',
'expense_date',
'private_notes',
''
]),
));
}
public function getDatatable($vendorPublicId = null)
{
return $this->expenseService->getDatatable($vendorPublicId, Input::get('sSearch'));
}
public function create($vendorPublicId = 0)
{
$vendor = Vendor::scope($vendorPublicId)->with('vendorcontacts')->firstOrFail();
$data = array(
'vendorPublicId' => Input::old('vendor') ? Input::old('vendor') : $vendorPublicId,
'expense' => null,
'method' => 'POST',
'url' => 'expenses',
'title' => trans('texts.new_expense'),
'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(),
);
$data = array_merge($data, self::getViewModel());
return View::make('expenses.edit', $data);
}
public function edit($publicId)
{
$expense = Expense::scope($publicId)->firstOrFail();
$expense->expense_date = Utils::fromSqlDate($expense->expense_date);
$data = array(
'vendor' => null,
'expense' => $expense,
'method' => 'PUT',
'url' => 'expenses/'.$publicId,
'title' => 'Edit Expense',
'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), );
return View::make('expense.edit', $data);
}
public function store(CreateExpenseRequest $request)
{
$expense = $this->expenseRepo->save($request->input());
Session::flash('message', trans('texts.created_expense'));
return redirect()->to('expenses');
}
public function bulk()
{
$action = Input::get('action');
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
$count = $this->expenseService->bulk($ids, $action);
if ($count > 0) {
$message = Utils::pluralize($action.'d_expense', $count);
Session::flash('message', $message);
}
return Redirect::to('expenses');
}
private static function getViewModel()
{
return [
'data' => Input::old('data'),
'account' => Auth::user()->account,
'sizes' => Cache::get('sizes'),
'paymentTerms' => Cache::get('paymentTerms'),
'industries' => Cache::get('industries'),
'currencies' => Cache::get('currencies'),
'languages' => Cache::get('languages'),
'countries' => Cache::get('countries'),
'customLabel1' => Auth::user()->account->custom_vendor_label1,
'customLabel2' => Auth::user()->account->custom_vendor_label2,
];
}
}

View File

@ -0,0 +1,29 @@
<?php namespace app\Http\Requests;
use App\Http\Requests\Request;
use Illuminate\Validation\Factory;
class CreateExpenseRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'amount' => 'required|positive',
];
}
}

View File

@ -0,0 +1,28 @@
<?php namespace app\Http\Requests;
use App\Http\Requests\Request;
use Illuminate\Validation\Factory;
use App\Models\Invoice;
class UpdatePaymentRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [];
}
}

View File

@ -2,9 +2,8 @@
use App\Http\Requests\Request;
use Illuminate\Validation\Factory;
use App\Models\Invoice;
class UpdatePaymentRequest extends Request
class UpdateExpenseRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
@ -23,6 +22,9 @@ class UpdatePaymentRequest extends Request
*/
public function rules()
{
return [];
return [
'amount' => 'required|positive',
];
}
}

View File

@ -188,6 +188,21 @@ Route::group(['middleware' => 'auth'], function() {
Route::get('api/vendor', array('as'=>'api.vendors', 'uses'=>'VendorController@getDatatable'));
Route::get('api/vendoractivities/{vendor_id?}', array('as'=>'api.vendoractivities', 'uses'=>'VendorActivityController@getDatatable'));
Route::post('vendors/bulk', 'VendorController@bulk');
// Expense
Route::get('expenses/{id}/edit', function() {
return View::make('header');
});
Route::resource('expenses', 'ExpenseController');
Route::get('expenses/create/{vendor_id?}', 'ExpenseController@create');
Route::get('api/expenses/{vendor_id?}', array('as'=>'api.expenses', 'uses'=>'ExpenseController@getDatatable'));
//Route::get('api/expenseactivities/{vendor_id?}', array('as'=>'api.expenseactivities', 'uses'=>'ExpenseActivityController@getDatatable'));
//Route::post('vendors/bulk', 'VendorController@bulk');
Route::post('expenses/bulk', 'ExpenseController@bulk');
});
@ -252,6 +267,7 @@ if (!defined('CONTACT_EMAIL')) {
define('ENV_STAGING', 'staging');
define('RECENTLY_VIEWED', 'RECENTLY_VIEWED');
define('ENTITY_CLIENT', 'client');
define('ENTITY_CONTACT', 'contact');
define('ENTITY_INVOICE', 'invoice');
@ -346,6 +362,12 @@ if (!defined('CONTACT_EMAIL')) {
define('ACTIVITY_TYPE_DELETE_VENDOR', 32);
define('ACTIVITY_TYPE_RESTORE_VENDOR', 33);
// expenses
define('ACTIVITY_TYPE_CREATE_EXPENSE', 34);
define('ACTIVITY_TYPE_ARCHIVE_EXPENSE', 35);
define('ACTIVITY_TYPE_DELETE_EXPENSE', 36);
define('ACTIVITY_TYPE_RESTORE_EXPENSE', 37);
define('DEFAULT_INVOICE_NUMBER', '0001');
define('RECENTLY_VIEWED_LIMIT', 8);
define('LOGGED_ERROR_LIMIT', 100);
@ -379,7 +401,6 @@ if (!defined('CONTACT_EMAIL')) {
define('MAX_NUM_VENDORS_PRO', 20000);
define('MAX_NUM_VENDORS_LEGACY', 500);
define('INVOICE_STATUS_DRAFT', 1);
define('INVOICE_STATUS_SENT', 2);
define('INVOICE_STATUS_VIEWED', 3);

View File

@ -0,0 +1,16 @@
<?php namespace app\Listeners;
use Carbon;
use App\Models\Credit;
use App\Events\PaymentWasDeleted;
use App\Ninja\Repositories\ExpenseRepository;
class ExpenseListener
{
protected $expenseRepo;
public function __construct(ExpenseRepository $expenseRepo)
{
$this->expenseRepo = $expenseRepo;
}
}

View File

@ -10,6 +10,7 @@ use App\Events\CreditWasCreated;
use App\Events\PaymentWasCreated;
use App\Events\VendorWasCreated;
use App\Events\ExpenseWasCreated;
class SubscriptionListener
{
@ -51,4 +52,10 @@ class SubscriptionListener
{
$this->checkSubscriptions(ACTIVITY_TYPE_CREATE_VENDOR, $event->vendor);
}
public function createdExpense(ExpenseWasCreated $event)
{
$this->checkSubscriptions(ACTIVITY_TYPE_CREATE_EXPENSE, $event->expense);
}
}

79
app/Models/Expense.php Normal file
View File

@ -0,0 +1,79 @@
<?php namespace App\Models;
use Utils;
use DB;
use Carbon;
use Laracasts\Presenter\PresentableTrait;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Events\ExpenseWasCreated;
class Expense extends EntityModel
{
use SoftDeletes;
use PresentableTrait;
protected $dates = ['deleted_at'];
protected $presenter = 'App\Ninja\Presenters\ExpensePresenter';
protected $fillable = [
'vendor_id',
];
public function account()
{
return $this->belongsTo('App\Models\Account');
}
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function vendor()
{
return $this->belongsTo('App\Models\Vendor')->withTrashed();
}
public function getName()
{
return '';
}
public function getEntityType()
{
return ENTITY_EXPENSE;
}
public function apply($amount)
{
if ($amount > $this->balance) {
$applied = $this->balance;
$this->balance = 0;
} else {
$applied = $amount;
$this->balance = $this->balance - $amount;
}
$this->save();
return $applied;
}
}
Expense::creating(function ($expense) {
$expense->setNullValues();
});
Expense::created(function ($expense) {
event(new ExpenseWasCreated($expense));
});
Expense::updating(function ($expense) {
$expense->setNullValues();
});
Expense::updated(function ($expense) {
event(new ExpenseWasUpdated($expense));
});

View File

@ -0,0 +1,56 @@
<?php namespace App\Models;
use Auth;
use Eloquent;
use Utils;
use Session;
use Request;
use Carbon;
class ExpenseAcitvity extends Eloquent {
public $timestamps = true;
public function scopeScope($query)
{
return $query->whereAccountId(Auth::user()->account_id);
}
public function account()
{
return $this->belongsTo('App\Models\Account');
}
public function user()
{
return $this->belongsTo('App\Models\User')->withTrashed();
}
public function vendor()
{
return $this->belongsTo('App\Models\Vendor')->withTrashed();
}
public function getMessage()
{
$activityTypeId = $this->activity_type_id;
$account = $this->account;
$vendor = $this->vendor;
$user = $this->user;
$contactId = $this->contact_id;
$isSystem = $this->is_system;
if($vendor) {
$route = $vendor->getRoute();
$data = [
'vendor' => link_to($route, $vendor->getDisplayName()),
'user' => $isSystem ? '<i>' . trans('texts.system') . '</i>' : $user->getDisplayName(),
'vendorcontact' => $contactId ? $vendor->getDisplayName() : $user->getDisplayName(),
];
} else {
return trans("texts.invalid_activity");
}
return trans("texts.activity_{$activityTypeId}", $data);
}
}

View File

@ -0,0 +1,17 @@
<?php namespace App\Ninja\Presenters;
use Utils;
use Laracasts\Presenter\Presenter;
class ExpensePresenter extends Presenter {
public function vendor()
{
return $this->entity->vendor ? $this->entity->vendor->getDisplayName() : '';
}
public function expense_date()
{
return Utils::fromSqlDate($this->entity->expense_date);
}
}

View File

@ -0,0 +1,94 @@
<?php namespace App\Ninja\Repositories;
use DB;
use Utils;
use App\Models\Expense;
use App\Models\Vendor;
use App\Ninja\Repositories\BaseRepository;
class ExpenseRepository extends BaseRepository
{
public function getClassName()
{
return 'App\Models\Expense';
}
public function find($vendorPublicId = null, $filter = null)
{
$query = DB::table('expenses')
->join('accounts', 'accounts.id', '=', 'expenses.account_id')
->join('vendors', 'vendors.id', '=', 'expenses.vendor_id')
->join('vendor_contacts', 'vendor_contacts.vendor_id', '=', 'vendors.id')
->where('vendors.account_id', '=', \Auth::user()->account_id)
->where('vendors.deleted_at', '=', null)
->where('vendor_contacts.deleted_at', '=', null)
->where('vendor_contacts.is_primary', '=', true)
->select(
DB::raw('COALESCE(vendors.currency_id, accounts.currency_id) currency_id'),
DB::raw('COALESCE(vendors.country_id, accounts.country_id) country_id'),
'expenses.public_id',
'vendors.name as vendor_name',
'vendors.public_id as vendor_public_id',
'expenses.amount',
'expenses.balance',
'expenses.expense_date',
'vendor_contacts.first_name',
'vendor_contacts.last_name',
'vendor_contacts.email',
'expenses.private_notes',
'expenses.deleted_at',
'expenses.is_deleted'
);
if ($vendorPublicId) {
$query->where('vendors.public_id', '=', $vendorPublicId);
}
if (!\Session::get('show_trash:expense')) {
$query->where('expenses.deleted_at', '=', null);
}
if ($filter) {
$query->where(function ($query) use ($filter) {
$query->where('vendors.name', 'like', '%'.$filter.'%');
});
}
return $query;
}
public function save($input)
{
$publicId = isset($input['public_id']) ? $input['public_id'] : false;
if ($publicId) {
$expense = Expense::scope($publicId)->firstOrFail();
} else {
$expense = Expense::createNew();
}
// First auto fille
$expense->fill($input);
// We can have an expense without a vendor
if(isset($input['vendor'])) {
$expense->vendor_id = Vendor::getPrivateId($input['vendor']);
}
$expense->expense_date = Utils::toSqlDate($input['expense_date']);
$expense->amount = Utils::parseFloat($input['amount']);
if(isset($input['amountcur']))
$expense->amountcur = Utils::parseFloat($input['amountcur']);
$expense->balance = Utils::parseFloat($input['amount']);
$expense->private_notes = trim($input['private_notes']);
if(isset($input['exchange_rate']))
$expense->exchange_rate = Utils::parseFloat($input['exchange_rate']);
$expense->save();
return $expense;
}
}

View File

@ -0,0 +1,24 @@
<?php namespace App\Ninja\Transformers;
use App\Models\Account;
use App\Models\VendorContact;
use League\Fractal;
class VendorContactTransformer extends EntityTransformer
{
public function transform(VendorContact $contact)
{
return [
'id' => (int) $contact->public_id,
'first_name' => $contact->first_name,
'last_name' => $contact->last_name,
'email' => $contact->email,
'updated_at' => $this->getTimestamp($contact->updated_at),
'archived_at' => $this->getTimestamp($contact->deleted_at),
'is_primary' => (bool) $contact->is_primary,
'phone' => $contact->phone,
'last_login' => $contact->last_login,
'account_key' => $this->account->account_key,
];
}
}

View File

@ -0,0 +1,91 @@
<?php namespace App\Ninja\Transformers;
use App\Models\Account;
use App\Models\Vendor;
use App\Models\Contact;
use League\Fractal;
/**
* @SWG\Definition(definition="Vendor", @SWG\Xml(name="Vendor"))
*/
class VendorTransformer extends EntityTransformer
{
/**
* @SWG\Property(property="id", type="integer", example=1, readOnly=true)
* @SWG\Property(property="balance", type="float", example=10, readOnly=true)
* @SWG\Property(property="paid_to_date", type="float", example=10, readOnly=true)
* @SWG\Property(property="user_id", type="integer", example=1)
* @SWG\Property(property="account_key", type="string", example="123456")
* @SWG\Property(property="updated_at", type="timestamp", example="")
* @SWG\Property(property="archived_at", type="timestamp", example="1451160233")
* @SWG\Property(property="address1", type="string", example="10 Main St.")
* @SWG\Property(property="address2", type="string", example="1st Floor")
* @SWG\Property(property="city", type="string", example="New York")
* @SWG\Property(property="state", type="string", example="NY")
* @SWG\Property(property="postal_code", type="string", example=10010)
* @SWG\Property(property="country_id", type="integer", example=840)
* @SWG\Property(property="work_phone", type="string", example="(212) 555-1212")
* @SWG\Property(property="private_notes", type="string", example="Notes...")
* @SWG\Property(property="last_login", type="date-time", example="2016-01-01 12:10:00")
* @SWG\Property(property="website", type="string", example="http://www.example.com")
* @SWG\Property(property="industry_id", type="integer", example=1)
* @SWG\Property(property="size_id", type="integer", example=1)
* @SWG\Property(property="is_deleted", type="boolean", example=false)
* @SWG\Property(property="payment_terms", type="", example=30)
* @SWG\Property(property="custom_value1", type="string", example="Value")
* @SWG\Property(property="custom_value2", type="string", example="Value")
* @SWG\Property(property="vat_number", type="string", example="123456")
* @SWG\Property(property="id_number", type="string", example="123456")
* @SWG\Property(property="language_id", type="integer", example=1)
*/
protected $availableIncludes = [
'contacts',
'invoices',
];
public function includeContacts(Vendor $vendor)
{
$transformer = new ContactTransformer($this->account, $this->serializer);
return $this->includeCollection($vendor->contacts, $transformer, ENTITY_CONTACT);
}
public function includeInvoices(Vendor $vendor)
{
$transformer = new InvoiceTransformer($this->account, $this->serializer);
return $this->includeCollection($vendor->invoices, $transformer, ENTITY_INVOICE);
}
public function transform(Vendor $vendor)
{
return [
'id' => (int) $vendor->public_id,
'name' => $vendor->name,
'balance' => (float) $vendor->balance,
'paid_to_date' => (float) $vendor->paid_to_date,
'user_id' => (int) $vendor->user->public_id + 1,
'account_key' => $this->account->account_key,
'updated_at' => $this->getTimestamp($vendor->updated_at),
'archived_at' => $this->getTimestamp($vendor->deleted_at),
'address1' => $vendor->address1,
'address2' => $vendor->address2,
'city' => $vendor->city,
'state' => $vendor->state,
'postal_code' => $vendor->postal_code,
'country_id' => (int) $vendor->country_id,
'work_phone' => $vendor->work_phone,
'private_notes' => $vendor->private_notes,
'last_login' => $vendor->last_login,
'website' => $vendor->website,
'industry_id' => (int) $vendor->industry_id,
'size_id' => (int) $vendor->size_id,
'is_deleted' => (bool) $vendor->is_deleted,
'payment_terms' => (int) $vendor->payment_terms,
'vat_number' => $vendor->vat_number,
'id_number' => $vendor->id_number,
'language_id' => (int) $vendor->language_id,
'currency_id' => (int) $vendor->currency_id
];
}
}

View File

@ -151,6 +151,21 @@ class EventServiceProvider extends ServiceProvider {
'App\Events\VendorWasRestored' => [
'App\Listeners\VendorActivityListener@restoredVendor',
],
// Expense events
'App\Events\ExpenseWasCreated' => [
'App\Listeners\ExpenseActivityListener@createdExpense',
'App\Listeners\SubscriptionListener@createdExpense',
],
'App\Events\ExpenseWasArchived' => [
'App\Listeners\ExpenseActivityListener@archivedExpense',
],
'App\Events\ExpenseWasDeleted' => [
'App\Listeners\ExpenseActivityListener@deletedExpense',
],
'App\Events\ExpenseWasRestored' => [
'App\Listeners\ExpenseActivityListener@restoredExpense',
],
];

View File

@ -0,0 +1,84 @@
<?php namespace App\Services;
use Utils;
use URL;
use App\Services\BaseService;
use App\Ninja\Repositories\ExpenseRepository;
class ExpenseService extends BaseService
{
protected $expenseRepo;
protected $datatableService;
public function __construct(ExpenseRepository $expenseRepo, DatatableService $datatableService)
{
$this->expenseRepo = $expenseRepo;
$this->datatableService = $datatableService;
}
protected function getRepo()
{
return $this->expenseRepo;
}
public function save($data)
{
return $this->expenseRepo->save($data);
}
public function getDatatable($vendorPublicId, $search)
{
$query = $this->expenseRepo->find($vendorPublicId, $search);
return $this->createDatatable(ENTITY_CREDIT, $query, !$vendorPublicId);
}
protected function getDatatableColumns($entityType, $hideClient)
{
return [
[
'vendor_name',
function ($model) {
return $model->vendor_public_id ? link_to("vendors/{$model->vendor_public_id}", Utils::getVendorDisplayName($model)) : '';
},
! $hideClient
],
[
'amount',
function ($model) {
return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id) . '<span '.Utils::getEntityRowClass($model).'/>';
}
],
[
'balance',
function ($model) {
return Utils::formatMoney($model->balance, $model->currency_id, $model->country_id);
}
],
[
'expense_date',
function ($model) {
return Utils::fromSqlDate($model->expense_date);
}
],
[
'private_notes',
function ($model) {
return $model->private_notes;
}
]
];
}
protected function getDatatableActions($entityType)
{
return [
[
trans('texts.apply_expense'),
function ($model) {
return URL::to("espense/create/{$model->vendor_public_id}") . '?paymentTypeId=1';
}
]
];
}
}

View File

@ -0,0 +1,56 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateExpensesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::dropIfExists('expenses');
Schema::create('expenses', function(Blueprint $table)
{
$table->increments('id');
$table->timestamps();
$table->unsignedInteger('account_id')->index();
$table->unsignedInteger('vendor_id')->nullable();
$table->unsignedInteger('user_id');
$table->softDeletes();
$table->boolean('is_deleted')->default(false);
$table->decimal('amount', 13, 2);
$table->decimal('amountcur', 13, 2);
$table->decimal('exchange_rate', 13, 2);
$table->decimal('balance', 13, 2);
$table->date('expense_date')->nullable();
$table->string('expense_number')->nullable();
$table->text('private_notes');
$table->integer('currency_id',false, true)->nullable();
$table->boolean('is_invoiced')->default(false);
$table->boolean('should_be_invoiced')->default(true);
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');;
$table->unsignedInteger('public_id')->index();
$table->unique( array('account_id','public_id') );
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('expenses');
}
}

View File

@ -30298,6 +30298,17 @@ function getClientDisplayName(client)
return '';
}
function getVendorDisplayName(vendor)
{
var contact = vendor.contacts ? vendor.vendorcontacts[0] : false;
if (vendor.name) {
return vendor.name;
} else if (contact) {
return getContactDisplayName(contact);
}
return '';
}
function populateInvoiceComboboxes(clientId, invoiceId) {
var clientMap = {};
var invoiceMap = {};

View File

@ -893,6 +893,13 @@ return array(
'activity_28' => ':user restored :credit credit',
'activity_29' => ':contact approved quote :quote',
'activity_30' => ':user created :vendor',
'activity_31' => ':user created :vendor',
'activity_32' => ':user created :vendor',
'activity_33' => ':user created :vendor',
'activity_34' => ':user created :vendor',
'activity_35' => ':user created :vendor',
'activity_36' => ':user created :vendor',
'activity_37' => ':user created :vendor',
'payment' => 'Payment',
'system' => 'System',
@ -1018,4 +1025,9 @@ return array(
'archive_vendor' => 'Archive vendor',
'delete_vendor' => 'Delete vendor',
'view_vendor' => 'View vendor',
// Expenses
'expense_amount' => 'Expense amount',
'expense_balance' => 'Expense balance',
'expense_date' => 'Expense date',
);