Compare commits

...

8 Commits

Author SHA1 Message Date
roshelrao 945724ae3c invoice changes 2021-07-19 13:46:12 +05:30
ddave 72c8b4774c Adding the action manager for the extension 2021-07-16 08:17:30 +02:00
roshelrao 9a1ee350c3 Invoice PDF builder 2021-07-14 13:10:39 +05:30
ddave 7228bb2922 Pdf builder for invoices 2021-07-09 11:24:04 +02:00
roshelrao 5416bdb840 added missing fields 2021-07-08 15:03:32 +05:30
roshelrao 1332448d72 invoice module 2021-07-01 15:51:04 +05:30
Thilina 9f47df5613 Ability to compile extensions 2021-06-29 11:37:46 +02:00
Thilina 731073a133 Merge tag 'v30.0.0.OS' into develop
v30.0.0.OS
2021-06-28 11:48:07 +02:00
30 changed files with 423443 additions and 34137 deletions
+49
View File
@@ -0,0 +1,49 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003
},
{
"name": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 0,
"runtimeArgs": [
"-dxdebug.start_with_request=yes"
],
"env": {
"XDEBUG_MODE": "debug,develop",
"XDEBUG_CONFIG": "client_port=${port}"
}
},
{
"name": "Launch Built-in web server",
"type": "php",
"request": "launch",
"runtimeArgs": [
"-dxdebug.mode=debug",
"-dxdebug.start_with_request=yes",
"-S",
"localhost:0"
],
"program": "",
"cwd": "${workspaceRoot}",
"port": 9003,
"serverReadyAction": {
"pattern": "Development Server \\(http://localhost:([0-9]+)\\) started",
"uriFormat": "http://localhost:%s",
"action": "openExternally"
}
}
]
}
+31
View File
@@ -0,0 +1,31 @@
<?php
use Classes\ExtensionManager;
use Utils\LogManager;
if (!isset($extensionIndex)) {
exit();
}
define('MODULE_PATH',APP_BASE_PATH.'extensions/'.$moduleName);
include APP_BASE_PATH.'header.php';
$extensionManager = new ExtensionManager();
$meta = $extensionManager->getExtensionMetaData($moduleName);
if (!$meta) {
LogManager::getInstance()->error("Extension metadata.json not found for $moduleName");
exit();
}
if ($meta->headless) {
LogManager::getInstance()->error("Extension running in headless mode for $moduleName");
exit();
}
?>
<script type="text/javascript" src="<?=BASE_URL.'dist/vendorReact.js'?>?v=<?=$jsVersion?>"></script>
<script type="text/javascript" src="<?=BASE_URL.'dist/vendorAntd.js'?>?v=<?=$jsVersion?>"></script>
<script type="text/javascript" src="<?=BASE_URL.'dist/vendorAntdIcons.js'?>?v=<?=$jsVersion?>"></script>
<script type="text/javascript" src="<?=BASE_URL.'dist/vendorAntv.js'?>?v=<?=$jsVersion?>"></script>
<script type="text/javascript" src="<?=BASE_URL.'dist/vendorOther.js'?>?v=<?=$jsVersion?>"></script>
<script type="text/javascript" src="<?=EXTENSIONS_URL.$moduleName.'/dist/'.$moduleName.'.js'?>?v=<?=$jsVersion?>"></script>
<?php
include $extensionIndex;
include APP_BASE_PATH.'footer.php';
?>
+3 -3
View File
@@ -2,7 +2,7 @@
namespace Classes\Pdf; namespace Classes\Pdf;
use Forms\Common\EmployeeFormPDFBuilder; use Invoices\InvoicePDFBuilder;
class PDFRegister class PDFRegister
{ {
@@ -10,8 +10,8 @@ class PDFRegister
public static function init() public static function init()
{ {
self::put('empf', function ($data) { self::put('invoice', function ($data) {
return new EmployeeFormPDFBuilder($data); return new InvoicePDFBuilder($data);
}); });
} }
+1
View File
@@ -0,0 +1 @@
git keep
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
<?php
require_once __DIR__.'/src/Invoices/Extension.php';
require_once __DIR__.'/src/Invoices/Manager.php';
require_once __DIR__.'/src/Invoices/Migration.php';
require_once __DIR__.'/src/Invoices/Model/Invoice.php';
+14
View File
@@ -0,0 +1,14 @@
{
"label": "Invoices",
"menu": ["Invoices", "fa-file"],
"icon": "fa-file-invoice",
"user_levels": [
"Admin",
"Manager",
"User"
],
"model_namespace": "\\Invoices\\Model",
"manager": "\\Invoices\\Extension",
"action": "\\Invoices\\Manager",
"headless": false
}
@@ -0,0 +1,30 @@
<?php
namespace Invoices;
use Classes\IceExtension;
class Extension extends IceExtension
{
public function install() {
$migration = new Migration();
return $migration->up();
}
public function uninstall() {
$migration = new Migration();
return $migration->down();
}
public function setupModuleClassDefinitions()
{
$this->addModelClass('Invoice');
}
public function setupRestEndPoints()
{
}
}
@@ -0,0 +1,55 @@
<?php
namespace Invoices;
use Classes\Authorizable;
use Classes\BaseService;
use Classes\IceResponse;
use Classes\Pdf\PdfBuilder;
use Invoices\Model\Invoice;
use Invoices\Pdf\InvoicePdf;
class InvoicePDFBuilder implements Authorizable, PdfBuilder
{
protected $formId;
public function __construct($formId)
{
$this->formId = $formId;
}
public function granted(): bool
{
$empForm = new EmployeeForm();
$empForm->Load("id = ?", array($this->formId));
$currentEmployeeId = BaseService::getInstance()->getCurrentProfileId();
$user = BaseService::getInstance()->getCurrentUser();
return ($currentEmployeeId == $empForm->employee
|| $user->user_level === 'Admin'
|| BaseService::getInstance()->isSubordinateEmployee($currentEmployeeId, $empForm->employee)
);
}
public function createPdf()
{
$response = FormsActionManager::getFormDataById($this->formId);
if ($response->getStatus() === IceResponse::ERROR) {
return null;
}
$form = $response->getData()['form'];
$invoiceForm = $response->getData()['data'];
$invoice = new Invoice();
$invoice->Load('id = ?', [$invoiceForm->invoice]);
//$employee = FileService::getInstance()->updateSmallProfileImage($employee);
$pdf = new InvoicePdf($invoice);
$pdf->initialize($form->name);
$pdf->process();
return $pdf;
}
}
@@ -0,0 +1,15 @@
<?php
namespace Invoices;
use Classes\IceResponse;
use Classes\SubActionManager;
class Manager extends SubActionManager
{
public function printInvoice($req)
{
$id = $req->id;
return new IceResponse(IceResponse::SUCCESS, true);
}
}
@@ -0,0 +1,53 @@
<?php
namespace Invoices;
use Classes\Migration\AbstractMigration;
class Migration extends AbstractMigration
{
public function up()
{
$sql = <<<'SQL'
create table Invoices
(
id bigint auto_increment primary key,
paymentId bigint not null,
invoiceId bigint not null,
description varchar(500) charset utf8 not null,
buyerName varchar(200) charset utf8 not null,
buyerAddress varchar(200) charset utf8 not null,
buyerPostalCode varchar(200) charset utf8 not null,
buyerCountry varchar(200) charset utf8 not null,
buyerVatId varchar(50) charset utf8 not null,
sellerName varchar(200) charset utf8 not null,
sellerAddress varchar(200) null,
sellerCountry varchar(200) charset utf8 not null,
sellerVatId varchar(50) charset utf8 not null,
amount decimal(10,2) default 0.00 null,
vat decimal(10,2) default 0.00 null,
vatRate decimal(10,2) default 0.00 null,
issuedDate datetime null,
dueDate datetime null,
paidDate datetime null,
created datetime null,
updated datetime null,
status enum('Pending', 'Paid', 'Processing', 'Draft', 'Sent', 'Canceled') collate utf8_unicode_ci default 'Pending' null,
acceptPayments tinyint default 0 null,
buyerEmail varchar(125) charset utf8 null,
items text charset utf8 null,
constraint invoiceId
unique (invoiceId)
)
collate=utf8mb4_unicode_ci;
SQL;
return $this->executeQuery($sql);
}
public function down()
{
$sql = <<<'SQL'
DROP TABLE IF EXISTS `Invoices`;
SQL;
return $this->executeQuery($sql);
}
}
@@ -0,0 +1,23 @@
<?php
namespace Invoices\Model;
use Classes\ModuleAccess;
use Model\BaseModel;
class Invoice extends BaseModel
{
public $table = 'Invoices';
public function getAdminAccess()
{
return array("get","element","save","delete");
}
public function getModuleAccess()
{
return [
new ModuleAccess('employees', 'admin'),
];
}
}
@@ -0,0 +1,42 @@
<?php
namespace Invoices\Pdf;
use Classes\Pdf\BasePdfTemplate;
use Invoices\Model\Invoice;
class InvoicePdf extends BasePdfTemplate
{
protected $invoice;
public function __construct(Invoice $invoice)
{
parent::__construct();
$this->invoice = $invoice;
}
public function process()
{
$this->addH3($this->form->name, 'B', 'L');
$this->addBorderedText($this->invoice->description);
}
// protected function addFormItems()
// {
// $signatures = [];
// $fields = json_decode($this->form->items);
// foreach ($fields as $field) {
// if ($field->field_type === 'signature') {
// $signatures[$field->field_label] = $this->employeeForm->{$field->name};
// } elseif ($field->field_type === 'fileupload') {
// // ignore
// } else {
// $this->addKeyValue($field->field_label, $this->employeeForm->{$field->name}, $field->field_type);
// }
// }
//
// foreach ($signatures as $name => $data) {
// $this->addSignature($name, $data);
// }
// }
}
+38
View File
@@ -0,0 +1,38 @@
<?php
/*$user = \Classes\BaseService::getInstance()->getCurrentUser();
echo "Welcome ".$user->username."<br/>";
echo "Invoices <br/>";
*/
use Classes\PermissionManager;
use Invoices\Model\Invoice;
?><div class="span9">
<ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">
<li class="active"><a id="tabInvoices" href="#tabPageInvoices"><?=t('Invoices')?></a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tabPageInvoices">
<div id="InvoicesTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
<div id="InvoicesForm"></div>
<div id="InvoicesFilterForm"></div>
</div>
</div>
</div>
<div id="dataGroup"></div>
<?php
$moduleData = [
'user_level' => $user->user_level,
'permissions' => [
'Invoice' => PermissionManager::checkGeneralAccess(new Invoice()),
]
];
?>
<script>
initAdminInvoices(<?=json_encode($moduleData)?>);
</script>
+17
View File
@@ -0,0 +1,17 @@
import {InvoiceAdapter} from './lib';
import IceDataPipe from '../../../../web/api/IceDataPipe';
function init(data) {
const modJsList = [];
modJsList.tabInvoices =new InvoiceAdapter('Invoice', 'Invoices','','');
modJsList.tabInvoices.setObjectTypeName('Invoice');
modJsList.tabInvoices.setDataPipe(new IceDataPipe(modJsList.tabInvoices));
modJsList.tabInvoices.setAccess(data.permissions.Invoice);
window.modJs = modJsList.tabInvoices;
window.modJsList = modJsList;
}
window.initAdminInvoices = init;
+236
View File
@@ -0,0 +1,236 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Space, Tag } from 'antd';
import ReactModalAdapterBase from '../../../../web/api/ReactModalAdapterBase';
import {
EditOutlined, DeleteOutlined, CopyOutlined, MonitorOutlined, PrinterOutlined,
} from '@ant-design/icons';
/**
* VatInvoiceAdapter
*/
class InvoiceAdapter extends ReactModalAdapterBase {
getDataMapping() {
return [
'id',
'paymentId',
'invoiceId',
'description',
'buyerName',
'buyerAddress',
'buyerPostalAddress',
'buyerVatId',
'buyerEmail',
'sellerName',
'sellerAddress',
'sellerVatId',
'amount',
'vat',
'vatRate',
'issuedDate',
'dueDate',
'paidDate',
'status',
'acceptPayments',
'created',
'updated',
'link',
'paymentLink'
];
}
getHeaders() {
return [
{ sTitle: 'ID', bVisible: false },
{ sTitle: 'Payment Id' },
{ sTitle: 'Invoice ID' },
{ sTitle: 'Description' },
{ sTitle: 'Buyer Name' },
{ sTitle: 'Buyer Address' },
{ sTitle: 'Buyer Postal Code' },
{ sTitle: 'Buyer Country' },
{ sTitle: 'Buyer Vat Id' },
{ sTitle: 'Buyer Email' },
{ sTitle: 'Seller Name' },
{ sTitle: 'Seller Country' },
{ sTitle: 'Seller Vat Id' },
{ sTitle: 'Amount' },
{ sTitle: 'Vat' },
{ sTitle: 'Vat Rate' },
{ sTitle: 'Issued Date' },
{ sTitle: 'Paid Date' },
{ sTitle: 'Status' },
{ sTitle: 'Accept Payments' },
{ sTitle: 'Created' },
{ sTitle: 'Updated' },
{ sTitle: 'Link' },
{ sTitle: 'Payment Link' },
];
}
getCountryList() {
return [
['DE', 'Germany'],
['LK', 'Sri Lanka'],
];
}
getFormFields() {
return [
[ 'id', {"label":"ID","type":"hidden"}],
[ 'paymentId', {"label":"Payment Id","type":"text","validation":"int"}],
[ 'invoiceId', {"label":"Invoice Id","type":"text","validation":"int"}],
[ 'description', {"label":"Description","type":"textarea","validation":"none"}],
[ 'buyerName', {"label":"Buyer Name","type":"text"}],
[ 'buyerAddress', {"label":"Buyer Address","type":"textarea"}],
[ 'buyerPostalCode', {"label":"Buyer Postal Code","type":"text"}],
[ 'buyerCountry', {"label":"Buyer Country","type":"select2", "remote-source": ["Country", "code", "name"]}],
[ 'buyerVatId', {"label":"Buyer Vat Id","type":"text","validation":"none"}],
[ 'buyerEmail', {"label":"Buyer Email","type":"text","validation":"email"}],
[ 'sellerName', {"label":"Seller Name","type":"text"}],
[ 'sellerAddress', {"label":"Seller Address","type":"text"}],
[ 'sellerCountry', {"label":"Seller Country","type":"select2", "remote-source": ["Country", "code", "name"]}],
[ 'sellerVatId', {"label":"Seller Vat Id","type":"text"}],
[ 'amount', {"label":"Amount with VAT","type":"text", "validation":"float"}],
[ 'vat', {"label":"Vat","type":"text", "validation":"float"}],
[ 'vatRate', {"label":"Vat Rate","type":"text", "validation":"float"}],
[ 'issuedDate', {"label":"Issued Date","type":"datetime", "validation":""}],
[ 'dueDate', {"label":"Due Date","type":"datetime", "validation":""}],
[ 'paidDate', {"label":"Paid Date","type":"datetime", "validation":""}],
[ 'status', {"label":"Status","type":"select","source":[["Pending","Pending"],["Paid","Paid"],["Processing","Processing"],["Draft","Draft"],["Sent","Sent"],["Canceled","Canceled"]]}],
[ 'acceptPayments', {"label":"Accept Payments","type":"select","source":[["0","No"],["1","Yes"]]}],
[ 'created', {"label":"Created","type":"datetime", "validation":""}],
[ 'updated', {"label":"Updated","type":"datetime", "validation":""}],
[ 'link', {"label":"Link","type":"placeholder"}],
[ 'paymentLink', {"label":"Payment Link","type":"placeholder"}],
['items', {
label: 'Items',
type: 'datagroup',
form: [
['description', { label: 'Description', type: 'textarea', validation: '' }],
['rate', { label: 'Rate', type: 'text', validation: '' }],
['qty', { label: 'Quantity', type: 'text', validation: '' }],
['lineTotal', { label: 'Line Total', type: 'text', validation: '' }],
],
html: '<div id="#_id_#" class="panel panel-default"><div class="panel-body">#_delete_##_edit_#<span style="color:#999;font-size:13px;font-weight:bold">Date: #_date_#</span><hr/>#_note_#</div></div>',
validation: 'none',
columns: [
{
title: 'Description',
dataIndex: 'description',
key: 'description',
},
{
title: 'Rate',
dataIndex: 'rate',
key: 'rate',
},
{
title: 'Quantity',
dataIndex: 'qty',
key: 'qty',
},
{
title: 'Line Total',
dataIndex: 'lineTotal',
key: 'lineTotal',
},
],
'sort-function': function (a, b) {
const t1 = Date.parse(a.date).getTime();
const t2 = Date.parse(b.date).getTime();
return (t1 < t2);
},
'custom-validate-function': function (data) {
const res = {};
res.valid = true;
data.date = new Date().toString('d-MMM-yyyy hh:mm tt');
res.params = data;
return res;
},
}],
];
}
getTableColumns() {
return [
{
title: 'Invoice Id',
dataIndex: 'invoiceId',
sorter: true,
},
{
title: 'Description',
dataIndex: 'description',
sorter: true,
},
];
}
getTableActionButtonJsx(adapter) {
return (text, record) => (
<Space size="middle">
{adapter.hasAccess('save') && adapter.showEdit
&& (
<Tag color="green" onClick={() => modJs.edit(record.id)} style={{ cursor: 'pointer' }}>
<EditOutlined />
{` ${adapter.gt('Edit')}`}
</Tag>
)}
{adapter.hasAccess('element')
&& (
<Tag color="blue" onClick={() => modJs.viewElement(record.id)} style={{ cursor: 'pointer' }}>
<MonitorOutlined />
{` ${adapter.gt('View')}`}
</Tag>
)}
{adapter.hasAccess('delete') && adapter.showDelete
&& (
<Tag color="volcano" onClick={() => modJs.deleteRow(record.id)} style={{ cursor: 'pointer' }}>
<DeleteOutlined />
{` ${adapter.gt('Delete')}`}
</Tag>
)}
{adapter.hasAccess('save')
&& (
<Tag color="cyan" onClick={() => modJs.copyRow(record.id)} style={{ cursor: 'pointer' }}>
<CopyOutlined />
{` ${adapter.gt('Copy')}`}
</Tag>
)}
<Tag color="green" onClick={() => modJs.printInvoice(record.id)} style={{ cursor: 'pointer' }}>
<PrinterOutlined />
{` ${adapter.gt('Print')}`}
</Tag>
</Space>
);
}
printInvoice(id) {
const params = {};
params.id = id;
const reqJson = JSON.stringify(params);
const callBackData = [];
callBackData.callBackData = [];
callBackData.callBackSuccess = 'printInvoiceSuccessCallback';
callBackData.callBackFail = 'printInvoiceFailCallback';
this.customAction('printInvoice', 'extension=invoices', reqJson, callBackData);
}
printInvoiceSuccessCallback(callBackData) {
this.showMessage('Success', 'Printing Done');
this.get([]);
}
printInvoiceFailCallback(callBackData) {
this.showMessage('Error', callBackData);
}
}
module.exports ={InvoiceAdapter};
+41
View File
@@ -361,6 +361,47 @@ gulp.task('modules-js', (done) => {
.pipe(gulp.dest('./web/dist')); .pipe(gulp.dest('./web/dist'));
}); });
gulp.task('extension-js', (done) => {
let extension = process.argv.filter((item) => item.substr(0, 3) === '--x');
if (extension.length === 1) {
extension = extension[0].substr(3);
}
// map them to our stream function
return browserify({
entries: [`extensions/${extension}/web/js/index.js`],
basedir: '.',
debug: true,
cache: {},
packageCache: {},
})
.external(vendorsFlat)
.transform('babelify', {
plugins: [
['@babel/plugin-proposal-class-properties', { loose: true }],
],
presets: ['@babel/preset-env', '@babel/preset-react'],
extensions: ['.js', '.jsx'],
})
.transform(require('browserify-css'))
.bundle()
.pipe(source(`${extension}.js`))
.pipe(buffer())
.pipe(ifElse(!isProduction, () => sourcemaps.init({ loadMaps: true })))
.pipe(ifElse(isProduction, () => uglifyes(
{
compress: true,
mangle: {
reserved: [],
},
},
)))
.pipe(ifElse(isProduction, () => javascriptObfuscator({
compact: true,
})))
.pipe(ifElse(!isProduction, () => sourcemaps.write('./')))
.pipe(gulp.dest(`./extensions/${extension}/dist`));
});
gulp.task('watch', () => { gulp.task('watch', () => {
gulp.watch('web/admin/src/*/*.js', gulp.series('admin-js')); gulp.watch('web/admin/src/*/*.js', gulp.series('admin-js'));
+1
View File
@@ -105,6 +105,7 @@ class UserAdapter extends AdapterBase {
if (msg == null) { if (msg == null) {
const id = $(`#${this.getTableName()}_submit #id`).val(); const id = $(`#${this.getTableName()}_submit #id`).val();
params.csrf = $(`#${this.getTableName()}Form`).data('csrf'); params.csrf = $(`#${this.getTableName()}Form`).data('csrf');
if (id != null && id !== undefined && id !== '') { if (id != null && id !== undefined && id !== '') {
params.id = id; params.id = id;
this.add(params, []); this.add(params, []);
+1 -1
View File
@@ -453,7 +453,7 @@ class AdapterBase extends ModuleBase {
$.getJSON(this.moduleRelativeURL, { $.getJSON(this.moduleRelativeURL, {
t: this.table, a: 'ca', sa: subAction, mod: module, req: request, t: this.table, a: 'ca', sa: subAction, mod: module, req: request,
}, (data) => { }, (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
callBackData.callBackData.push(data.data); callBackData.callBackData.push(data.data);
that.callFunction(callBackData.callBackSuccess, callBackData.callBackData); that.callFunction(callBackData.callBackSuccess, callBackData.callBackData);
} else { } else {
+47033 -4
View File
File diff suppressed because one or more lines are too long
+531 -1
View File
File diff suppressed because one or more lines are too long
+10479 -4
View File
File diff suppressed because one or more lines are too long
+37479 -5
View File
File diff suppressed because one or more lines are too long
+34095 -34095
View File
File diff suppressed because one or more lines are too long
+91750 -5
View File
File diff suppressed because one or more lines are too long
+39547 -2
View File
File diff suppressed because one or more lines are too long
+92733 -5
View File
File diff suppressed because one or more lines are too long
+15464 -2
View File
File diff suppressed because one or more lines are too long
+28918 -10
View File
File diff suppressed because one or more lines are too long