Compare commits

..

No commits in common. "dev" and "6492-replaceAgencyVolume" have entirely different histories.

26 changed files with 235 additions and 85 deletions

1
.gitignore vendored
View File

@ -1,6 +1,5 @@
node_modules node_modules
build/ build/
dist/
config.my.php config.my.php
.vscode/ .vscode/
.quasar .quasar

View File

@ -28,7 +28,7 @@ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
# Hedera # Hedera
RUN curl -sL https://apt.verdnatura.es/conf/verdnatura.gpg | tee /etc/apt/trusted.gpg.d/verdnatura.gpg \ RUN curl -sL https://apt.verdnatura.es/conf/verdnatura.gpg | apt-key add - \
&& echo "deb http://apt.verdnatura.es/ bookworm main" \ && echo "deb http://apt.verdnatura.es/ bookworm main" \
> /etc/apt/sources.list.d/vn.list \ > /etc/apt/sources.list.d/vn.list \
&& apt-get update \ && apt-get update \

73
Jenkinsfile vendored
View File

@ -4,12 +4,20 @@ def BRANCH_ENV = [
test: 'test', test: 'test',
master: 'production' master: 'production'
] ]
def remote = [:]
node { node {
stage('Setup') { stage('Setup') {
env.MAIN_REPLICAS = 1
env.CRON_REPLICAS = 0
env.NODE_ENV = BRANCH_ENV[env.BRANCH_NAME] ?: 'dev' env.NODE_ENV = BRANCH_ENV[env.BRANCH_NAME] ?: 'dev'
switch (env.BRANCH_NAME) {
case 'master':
env.MAIN_REPLICAS = 3
env.CRON_REPLICAS = 1
break
}
echo "NODE_NAME: ${env.NODE_NAME}" echo "NODE_NAME: ${env.NODE_NAME}"
echo "WORKSPACE: ${env.WORKSPACE}" echo "WORKSPACE: ${env.WORKSPACE}"
} }
@ -18,6 +26,7 @@ pipeline {
agent any agent any
environment { environment {
PROJECT_NAME = 'hedera-web' PROJECT_NAME = 'hedera-web'
STACK_NAME = "${env.PROJECT_NAME}-${env.BRANCH_NAME}"
} }
stages { stages {
stage('Debuild') { stage('Debuild') {
@ -29,28 +38,35 @@ pipeline {
} }
agent { agent {
docker { docker {
image 'registry.verdnatura.es/verdnatura/debuild:2.23.4-vn7' image 'registry.verdnatura.es/debuild:2.23.4-vn1'
registryUrl 'https://registry.verdnatura.es/' registryUrl 'https://registry.verdnatura.es/'
registryCredentialsId 'docker-registry' registryCredentialsId 'docker-registry'
args '-v /mnt/appdata/reprepro:/reprepro'
} }
} }
steps { steps {
sh 'debuild -us -uc -b' sh 'debuild -us -uc -b'
sh 'mkdir -p debuild' sh 'vn-includedeb bookworm'
sh 'mv ../hedera-web_* debuild' }
}
stage('Container') {
when {
anyOf {
branch 'master'
branch 'test'
}
}
environment {
CREDS = credentials('docker-registry')
}
steps {
script { script {
def files = findFiles(glob: 'debuild/*.changes') def packageJson = readJSON file: 'package.json'
files.each { file -> env.CHANGES_FILE = file.name } env.VERSION = packageJson.version
}
configFileProvider([
configFile(fileId: "dput.cf", variable: 'DPUT_CONFIG')
]) {
sshagent(credentials: ['jenkins-agent']) {
sh 'dput --config "$DPUT_CONFIG" verdnatura "debuild/$CHANGES_FILE"'
}
} }
sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY'
sh 'docker-compose build --build-arg BUILD_ID=$BUILD_ID --parallel'
sh 'docker-compose push'
} }
} }
stage('Deploy') { stage('Deploy') {
@ -61,35 +77,14 @@ pipeline {
} }
} }
environment { environment {
CREDS = credentials('docker-registry') DOCKER_HOST = "${env.SWARM_HOST}"
IMAGE = "$REGISTRY/verdnatura/hedera-web"
} }
steps { steps {
script { script {
def packageJson = readJSON file: 'package.json' def packageJson = readJSON file: 'package.json'
env.VERSION = "${packageJson.version}" env.VERSION = packageJson.version
env.TAG = "${packageJson.version}-build${env.BUILD_ID}"
}
sh 'docker-compose build --build-arg BUILD_ID=$BUILD_ID --parallel'
sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY'
sh 'docker push $IMAGE:$TAG'
script {
if (env.BRANCH_NAME == 'master') {
sh 'docker tag $IMAGE:$TAG $IMAGE:latest'
sh 'docker push $IMAGE:latest'
}
}
withKubeConfig([
serverUrl: "$KUBERNETES_API",
credentialsId: 'kubernetes',
namespace: 'salix'
]) {
sh 'kubectl set image deployment/hedera-web-$BRANCH_NAME hedera-web-$BRANCH_NAME=$IMAGE:$TAG'
sh 'kubectl set image deployment/hedera-web-cron-$BRANCH_NAME hedera-web-cron-$BRANCH_NAME=$IMAGE:$TAG'
} }
sh "docker stack deploy --with-registry-auth --compose-file docker-compose.yml ${env.STACK_NAME}"
} }
} }
} }

View File

@ -5,12 +5,9 @@ Hedera is the main web shop page for Verdnatura.
## Prerequisites ## Prerequisites
Required applications. Required applications.
* PHP >= 8.4 * PHP >= 7.0
* Node.js >= 20.0 * Node.js >= 8.0
* [php-vn-lib](https://gitea.verdnatura.es/verdnatura/php-vn-lib)
Take a look to *debian/control* file to see additional dependencies.
Copy config.php to *config.my.php* and place your DB config there.
### Installing dependencies and launching ### Installing dependencies and launching

View File

@ -24,7 +24,7 @@ return [
,'port' => 3306 ,'port' => 3306
,'schema' => 'hedera' ,'schema' => 'hedera'
,'user' => 'hedera-web' ,'user' => 'hedera-web'
,'pass' => '' // base64 encoded ,'pass' => ''
,'tz' => 'Europe/madrid' ,'tz' => 'Europe/madrid'
] ]
]; ];

2
debian/changelog vendored
View File

@ -1,4 +1,4 @@
hedera-web (25.4.4) stable; urgency=low hedera-web (24.8.2) stable; urgency=low
* Initial Release. * Initial Release.

1
debian/cron.d vendored
View File

@ -3,6 +3,7 @@ MAILTO=webmaster
*/2 * * * * root hedera-web.php -m edi/load */2 * * * * root hedera-web.php -m edi/load
0 23 * * * root hedera-web.php -m edi/clean 0 23 * * * root hedera-web.php -m edi/clean
0 5 * * * root hedera-web.php -m edi/update 0 5 * * * root hedera-web.php -m edi/update
0 5 * * * root hedera-web.php -m misc/exchange-rate
0 0 * * * root hedera-web.php -m image/sync 0 0 * * * root hedera-web.php -m image/sync
0 1 * * * root /usr/share/hedera-web/utils/image-clean.sh > /dev/null 0 1 * * * root /usr/share/hedera-web/utils/image-clean.sh > /dev/null
0 */1 * * * root /usr/share/hedera-web/utils/update-browscap.sh > /dev/null 0 */1 * * * root /usr/share/hedera-web/utils/update-browscap.sh > /dev/null

View File

@ -1,9 +1,47 @@
version: '3.7' version: '3.7'
services: services:
main: main:
image: registry.verdnatura.es/verdnatura/hedera-web:${TAG:?} image: registry.verdnatura.es/hedera-web:${VERSION:?}
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
args: args:
- VERSION=${VERSION:?} - VERSION=${VERSION:?}
ports:
- 80
configs:
- source: config
target: /etc/hedera-web/config.my.php
volumes:
- /mnt/appdata:/mnt/storage
- /mnt/appdata/image:/var/lib/hedera-web/image-db
- /mnt/appdata/vn-access:/var/lib/hedera-web/vn-access
deploy:
replicas: ${MAIN_REPLICAS:?}
placement:
constraints:
- node.role == worker
resources:
limits:
memory: 2G
cron:
image: registry.verdnatura.es/hedera-web:${VERSION:?}
command: 'cron -f'
configs:
- source: config
target: /etc/hedera-web/config.my.php
volumes:
- /mnt/appdata:/mnt/storage
- /mnt/appdata/image:/var/lib/hedera-web/image-db
deploy:
replicas: ${CRON_REPLICAS:?}
placement:
constraints:
- node.role == worker
resources:
limits:
memory: 1G
configs:
config:
external: true
name: ${PROJECT_NAME:?}-${BRANCH_NAME:?}

View File

@ -65,8 +65,8 @@
one-way="true" one-way="true"
one-time="true"> one-time="true">
<db-model property="model"> <db-model property="model">
SELECT id, name FROM vn.country SELECT id, country FROM vn.country
ORDER BY name ORDER BY country
</db-model> </db-model>
</htk-combo> </htk-combo>
</div> </div>

View File

@ -18,8 +18,8 @@
LEFT JOIN image im LEFT JOIN image im
ON im.collectionFk = 'catalog' ON im.collectionFk = 'catalog'
AND im.name = i.image AND im.name = i.image
WHERE i.isActive WHERE i.longName LIKE CONCAT('%', #search, '%')
AND (i.longName LIKE CONCAT('%', #search, '%') OR i.id = #search) OR i.id = #search
ORDER BY i.longName LIMIT 50 ORDER BY i.longName LIMIT 50
</db-model> </db-model>
<custom> <custom>

View File

@ -275,7 +275,7 @@
} }
& > .htk-image { & > .htk-image {
width: 100%; width: 100%;
height: 210px; height: 180px;
& > img { & > img {
height: initial; height: initial;

View File

@ -117,7 +117,7 @@
SELECT i.id, i.longName item, i.subName, SELECT i.id, i.longName item, i.subName,
i.tag5, i.value5, i.tag6, i.value6, i.tag5, i.value5, i.tag6, i.value6,
i.tag7, i.value7, i.tag8, i.value8, i.tag7, i.value7, i.tag8, i.value8,
i.relevancy, i.size, i.category, b.minQuantity, i.relevancy, i.size, i.category, i.minQuantity,
k.name ink, p.name producer, o.name origin, k.name ink, p.name producer, o.name origin,
b.available, b.price, b.`grouping`, b.available, b.price, b.`grouping`,
i.image, im.updated i.image, im.updated
@ -126,13 +126,19 @@
LEFT JOIN vn.ink k ON k.id = i.inkFk LEFT JOIN vn.ink k ON k.id = i.inkFk
LEFT JOIN vn.producer p ON p.id = i.producerFk LEFT JOIN vn.producer p ON p.id = i.producerFk
LEFT JOIN vn.origin o ON o.id = i.originFk LEFT JOIN vn.origin o ON o.id = i.originFk
LEFT JOIN image im ON im.collectionFk = 'catalog' LEFT JOIN image im
ON im.collectionFk = 'catalog'
AND im.name = i.image AND im.name = i.image
WHERE b.available > 0 WHERE b.available > 0
ORDER BY i.relevancy DESC, i.name, i.size ORDER BY i.relevancy DESC, i.name, i.size
LIMIT 5000; LIMIT 5000;
DROP TEMPORARY TABLE tmp.item; DROP TEMPORARY TABLE
CALL vn.ticketCalculatePurge(); tmp.item,
tmp.ticketCalculateItem,
tmp.ticketComponentPrice,
tmp.ticketComponent,
tmp.ticketLot,
tmp.zoneGetShipped;
</db-model> </db-model>
<db-form id="$card" v-model="card" model="items"/> <db-form id="$card" v-model="card" model="items"/>
<vn-lot id="card-lot"/> <vn-lot id="card-lot"/>

View File

@ -120,7 +120,7 @@
form-id="iter" form-id="iter"
on-change="onAddressChange"> on-change="onAddressChange">
<db-model property="model" id="addresses"> <db-model property="model" id="addresses">
SELECT a.id, a.nickname, p.name province, a.city, a.street, a.isActive, c.name SELECT a.id, a.nickname, p.name province, a.city, a.street, a.isActive, c.country
FROM myAddress a FROM myAddress a
LEFT JOIN vn.province p ON p.id = a.provinceFk LEFT JOIN vn.province p ON p.id = a.provinceFk
JOIN vn.country c ON c.id = p.countryFk JOIN vn.country c ON c.id = p.countryFk

View File

@ -67,7 +67,7 @@ export default new Class({
methods = ['balance']; methods = ['balance'];
selectedMethod = 'BALANCE'; selectedMethod = 'BALANCE';
} else { } else {
methods = ['card']; methods = ['card', 'transfer', 'later'];
if (!creditExceededCond) { if (!creditExceededCond) {
methods.push('credit'); methods.push('credit');
@ -98,6 +98,9 @@ export default new Class({
case 'CARD': case 'CARD':
id = 'cardMethod'; id = 'cardMethod';
break; break;
case 'TRANSFER':
id = 'transferMethod';
break;
default: default:
id = null; id = null;
} }

View File

@ -162,6 +162,32 @@
<t>You will be redirected to the payment.</t> <t>You will be redirected to the payment.</t>
</div> </div>
</div> </div>
<div id="transfer-method">
<label>
<htk-radio radio-group="pay-method" value="TRANSFER"/>
<t>Bank Transfer</t>
</label>
<div>
<t>Make a transfer to one account.</t>
<htk-repeater form-id="iter">
<db-model property="model">
SELECT name, iban FROM mainAccountBank
</db-model>
<custom>
<div class="transfer-account">
<p>{{iter.name}}</p>
<p>{{iter.iban}}</p>
</div>
</custom>
</htk-repeater>
</div>
</div>
<div id="later-method">
<label>
<htk-radio radio-group="pay-method" value="LATER"/>
<t>Pay later</t>
</label>
</div>
</div> </div>
</div> </div>
<div class="button-bar vn-mt-md"> <div class="button-bar vn-mt-md">

View File

@ -3,17 +3,30 @@ import './style.scss';
export default new Class({ export default new Class({
Extends: Hedera.Form, Extends: Hedera.Form,
Template: require('./ui.xml'), Template: require('./ui.xml'),
donwloadRenderer(column, invoice) {
var invoiceId = invoice.$.id;
onDownloadClick(column, value, row) { if (invoice.$.hasPdf && invoiceId) {
var model = this.$.invoices; var params = {
var hasPdf = model.get(row, 'hasPdf'); srv: 'rest:dms/invoice',
var id = model.get(row, 'id'); invoice: invoiceId,
access_token: this.conn.token
};
if (hasPdf && id) { Object.assign(column, {
let params = Vn.Url.makeUri({ access_token: this.conn.token }); tip: _('Download PDF'),
window.open(`/api/InvoiceOuts/${id}/download?${params}`); disabled: false,
icon: 'download',
href: '?'+ Vn.Url.makeUri(params)
});
} else } else
Htk.Toast.showWarning(_('Request the invoice to your salesperson')); Object.assign(column, {
tip: _('Request the invoice to your salesperson'),
disabled: true,
icon: 'warning',
href: null
});
} }
}); });

View File

@ -6,7 +6,7 @@
<htk-grid <htk-grid
class="box vn-w-sm" class="box vn-w-sm"
show-header="false"> show-header="false">
<db-model property="model" id="invoices"> <db-model property="model" id="tickets">
SELECT id, ref, issued, amount, hasPdf SELECT id, ref, issued, amount, hasPdf
FROM myInvoice FROM myInvoice
ORDER BY issued DESC ORDER BY issued DESC
@ -16,9 +16,8 @@
<htk-column-date title="_Date" column="issued" format="_%e %b %Y"/> <htk-column-date title="_Date" column="issued" format="_%e %b %Y"/>
<htk-column-spin title="_Import" column="amount" unit="€" digits="2"/> <htk-column-spin title="_Import" column="amount" unit="€" digits="2"/>
<htk-column-button <htk-column-button
icon="download" renderer="donwloadRenderer"
tip="_Download PDF" target="_blank"/>
on-clicked="onDownloadClick"/>
</htk-grid> </htk-grid>
</div> </div>
</vn> </vn>

View File

@ -26,8 +26,8 @@
<label><t>Realm</t></label> <label><t>Realm</t></label>
<htk-combo form="lot" column="realm" not-null="false"> <htk-combo form="lot" column="realm" not-null="false">
<db-model property="model"> <db-model property="model">
SELECT id, name FROM vn.itemCategory SELECT id, reino FROM vn2008.reinos
WHERE display ORDER BY name WHERE display != FALSE ORDER BY reino
</db-model> </db-model>
</htk-combo> </htk-combo>
</div> </div>

View File

@ -25,9 +25,9 @@
on-ready="this.onConfigChange()"> on-ready="this.onConfigChange()">
<db-model property="model"> <db-model property="model">
SELECT c.id, c.name reportTitle, c.namePrefix, c.warehouse, c.family, SELECT c.id, c.name reportTitle, c.namePrefix, c.warehouse, c.family,
c.shelf, c.maxAmount, c.showPacking, c.stack, it.categoryFk realm c.shelf, c.maxAmount, c.showPacking, c.stack, t.reino_id realm
FROM shelfMultiConfig c FROM shelfConfig c
JOIN vn.itemType it ON it.id = c.family JOIN vn2008.Tipos t ON t.tipo_id = c.family
</db-model> </db-model>
</htk-combo> </htk-combo>
</div> </div>
@ -39,8 +39,8 @@
<label><t>Reign</t></label> <label><t>Reign</t></label>
<htk-combo form="lot" name="realm"> <htk-combo form="lot" name="realm">
<db-model property="model"> <db-model property="model">
SELECT id, name FROM vn.itemCategory SELECT id, reino FROM vn2008.reinos
WHERE display ORDER BY name WHERE display != FALSE ORDER BY reino
</db-model> </db-model>
</htk-combo> </htk-combo>
</div> </div>
@ -48,8 +48,8 @@
<label><t>Family</t></label> <label><t>Family</t></label>
<htk-combo form="lot" name="family"> <htk-combo form="lot" name="family">
<db-model property="model" lot="lot"> <db-model property="model" lot="lot">
SELECT id, name FROM vn.itemType SELECT tipo_id, Tipo FROM vn2008.Tipos
WHERE categoryFk = #realm ORDER BY name WHERE reino_id = #realm ORDER BY Tipo
</db-model> </db-model>
</htk-combo> </htk-combo>
</div> </div>

View File

@ -1,6 +1,6 @@
{ {
"name": "hedera-web", "name": "hedera-web",
"version": "25.4.4", "version": "24.8.2",
"description": "Verdnatura web page", "description": "Verdnatura web page",
"license": "GPL-3.0", "license": "GPL-3.0",
"repository": { "repository": {

21
rest/dms/invoice.php Normal file
View File

@ -0,0 +1,21 @@
<?php
use Vn\Web\Security;
use Vn\Web\Util;
use Vn\Lib;
class Invoice extends Vn\Web\RestRequest {
const PARAMS = ['invoice'];
const SECURITY = Security::INVOKER;
function run($db) {
$pdfPath = $db->getValueFromFile(__DIR__ .'/invoice',
['invoice' =>(int) $_GET['invoice']]);
if (!$pdfPath)
throw new Lib\UserException(s('Invoice id not found'));
Util::printFile($pdfPath);
}
}

5
rest/dms/invoice.sql Normal file
View File

@ -0,0 +1,5 @@
SELECT CONCAT_WS('/', c.pdfsDir, vn.invoiceOut_getPath(#invoice))
FROM config c
JOIN myInvoice i
WHERE i.id = #invoice

View File

@ -8,7 +8,7 @@ class Clean extends Edi\Method {
$cleanPeriod = $db->getValue( $cleanPeriod = $db->getValue(
"SELECT ic.cleanPeriod "SELECT ic.cleanPeriod
FROM imapMultiConfig ic FROM imapConfig ic
JOIN util.config c ON c.environment = ic.environment"); JOIN util.config c ON c.environment = ic.environment");
$deleted = 0; $deleted = 0;

View File

@ -14,7 +14,7 @@ abstract class Method extends \Vn\Lib\Method {
$imapConf = $db->getRow( $imapConf = $db->getRow(
"SELECT ic.host, ic.user, ic.pass, ic.successFolder, ic.errorFolder "SELECT ic.host, ic.user, ic.pass, ic.successFolder, ic.errorFolder
FROM imapMultiConfig ic FROM imapConfig ic
JOIN util.config c ON c.environment = ic.environment"); JOIN util.config c ON c.environment = ic.environment");
$this->mailbox = sprintf('{%s/imap/ssl/novalidate-cert}', $this->mailbox = sprintf('{%s/imap/ssl/novalidate-cert}',

View File

@ -0,0 +1,41 @@
<?php
/**
* Ejemplo:
* <Cube><Cube time="2010-12-10"><Cube currency="USD" rate="1.3244"/>
*/
class ExchangeRate extends Vn\Lib\Method {
function run($db) {
$db->selectDb('vn2008');
// Indica la URL del archivo
$xml = new SimpleXMLElement(
'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml', 0, TRUE);
$date = $db->getValue("SELECT MAX(date) fecha FROM reference_rate");
$maxDate = $date ? DateTime::createFromFormat('Y-m-d', $date) : NULL;
foreach ($xml->Cube[0]->Cube as $cube) {
$xmlDate = new DateTime($cube['time']);
// Si existen datos más recientes de la máxima fecha los añade
if ($maxDate <= $xmlDate)
foreach ($cube->Cube as $subCube)
if ($subCube['currency'] == 'USD') {
$params = [
'date' => $xmlDate,
'rate' => $subCube['rate']
];
$db->query(
'REPLACE INTO reference_rate(moneda_id, date, rate)
VALUES(2, #date, #rate)',
$params
);
}
}
$db->queryFromFile(__DIR__.'/exrate-add');
}
}

6
rest/misc/exrate-add.sql Normal file
View File

@ -0,0 +1,6 @@
INSERT INTO reference_rate (moneda_id, date, rate)
SELECT 2, TIMESTAMPADD (DAY, 1, r1.date), r1.rate
FROM reference_rate r1
LEFT JOIN reference_rate r2
ON TIMESTAMPADD(DAY, 1, r1.date) = r2.date
WHERE r2.date IS NULL AND r1.date < TIMESTAMPADD (DAY, -2, CURDATE())