Compare commits

..

No commits in common. "dev" and "5122-multipleBasket" have entirely different histories.

103 changed files with 1938 additions and 1536 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

@ -1,5 +1,5 @@
# Not using buster because of bug: https://bugs.php.net/bug.php?id=78870 # Not using buster because of bug: https://bugs.php.net/bug.php?id=78870
FROM debian:bookworm-slim FROM debian:stretch-slim
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
@ -23,13 +23,13 @@ RUN a2dissite 000-default
# NodeJs # NodeJs
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ RUN curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
&& apt-get install -y --no-install-recommends nodejs && apt-get install -y --no-install-recommends nodejs
# 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/ stretch main" \
> /etc/apt/sources.list.d/vn.list \ > /etc/apt/sources.list.d/vn.list \
&& apt-get update \ && apt-get update \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \

98
Jenkinsfile vendored
View File

@ -1,25 +1,34 @@
#!/usr/bin/env groovy #!/usr/bin/env groovy
def BRANCH_ENV = [
test: 'test',
master: 'production'
]
def remote = [:]
node {
stage('Setup') {
env.NODE_ENV = BRANCH_ENV[env.BRANCH_NAME] ?: 'dev'
echo "NODE_NAME: ${env.NODE_NAME}"
echo "WORKSPACE: ${env.WORKSPACE}"
}
}
pipeline { 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('Checkout') {
steps {
script {
def packageJson = readJSON file: 'package.json'
env.VERSION = packageJson.version
switch (env.BRANCH_NAME) {
case 'master':
env.NODE_ENV = 'production'
env.MAIN_REPLICAS = 3
env.CRON_REPLICAS = 1
break
case 'test':
env.NODE_ENV = 'test'
env.MAIN_REPLICAS = 1
env.CRON_REPLICAS = 0
break
}
}
setEnv()
}
}
stage('Debuild') { stage('Debuild') {
when { when {
anyOf { anyOf {
@ -29,28 +38,31 @@ pipeline {
} }
agent { agent {
docker { docker {
image 'registry.verdnatura.es/verdnatura/debuild:2.23.4-vn7' image 'registry.verdnatura.es/debuild:2.21.3-vn2'
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 stretch'
sh 'mv ../hedera-web_* debuild'
script {
def files = findFiles(glob: 'debuild/*.changes')
files.each { file -> env.CHANGES_FILE = file.name }
}
configFileProvider([
configFile(fileId: "dput.cf", variable: 'DPUT_CONFIG')
]) {
sshagent(credentials: ['jenkins-agent']) {
sh 'dput --config "$DPUT_CONFIG" verdnatura "debuild/$CHANGES_FILE"'
} }
} }
stage('Container') {
when {
anyOf {
branch 'master'
branch 'test'
}
}
environment {
CREDS = credentials('docker-registry')
}
steps {
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,41 +73,15 @@ pipeline {
} }
} }
environment { environment {
CREDS = credentials('docker-registry') DOCKER_HOST = "${env.SWARM_HOST}"
IMAGE = "$REGISTRY/verdnatura/hedera-web"
} }
steps { steps {
script { sh "docker stack deploy --with-registry-auth --compose-file docker-compose.yml ${env.STACK_NAME}"
def packageJson = readJSON file: 'package.json'
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'
}
} }
} }
} }
post { post {
unsuccessful { unsuccessful {
setEnv()
sendEmail() sendEmail()
} }
} }

View File

@ -1,49 +1,18 @@
# Hedera # Hedera
Hedera is the main web shop page for Verdnatura. Hedera is the main web page for Verdnatura.
## Prerequisites ## Getting Started
Required applications. Required dependencies.
* PHP >= 8.4 * PHP >= 7.0
* Node.js >= 20.0 * Node.js >= 8.0
Take a look to *debian/control* file to see additional dependencies. Launch application for development.
Copy config.php to *config.my.php* and place your DB config there.
### Installing dependencies and launching
Pull from repository.
Run this commands on project root directory to install Node dependencies.
``` ```
$ npm install $ npm run dev
``` ```
Install project dependences (debian/control).
Pull from repository [php-vn-lib](https://gitea.verdnatura.es/verdnatura/php-vn-lib) and install [dependences](https://gitea.verdnatura.es/verdnatura/php-vn-lib/src/branch/master/debian/control) of this project.
Configure config.php file.
Launch project fronted.
```
$ npm run front
```
Launch salix backend.
```
$ npm run db
$ npm run back
```
Launch project backend.
```
$ php -S 127.0.0.1:3001 -t . index.php
```
### Command line
Run server side method from command line. Run server side method from command line.
``` ```
$ php hedera-web.php -m method_path $ php hedera-web.php -m method_path
@ -51,8 +20,6 @@ $ php hedera-web.php -m method_path
## Built with ## Built with
* [nodejs](https://nodejs.org/)
* [php](https://www.php.net/)
* [Webpack](https://webpack.js.org/) * [Webpack](https://webpack.js.org/)
* [MooTools](https://mootools.net/) * [MooTools](https://mootools.net/)
* [TinyMCE](https://www.tinymce.com/) * [TinyMCE](https://www.tinymce.com/)

View File

@ -12,8 +12,6 @@
* *
* - http://www.mydomain.org -> config.www.php * - http://www.mydomain.org -> config.www.php
* - http://test.mydomain.org -> config.test.php * - http://test.mydomain.org -> config.test.php
*
* Put the password in base64.
*/ */
return [ return [
/** /**
@ -24,7 +22,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 (22.48.10) 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

2
debian/links vendored
View File

@ -1,3 +1,3 @@
usr/share/hedera-web/hedera-web.php usr/bin/hedera-web.php usr/share/hedera-web/hedera-web.php usr/bin/hedera-web.php
etc/hedera-web/apache.conf etc/apache2/conf-available/hedera-web.conf etc/hedera-web/apache.conf etc/apache2/conf-available/hedera-web.conf
etc/hedera-web/php.ini etc/php/8.2/apache2/conf.d/99-hedera-web.ini etc/hedera-web/php.ini etc/php/7.0/apache2/conf.d/99-hedera-web.ini

View File

@ -1,9 +1,41 @@
version: '3.7' version: '3.7'
services: services:
main: main:
image: registry.verdnatura.es/verdnatura/hedera-web:${TAG:?} image: registry.verdnatura.es/hedera-web:${BRANCH_NAME:?}
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
cron:
image: registry.verdnatura.es/hedera-web:${BRANCH_NAME:?}
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
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

@ -7,7 +7,79 @@ export default new Class({
activate() { activate() {
this.$.userModel.setInfo('c', 'myClient', 'hedera'); this.$.userModel.setInfo('c', 'myClient', 'hedera');
this.$.userModel.setInfo('u', 'myUser', 'account'); this.$.userModel.setInfo('u', 'myUser', 'account');
this.$.changePassword.conn = this.conn
this.$.changePassword.user = this.gui.user if (this.hash.$.verificationToken)
this.onPassChangeClick();
}
,onPassChangeClick() {
this.$.oldPassword.value = '';
this.$.newPassword.value = '';
this.$.repeatPassword.value = '';
var verificationToken = this.hash.$.verificationToken;
this.$.oldPassword.style.display = verificationToken ? 'none' : 'block';
this.$.changePassword.show();
if (verificationToken)
this.$.newPassword.focus();
else
this.$.oldPassword.focus();
}
,async onPassModifyClick() {
const form = this.$.changePassword.node;
Vn.Node.disableInputs(form);
try {
const oldPassword = this.$.oldPassword.value;
const newPassword = this.$.newPassword.value;
const repeatedPassword = this.$.repeatPassword.value;
try {
if (newPassword == '' && repeatedPassword == '')
throw new Error(_('Passwords empty'));
if (newPassword !== repeatedPassword)
throw new Error(_('Passwords doesn\'t match'));
} catch (err) {
return Htk.Toast.showError(err.message);
}
const verificationToken = this.hash.$.verificationToken;
const params = {newPassword};
try {
if (verificationToken) {
params.verificationToken = verificationToken;
await this.conn.send('user/restore-password', params);
} else {
let userId = this.gui.user.id;
params.oldPassword = oldPassword;
await this.conn.patch(
`Accounts/${userId}/changePassword`, params);
}
} catch(err) {
Htk.Toast.showError(err.message);
if (verificationToken)
this.$.newPassword.select();
else
this.$.oldPassword.select();
return;
}
this.hash.unset('verificationToken');
await this.conn.open(this.gui.user.name, newPassword);
this.$.changePassword.hide();
} finally {
Vn.Node.disableInputs(form, false);
}
Htk.Toast.showMessage(_('Password changed!'));
}
,onPassInfoClick() {
this.$.passwordInfo.show();
} }
}); });

View File

@ -1,5 +1,11 @@
<vn> <vn>
<vn-group> <vn-group>
<db-form v-model="passwordForm">
<db-model property="model">
SELECT length, nAlpha, nUpper, nDigits, nPunct
FROM account.userPassword
</db-model>
</db-form>
<db-form id="user-form"> <db-form id="user-form">
<db-model property="model" id="user-model" updatable="true"> <db-model property="model" id="user-model" updatable="true">
SELECT u.id, u.name, u.email, u.nickname, SELECT u.id, u.name, u.email, u.nickname,
@ -21,7 +27,7 @@
<htk-bar-button <htk-bar-button
icon="lock_reset" icon="lock_reset"
tip="_Change password" tip="_Change password"
on-click="this.$.changePassword.open()"/> on-click="this.onPassChangeClick()"/>
</div> </div>
<div id="form" class="conf"> <div id="form" class="conf">
<div class="form box vn-w-sm vn-pa-lg"> <div class="form box vn-w-sm vn-pa-lg">
@ -68,5 +74,64 @@
</div> </div>
</div> </div>
</div> </div>
<htk-change-password id="change-password"/> <htk-popup
id="change-password"
modal="true">
<div property="child-node" class="htk-dialog vn-w-xs vn-pa-lg">
<div class="form">
<h5 class="vn-mb-md">
<t>Change password</t>
</h5>
<input
id="old-password"
type="password"
placeholder="_Old password"/>
<input
id="new-password"
type="password"
placeholder="_New password"
autocomplete="new-password"/>
<input
id="repeat-password"
type="password"
placeholder="_Repeat password"
autocomplete="new-password"/>
</div>
<div class="button-bar">
<button class="thin" on-click="this.onPassModifyClick()">
<t>Modify</t>
</button>
<button class="thin" on-click="this.onPassInfoClick()">
<t>Requirements</t>
</button>
<div class="clear"/>
</div>
</div>
</htk-popup>
<htk-popup
id="password-info"
modal="true">
<div property="child-node" class="htk-dialog pass-info vn-w-xs vn-pa-lg">
<h5 class="vn-mb-md">
<t>Password requirements</t>
</h5>
<ul>
<li>
{{passwordForm.length}} <t>characters long</t>
</li>
<li>
{{passwordForm.nAlpha}} <t>alphabetic characters</t>
</li>
<li>
{{passwordForm.nUpper}} <t>capital letters</t>
</li>
<li>
{{passwordForm.nDigits}} <t>digits</t>
</li>
<li>
{{passwordForm.nPunct}} <t>symbols</t>
</li>
</ul>
</div>
</htk-popup>
</vn> </vn>

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

@ -7,8 +7,8 @@ export default new Class({
,activate() { ,activate() {
if (!this.hash.$.to) if (!this.hash.$.to)
this.hash.assign({ this.hash.assign({
from: Date.vnNew(), from: new Date(),
to: Date.vnNew() to: new Date()
}); });
} }
}); });

View File

@ -3,5 +3,12 @@ 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'),
onShowClick(column, agencyId) {
this.hash.setAll({
form: 'agencies/provinces',
agency: agencyId
});
}
}); });

View File

@ -1,4 +1,5 @@
ListByAgency: Paquets per agència ListByAgency: Paquets per agència
ShowByProvince: Mostrar desglose per província
Agency: Agència Agency: Agència
Exps: Exps. Exps: Exps.
Bundles: Paquets Bundles: Paquets

View File

@ -1,4 +1,5 @@
ListByAgency: Bundles by agency ListByAgency: Bundles by agency
ShowByProvince: Show breakdown by province
Agency: Agency Agency: Agency
Exps: Exps. Exps: Exps.
Bundles: Bundles Bundles: Bundles

View File

@ -1,4 +1,5 @@
ListByAgency: Bultos por agencia ListByAgency: Bultos por agencia
ShowByProvince: Mostrar desglose por provincia
Agency: Agencia Agency: Agencia
Exps: Exps. Exps: Exps.
Bundles: Bultos Bundles: Bultos

View File

@ -1,4 +1,5 @@
ListByAgency: Liste par agence ListByAgency: Liste par agence
ShowByProvince: Montrer par province
Agency: Agence Agency: Agence
Exps: Expéditeur Exps: Expéditeur
Bundles: Cartons Bundles: Cartons

View File

@ -1,4 +1,5 @@
ListByAgency: Bultos por agencia ListByAgency: Bultos por agencia
ShowByProvince: Mostrar desglosse por Distrito
Agency: Agencia Agency: Agencia
Exps: Exps. Exps: Exps.
Bundles: Bultos Bundles: Bultos

View File

@ -7,9 +7,14 @@
<htk-grid> <htk-grid>
<db-model property="model"> <db-model property="model">
<custom> <custom>
CALL vn.agencyVolume() CALL vn2008.agencia_volume ()
</custom> </custom>
</db-model> </db-model>
<htk-column-button
column="agency_id"
icon="search"
tip="_ShowByProvince"
on-clicked="onShowClick"/>
<htk-column-text title="_Agency" column="Agencia"/> <htk-column-text title="_Agency" column="Agencia"/>
<htk-column-spin title="_Exps" column="expediciones"/> <htk-column-spin title="_Exps" column="expediciones"/>
<htk-column-spin title="_Bundles" column="Bultos"/> <htk-column-spin title="_Bundles" column="Bultos"/>

View File

@ -0,0 +1,7 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml')
});

View File

@ -0,0 +1,6 @@
ByProvince: Desglose per província
Return: Tornar
SelectAgency: Selecciona una agència al llistat de l'esquerra
Province: Província
Expeditions: Exps.
Left: Falten

View File

@ -0,0 +1,6 @@
ByProvince: Breakdown by province
Return: Return
SelectAgency: Select an agency
Province: Province
Expeditions: Exps.
Left: Left

View File

@ -0,0 +1,6 @@
ByProvince: Desglose por provincia
Return: Volver
SelectAgency: Selecciona una agencia
Province: Provincia
Expeditions: Exps.
Left: Faltan

View File

@ -0,0 +1,6 @@
ByProvince: Par province
Return: Retour
SelectAgency: Sélectionnez une agence
Province: Province
Expeditions: Expéditions
Left: Restant

View File

@ -0,0 +1,6 @@
ByProvince: Desglosse por Distritos
Return: Voltar
SelectAgency: Seleccione uma agência
Province: Distrito
Expeditions: Exps.
Left: Faltam

View File

View File

@ -0,0 +1,18 @@
<vn>
<div id="title">
<h1><t>ByProvince</t></h1>
</div>
<div id="form" class="provinces vn-w-sm">
<div class="box">
<htk-grid>
<db-model property="model" lot="hash">
CALL vn2008.desglose_volume(#agency)
</db-model>
<htk-column-text title="_Province" column="Provincia"/>
<htk-column-spin title="_Expeditions" column="expediciones"/>
<htk-column-spin title="_Bundles" column="Bultos"/>
<htk-column-spin title="_Left" column="Prevision"/>
</htk-grid>
</div>
</div>
</vn>

View File

@ -14,7 +14,7 @@ export default new Class({
,refreshCaptcha() { ,refreshCaptcha() {
params = { params = {
srv: 'rest:misc/captcha', srv: 'rest:misc/captcha',
stamp: Date.vnNew().getTime() stamp: new Date().getTime()
}; };
this.$.captchaImg.src = '?'+ Vn.Url.makeUri(params); this.$.captchaImg.src = '?'+ Vn.Url.makeUri(params);
} }

View File

@ -13,8 +13,7 @@ BecauseOurSalesDep: >-
Pour nos professionnels de service commercial qui sera toujours de trouver une Pour nos professionnels de service commercial qui sera toujours de trouver une
solution à vos besoins. solution à vos besoins.
BecauseOurWorkShop: Parce que nous avons un atelier de couture pour aider. BecauseOurWorkShop: Parce que nous avons un atelier de couture pour aider.
BecauseWeHaveWhatYouNeed: >- BecauseWeHaveWhatYouNeed: Parce que nous avons ce que vous avez besoin quand vous en avez besoin ...
Parce que nous avons ce que vous avez besoin quand vous en avez besoin ...
AboutDesc: >- AboutDesc: >-
Nous sommes une société spécialisée dans le commerce de gros et de la Nous sommes une société spécialisée dans le commerce de gros et de la
distribution d'une large gamme d'accessoires, des verts et des fleurs à des distribution d'une large gamme d'accessoires, des verts et des fleurs à des
@ -32,6 +31,6 @@ AboutDisp: >-
Mercaflor - Mercavalencia (Valencia) qui effectuent des ventes directes Mercaflor - Mercavalencia (Valencia) qui effectuent des ventes directes
seulement. seulement.
AboutOrder: >- AboutOrder: >-
Vous pouvez faire vos commandes et réservations par téléphone au +33 783 285 Vous pouvez faire vos commandes et réservations par téléphone au +33 781 533
437, en ligne grâce à notre site Internet ou directement dans nos 900, en ligne grâce à notre site Internet ou directement dans nos
installations. installations.

View File

@ -5,46 +5,20 @@ export default new Class({
Template: require('./ui.xml'), Template: require('./ui.xml'),
async open() { async open() {
await this.loadOrder();
if (this.orderId) {
await Hedera.Form.prototype.open.call(this); await Hedera.Form.prototype.open.call(this);
this.basket = new Hedera.Basket(this.app);
this.orderId = this.$.params.$.id || this.basket.orderId;
if (!this.orderId)
return this.hash.setAll({form: 'ecomerce/checkout'});
this.$.lot.assign({id: this.orderId}); this.$.lot.assign({id: this.orderId});
}
}, },
activate() { activate() {
this.$.items.setInfo('bi', 'myOrderRow', 'hedera'); this.$.items.setInfo('bi', 'myOrderRow', 'hedera');
}, },
async onHashChange() {
if (!this.isOpen) return;
await this.loadOrder();
if (this.orderId)
this.$.lot.assign({id: this.orderId});
},
async loadOrder() {
const basket = new Hedera.Basket(this.app);
if (this.hash.$.id) {
this.orderId = this.hash.$.id;
} else if (await basket.check()) {
this.orderId = basket.orderId;
}
},
onOrderReady(form) {
if (form.row < 0)
return;
if (form.$.method != 'PICKUP') {
Vn.Node.show(this.$.address);
Vn.Node.setText(this.$.method, _('Agency'));
} else {
Vn.Node.hide(this.$.address);
Vn.Node.setText(this.$.method, _('Warehouse'));
}
},
onConfigureClick() { onConfigureClick() {
Htk.Toast.showWarning(_('RememberReconfiguringImpact')); Htk.Toast.showWarning(_('RememberReconfiguringImpact'));
this.hash.setAll({ this.hash.setAll({

View File

@ -1,16 +1,13 @@
ShoppingBasket: Cistella de la compra ShoppingBasket: Cistella de la compra
Order: Encàrrec Order: Encàrrec
ShippingInformation: Dades d'enviament ShippingInformation: Dades d'enviament
DeliveryAddress: Adreça de lliurament
Delivery at: Lliurament el
Agency: Agència
Warehouse: Magatzem
Delete: Borrar encàrrec Delete: Borrar encàrrec
GoToCatalog: Anar al catàleg GoToCatalog: Anar al catàleg
ConfigureOrder: Configurar encàrrec ConfigureOrder: Configurar encàrrec
Checkout: Tramitar encàrrec Checkout: Tramitar encàrrec
OrderNumber: N encàrec OrderNumber: N encàrec
DateExit: Data d'eixida DateExit: Data d'eixida
Warehouse: Magatzem
OrderTotal: Total encàrrec OrderTotal: Total encàrrec
Amount: Quant Amount: Quant
Pack: Pack Pack: Pack

View File

@ -1,16 +1,13 @@
ShoppingBasket: Shopping basket ShoppingBasket: Shopping basket
Order: Order Order: Order
ShippingInformation: Shipping information ShippingInformation: Shipping information
DeliveryAddress: Delivery address
Delivery at: Delivery at
Agency: Agency
Warehouse: Store
Delete: Delete order Delete: Delete order
GoToCatalog: Go to catalog GoToCatalog: Go to catalog
ConfigureOrder: Configure order ConfigureOrder: Configure order
Checkout: Checkout Checkout: Checkout
OrderNumber: Order number OrderNumber: Order number
DateExit: Shipping date DateExit: Shipping date
Warehouse: Store
OrderTotal: Total OrderTotal: Total
Amount: Amount Amount: Amount
Pack: Pack Pack: Pack

View File

@ -1,16 +1,13 @@
ShoppingBasket: Cesta de la compra ShoppingBasket: Cesta de la compra
Order: Pedido Order: Pedido
ShippingInformation: Datos de envío ShippingInformation: Datos de envío
DeliveryAddress: Dirección de entrega
Delivery at: Entrega el
Agency: Agencia
Warehouse: Almacén
Delete: Borrar pedido Delete: Borrar pedido
GoToCatalog: Ir al catálogo GoToCatalog: Ir al catálogo
ConfigureOrder: Configurar pedido ConfigureOrder: Configurar pedido
Checkout: Finalizar pedido Checkout: Finalizar pedido
OrderNumber: Nº pedido OrderNumber: Nº pedido
DateExit: Fecha de salida DateExit: Fecha de salida
Warehouse: Almacén
OrderTotal: Total OrderTotal: Total
Amount: Cantidad Amount: Cantidad
Pack: Pack Pack: Pack

View File

@ -1,16 +1,13 @@
ShoppingBasket: Panier ShoppingBasket: Panier
Order: Commande Order: Commande
ShippingInformation: Informations sur la livraison ShippingInformation: Informations sur la livraison
DeliveryAddress: Addresse de livraison
Delivery at: Livraison à
Agency: Agence
Warehouse: Entrepôt
Delete: Effacer Delete: Effacer
GoToCatalog: Aller au catalogue GoToCatalog: Aller au catalogue
ConfigureOrder: Définissez l'ordre ConfigureOrder: Définissez l'ordre
Checkout: Caisse Checkout: Caisse
OrderNumber: Numéro de commande OrderNumber: Numéro de commande
DateExit: Date de sortie DateExit: Date de sortie
Warehouse: Magasin
OrderTotal: Total commande OrderTotal: Total commande
Amount: Quant Amount: Quant
Pack: Pack Pack: Pack

View File

@ -1,16 +1,13 @@
ShoppingBasket: Cesta da compra ShoppingBasket: Cesta da compra
Order: Encomenda Order: Encomenda
ShippingInformation: Dados de envio ShippingInformation: Dados de envio
DeliveryAddress: Endereço de entrega
Delivery at: Entrega na
Agency: Agência
Warehouse: Armazém
Delete: Eliminar encomenda Delete: Eliminar encomenda
GoToCatalog: Ir ao catálogo GoToCatalog: Ir ao catálogo
ConfigureOrder: Configurar encomenda ConfigureOrder: Configurar encomenda
Checkout: Finalizar encomenda Checkout: Finalizar encomenda
OrderNumber: Nº encomenda OrderNumber: Nº encomenda
DateExit: Data de saída DateExit: Data de saída
Warehouse: Armazém
OrderTotal: Total OrderTotal: Total
Amount: Quantidade Amount: Quantidade
Pack: Pack Pack: Pack

View File

@ -1,4 +1,7 @@
<vn> <vn>
<vn-lot-query id="params">
<vn-spec name="id" type="Number"/>
</vn-lot-query>
<div id="title"> <div id="title">
<h1>{{_(params.$.id ? 'Order' : 'ShoppingBasket')}}</h1> <h1>{{_(params.$.id ? 'Order' : 'ShoppingBasket')}}</h1>
</div> </div>
@ -8,7 +11,7 @@
tip="_ConfigureOrder" tip="_ConfigureOrder"
on-click="this.onConfigureClick()"/> on-click="this.onConfigureClick()"/>
<htk-bar-button <htk-bar-button
icon="shopping_bag" icon="local_florist"
tip="_Catalog" tip="_Catalog"
on-click="this.onCatalogClick()"/> on-click="this.onCatalogClick()"/>
<htk-bar-button <htk-bar-button
@ -17,15 +20,11 @@
on-click="this.onConfirmClick()"/> on-click="this.onConfirmClick()"/>
</div> </div>
<vn-group> <vn-group>
<vn-lot-query id="params" on-change="this.onHashChange()">
<vn-spec name="id" type="Number"/>
</vn-lot-query>
<vn-lot id="lot"/> <vn-lot id="lot"/>
<db-form v-model="order" on-ready="onOrderReady"> <db-form v-model="order">
<db-model property="model" lot="lot"> <db-model property="model" lot="lot">
SELECT o.id, o.sent, SELECT o.id, o.sent,
ag.description agency, v.code method, ag.description agency, v.code method, ad.nickname
ad.nickname, ad.postalCode, ad.city, ad.street
FROM myOrder o FROM myOrder o
JOIN vn.agencyMode ag ON ag.id = o.agencyModeFk JOIN vn.agencyMode ag ON ag.id = o.agencyModeFk
LEFT JOIN myAddress ad ON ad.id = o.addressFk LEFT JOIN myAddress ad ON ad.id = o.addressFk
@ -38,20 +37,12 @@
<div class="box vn-w-sm vn-pa-lg"> <div class="box vn-w-sm vn-pa-lg">
<div class="head vn-pb-lg"> <div class="head vn-pb-lg">
<h5>#{{order.id}}</h5> <h5>#{{order.id}}</h5>
<div class="delivery"> <div class="vn-mt-md">
<h6><t>ShippingInformation</t></h6> <h6><t>ShippingInformation</t></h6>
<p></p>
<p><t>Delivery at</t> {{Vn.Value.format(order.sent, _('%D'))}}</p>
<p><span id="method"><t>Agency</t></span> {{order.agency}}</p>
<p>{{order.nickname}}</p> <p>{{order.nickname}}</p>
<p>
<t>Delivery at</t> {{Vn.Value.format(order.sent, _('%D'))}}
</p>
<p>
<span id="method"><t>Agency</t></span> {{order.agency}}
</p>
</div>
<div id="address" class="address vn-mt-md">
<h6><t>DeliveryAddress</t></h6>
<p>{{order.street}}</p>
<p>{{order.postalCode}}, {{order.city}}</p>
</div> </div>
<p class="total"> <p class="total">
<t>Total</t> <t>Total</t>

View File

@ -7,11 +7,15 @@ const Catalog = new Class({
,_menuShown: false ,_menuShown: false
,async open() { ,async open() {
let isOk = true;
const basket = new Hedera.Basket(this.app); const basket = new Hedera.Basket(this.app);
this.orderId = basket.orderId;
if (!localStorage.getItem('hederaGuest')) { if (!localStorage.getItem('hederaGuest')) {
if (await basket.check('catalog')) if (!this.orderId)
this.orderId = basket.orderId; return this.hash.setAll({form: 'ecomerce/checkout'});
else
isOk = await basket.checkRedirect(this.orderId);
} else { } else {
const resultSet = await this.conn.execQuery( const resultSet = await this.conn.execQuery(
'CALL myOrder_configureForGuest(@orderId); SELECT @orderId;'); 'CALL myOrder_configureForGuest(@orderId); SELECT @orderId;');
@ -20,8 +24,7 @@ const Catalog = new Class({
this.orderId = resultSet.fetchValue(); this.orderId = resultSet.fetchValue();
} }
if (this.orderId) if (isOk) await Hedera.Form.prototype.open.call(this);
await Hedera.Form.prototype.open.call(this);
} }
,activate() { ,activate() {
@ -203,12 +206,6 @@ const Catalog = new Class({
this.hideMenu(); this.hideMenu();
} }
,itemRenderer(builder, form) {
var minQuantity = builder.$.minQuantity;
minQuantity.style.display = form.$.minQuantity
? 'block' : 'hidden';
}
,realmRenderer(builder, form) { ,realmRenderer(builder, form) {
var link = builder.$.link; var link = builder.$.link;
link.href = this.hash.make({ link.href = this.hash.make({
@ -279,8 +276,6 @@ const Catalog = new Class({
orderId: this.orderId orderId: this.orderId
}); });
this.$.cardPopup.show(event.currentTarget); this.$.cardPopup.show(event.currentTarget);
this.$.cardMinQuantity.style.display = form.$.minQuantity
? 'block' : 'none';
} }
,onAddLotClick(column, value, row) { ,onAddLotClick(column, value, row) {
@ -325,7 +320,7 @@ const Catalog = new Class({
} }
if (amountSum > 0) { if (amountSum > 0) {
await this.conn.execQuery(sql); this.conn.execQuery(sql);
var itemName = this.$.$card.get('item'); var itemName = this.$.$card.get('item');
Htk.Toast.showMessage( Htk.Toast.showMessage(

View File

@ -46,4 +46,3 @@ NoMoreAmountAvailable: No hi ha més quantitat disponible
MinimalGrouping: Empaquetat mínim MinimalGrouping: Empaquetat mínim
Available: Disponible Available: Disponible
GroupingPrice: Preu per grup GroupingPrice: Preu per grup
MinimalQuantity: Quantitat mínima

View File

@ -46,4 +46,3 @@ NoMoreAmountAvailable: No more amount available
MinimalGrouping: Minimal packing MinimalGrouping: Minimal packing
Available: Available Available: Available
GroupingPrice: Price per group GroupingPrice: Price per group
MinimalQuantity: Minimal quantity

View File

@ -46,4 +46,3 @@ NoMoreAmountAvailable: No hay más cantidad disponible
MinimalGrouping: Empaquetado mínimo MinimalGrouping: Empaquetado mínimo
Available: Disponible Available: Disponible
GroupingPrice: Precio per grupo GroupingPrice: Precio per grupo
MinimalQuantity: Cantidad mínima

View File

@ -46,4 +46,3 @@ NoMoreAmountAvailable: Pas plus disponible
MinimalGrouping: Emballage minimal MinimalGrouping: Emballage minimal
Available: Disponible Available: Disponible
GroupingPrice: Prix par groupe GroupingPrice: Prix par groupe
MinimalQuantity: Quantité minimum

View File

@ -46,4 +46,3 @@ NoMoreAmountAvailable: Não há mais quantidade disponível
MinimalGrouping: Embalagem mínima MinimalGrouping: Embalagem mínima
Available: Disponível Available: Disponível
GroupingPrice: Preço por grupo GroupingPrice: Preço por grupo
MinimalQuantity: Quantidade mínima

View File

@ -192,25 +192,6 @@
margin: 0; margin: 0;
margin-bottom: 1px; margin-bottom: 1px;
} }
& > .min-quantity {
bottom: 32px;
right: 0;
}
}
.min-quantity {
position: absolute;
display: none;
bottom: 30px;
right: 0;
font-size: .8rem;
color: #a44;
cursor: pointer;
& > span {
font-size: 16px;
vertical-align: middle;
}
} }
/* Tags */ /* Tags */
@ -275,7 +256,7 @@
} }
& > .htk-image { & > .htk-image {
width: 100%; width: 100%;
height: 210px; height: 180px;
& > img { & > img {
height: initial; height: initial;
@ -285,7 +266,7 @@
flex: auto; flex: auto;
overflow: hidden; overflow: hidden;
margin: 10px; margin: 10px;
height: 185px; height: 170px;
& > h2 { & > h2 {
max-height: 3rem; max-height: 3rem;
@ -370,7 +351,6 @@
& > .top { & > .top {
padding: 14px; padding: 14px;
position: relative;
& > .item-info { & > .item-info {
margin-left: 126px; margin-left: 126px;
@ -391,11 +371,6 @@
margin-top: 15px 0; margin-top: 15px 0;
font-size: .9rem; font-size: .9rem;
} }
& > .min-quantity {
bottom: 0;
right: 0;
padding: 14px;
}
} }
& > .lots-grid { & > .lots-grid {
border-top: 1px solid #DDD; border-top: 1px solid #DDD;

View File

@ -94,9 +94,10 @@
<vn-lot id="order-lot"/> <vn-lot id="order-lot"/>
<db-form v-model="basket"> <db-form v-model="basket">
<db-model property="model" lot="order-lot"> <db-model property="model" lot="order-lot">
SELECT o.id, o.sent, ad.nickname SELECT o.id, o.sent, a.description agency, m.code method
FROM myOrder o FROM myOrder o
LEFT JOIN myAddress ad ON ad.id = o.addressFk JOIN vn.agencyMode a ON a.id = o.agencyModeFk
JOIN vn.deliveryMethod m ON m.id = o.deliveryMethodFk
WHERE o.id = #orderId WHERE o.id = #orderId
</db-model> </db-model>
</db-form> </db-form>
@ -115,9 +116,8 @@
WHERE #filter; WHERE #filter;
CALL myOrder_calcCatalogFull(#orderId); CALL myOrder_calcCatalogFull(#orderId);
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.tag7, i.value7, i.tag8, i.value8, i.relevancy, i.size, i.category,
i.relevancy, i.size, i.category, b.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"/>
@ -143,8 +149,7 @@
id="grid-view" id="grid-view"
empty-message="_Choose filter from right menu" empty-message="_Choose filter from right menu"
form-id="item" form-id="item"
model="items" model="items">
renderer="itemRenderer">
<custom> <custom>
<div <div
id="item-box" id="item-box"
@ -182,10 +187,6 @@
<td>{{item.tag7}}</td> <td>{{item.tag7}}</td>
<td>{{item.value7}}</td> <td>{{item.value7}}</td>
</tr> </tr>
<tr>
<td>{{item.tag8}}</td>
<td>{{item.value8}}</td>
</tr>
</table> </table>
<div class="available-price"> <div class="available-price">
<span class="grouping" title="_MinimalGrouping"> <span class="grouping" title="_MinimalGrouping">
@ -198,12 +199,6 @@
{{Vn.Value.format(item.price, '%.02d€')}} {{Vn.Value.format(item.price, '%.02d€')}}
</span> </span>
</div> </div>
<div id="min-quantity" class="min-quantity" title="_MinimalQuantity">
<span class="htk-icon material-symbols-rounded">
production_quantity_limits
</span>
{{item.minQuantity}}
</div>
</div> </div>
</div> </div>
</custom> </custom>
@ -212,8 +207,11 @@
</div> </div>
<div id="right-panel" class="catalog-panel right-panel side-panel" on-click="onRightPanelClick"> <div id="right-panel" class="catalog-panel right-panel side-panel" on-click="onRightPanelClick">
<div class="basket-info"> <div class="basket-info">
<p>{{basket.nickname}}</p>
<p>{{Vn.Value.format(basket.sent, '%D')}}</p> <p>{{Vn.Value.format(basket.sent, '%D')}}</p>
<p>
{{_(basket.method != 'PICKUP' ? 'Agency' : 'Warehouse')}}
{{basket.agency}}
</p>
<button class="thin" on-click="this.onConfigureClick()"> <button class="thin" on-click="this.onConfigureClick()">
<t>Modify</t> <t>Modify</t>
</button> </button>
@ -462,12 +460,6 @@
</tr> </tr>
</custom> </custom>
</htk-repeater> </htk-repeater>
<div id="card-min-quantity" class="min-quantity" title="_MinimalQuantity">
<span class="htk-icon material-symbols-rounded">
production_quantity_limits
</span>
{{card.minQuantity}}
</div>
</div> </div>
<htk-grid class="lots-grid" show-header="false"> <htk-grid class="lots-grid" show-header="false">
<db-model <db-model

View File

@ -8,7 +8,7 @@ export default new Class({
this.autoStepLocked = true; this.autoStepLocked = true;
this.$.assistant.stepsIndex = this.agencySteps; this.$.assistant.stepsIndex = this.agencySteps;
this.today = Date.vnNew(); this.today = new Date();
this.today.setHours(0, 0, 0, 0); this.today.setHours(0, 0, 0, 0);
}, },
@ -22,8 +22,8 @@ export default new Class({
let date; let date;
const row = orderForm.$ || defaultsForm.$ || {}; const row = orderForm.$ || defaultsForm.$ || {};
if (!date || date.getTime() < (Date.vnNew()).getTime()) { if (!date || date.getTime() < (new Date()).getTime()) {
date = Date.vnNew(); date = new Date();
date.setHours(0, 0, 0, 0); date.setHours(0, 0, 0, 0);
let addDays = 0; let addDays = 0;
@ -81,27 +81,21 @@ export default new Class({
if (!resultSet.fetchResult()) if (!resultSet.fetchResult())
return; return;
let redirect;
const basket = new Hedera.Basket(this.app);
if (id) { if (id) {
Htk.Toast.showMessage(_('OrderUpdated')); Htk.Toast.showMessage(_('OrderUpdated'));
switch(this.hash.$.continue) { switch(this.hash.$.continue) {
case 'catalog': case 'catalog':
redirect = {form: 'ecomerce/catalog'}; this.hash.setAll({form: 'ecomerce/catalog'});
break; break;
default: default:
redirect = {form: 'ecomerce/basket'}; this.hash.setAll({form: 'ecomerce/basket', id});
if (id !== basket.orderId)
redirect.id = id;
} }
} else { } else {
const basket = new Hedera.Basket(this.app);
basket.loadIntoBasket(resultSet.fetchValue()); basket.loadIntoBasket(resultSet.fetchValue());
redirect = {form: 'ecomerce/catalog'}; this.hash.setAll({form: 'ecomerce/catalog'});
} }
this.hash.setAll(redirect);
}, },
onCancelClick() { onCancelClick() {

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

@ -7,7 +7,7 @@ export default new Class({
async open() { async open() {
const basket = new Hedera.Basket(this.app); const basket = new Hedera.Basket(this.app);
try { try {
await basket.checkOrder(this.hash.$.id); await basket.check(this.hash.$.id);
} catch (err) { } catch (err) {
Htk.Toast.showError(err.message); Htk.Toast.showError(err.message);
} }
@ -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

@ -35,10 +35,8 @@
<div class="vn-w-sm"> <div class="vn-w-sm">
<div class="box vn-pa-lg summary"> <div class="box vn-pa-lg summary">
<div> <div>
<h5>#{{order.id}}</h5>
<div class="delivery"> <div class="delivery">
<h6><t>ShippingInformation</t></h6> <h6><t>ShippingInformation</t></h6>
<p>{{order.nickname}}</p>
<p> <p>
<t>Delivery at</t> {{Vn.Value.format(order.sent, _('%D'))}} <t>Delivery at</t> {{Vn.Value.format(order.sent, _('%D'))}}
</p> </p>
@ -48,6 +46,7 @@
</div> </div>
<div id="address" class="address vn-mt-md"> <div id="address" class="address vn-mt-md">
<h6><t>DeliveryAddress</t></h6> <h6><t>DeliveryAddress</t></h6>
<p>{{order.nickname}}</p>
<p>{{order.street}}</p> <p>{{order.street}}</p>
<p>{{order.postalCode}}, {{order.city}}</p> <p>{{order.postalCode}}, {{order.city}}</p>
</div> </div>
@ -162,6 +161,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

@ -4,16 +4,29 @@ export default new Class({
Extends: Hedera.Form, Extends: Hedera.Form,
Template: require('./ui.xml'), Template: require('./ui.xml'),
onDownloadClick(column, value, row) { donwloadRenderer(column, invoice) {
var model = this.$.invoices; var invoiceId = invoice.$.id;
var hasPdf = model.get(row, 'hasPdf');
var id = model.get(row, 'id');
if (hasPdf && id) { if (invoice.$.hasPdf && invoiceId) {
let params = Vn.Url.makeUri({ access_token: this.conn.token }); var params = {
window.open(`/api/InvoiceOuts/${id}/download?${params}`); srv: 'rest:dms/invoice',
invoice: invoiceId,
access_token: this.conn.token
};
Object.assign(column, {
tip: _('Download PDF'),
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

@ -1,8 +1 @@
Pending: Pendents Pending: Pendents
PendingOrders: Comandes pendents
NewOrder: Nova comanda
ViewOrder: Veure comanda
RemoveOrder: Eliminar comanda
LoadOrderIntoCart: Carregar comanda a la cistella
AreYouSureDeleteOrder: Segur que vols esborrar la comanda?
OrderLoadedIntoBasket: Comanda carregada a la cistella!

View File

@ -1,8 +1 @@
Pending: Pending Pending: Pending
PendingOrders: Pending orders
NewOrder: New order
ViewOrder: View order
RemoveOrder: Delete order
LoadOrderIntoCart: Load order into cart
AreYouSureDeleteOrder: Are you sure you want to delete the order?
OrderLoadedIntoBasket: Order loaded into basket!

View File

@ -1,8 +1 @@
Pending: En attente Pending: Pendents
PendingOrders: Commandes en attente
NewOrder: Nouvelle commande
ViewOrder: Afficher la commande
RemoveOrder: Supprimer la commande
LoadOrderIntoCart: Charger la commande dans le panier
AreYouSureDeleteOrder: Êtes-vous sûr de vouloir supprimer la commande?
OrderLoadedIntoBasket: Commande chargée dans le panier!

View File

@ -1,8 +1 @@
Pending: Pendentes Pending: Pendientes
PendingOrders: Pedidos pendentes
NewOrder: Novo pedido
ViewOrder: Ver pedido
RemoveOrder: Excluir pedido
LoadOrderIntoCart: Carrega o pedido no carrinho
AreYouSureDeleteOrder: Tem certeza de que deseja excluir o pedido?
OrderLoadedIntoBasket: Pedido carregado na cesta!

View File

@ -14,7 +14,7 @@
class="htk-list box confirmed vn-w-sm" class="htk-list box confirmed vn-w-sm"
form-id="iter"> form-id="iter">
<db-model property="model" id="orders"> <db-model property="model" id="orders">
SELECT o.id, o.sent, o.deliveryMethodFk, o.taxableBase, SELECT o.id, o.sent, o.deliveryMethodFk, o.total,
a.nickname, am.description agency a.nickname, am.description agency
FROM myOrder o FROM myOrder o
JOIN myAddress a ON a.id = o.addressFk JOIN myAddress a ON a.id = o.addressFk
@ -33,7 +33,7 @@
<p>#{{iter.id}}</p> <p>#{{iter.id}}</p>
<p>{{iter.nickname}}</p> <p>{{iter.nickname}}</p>
<p>{{iter.agency}}</p> <p>{{iter.agency}}</p>
<p>{{Vn.Value.format(iter.taxableBase, '%.2d€')}}</p> <p>{{Vn.Value.format(iter.total, '%.2d€')}}</p>
</div> </div>
<div <div
class="actions" class="actions"

View File

@ -15,10 +15,8 @@
<label><t>Store</t></label> <label><t>Store</t></label>
<htk-combo form="lot" column="warehouse"> <htk-combo form="lot" column="warehouse">
<db-model property="model"> <db-model property="model">
SELECT id, name SELECT id, name FROM vn2008.warehouse
FROM vn.warehouse WHERE reserve ORDER BY name
WHERE hasAvailable
ORDER BY name
</db-model> </db-model>
</htk-combo> </htk-combo>
</div> </div>
@ -26,8 +24,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

@ -6,7 +6,7 @@ export default new Class({
activate() { activate() {
this.$.lot.assign({ this.$.lot.assign({
date: Date.vnNew(), date: new Date(),
useIds: false useIds: false
}); });
}, },

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>
@ -57,10 +57,8 @@
<label><t>Store</t></label> <label><t>Store</t></label>
<htk-combo form="lot"> <htk-combo form="lot">
<db-model property="model"> <db-model property="model">
SELECT id, name SELECT id, name FROM vn2008.warehouse
FROM vn.warehouse WHERE reserve ORDER BY name
WHERE hasAvailable
ORDER BY name
</db-model> </db-model>
</htk-combo> </htk-combo>
</div> </div>

View File

@ -40,7 +40,9 @@ export const routes = {
}, },
agencies: { agencies: {
packages: packages:
() => import('agencies/packages') () => import('agencies/packages'),
provinces:
() => import('agencies/provinces')
}, },
cms: { cms: {
about: about:

View File

@ -238,10 +238,9 @@ module.exports = new Class({
if (err instanceof Vn.JsonException) { if (err instanceof Vn.JsonException) {
const statusCode = err.statusCode; const statusCode = err.statusCode;
if (statusCode >= 400 && statusCode < 500) { if (statusCode >= 400 && statusCode < 500) {
if (err.statusCode == 403) { if (err.statusCode == 403)
Htk.Toast.showError(err.message || Htk.Toast.showError(_('You don\'t have enough privileges'));
_('You don\'t have enough privileges')); else {
} else {
switch (err.exception) { switch (err.exception) {
case 'UserDisabledError': case 'UserDisabledError':
case 'OutdatedVersionError': case 'OutdatedVersionError':

View File

@ -6,59 +6,30 @@ module.exports = class {
if (orderId) orderId = parseInt(orderId); if (orderId) orderId = parseInt(orderId);
this.orderId = orderId; this.orderId = orderId;
} }
async checkOrder(orderId) { async check(orderId) {
const resultSet = await this.app.conn.execQuery( const resultSet = await this.app.conn.execQuery(
'CALL myOrder_checkConfig(#id)', 'CALL myOrder_checkConfig(#id)',
{id: orderId} {id: orderId}
); );
resultSet.fetchValue(); resultSet.fetchValue();
} }
async check(checkoutContinue) { async checkRedirect(orderId) {
if (this.orderId) {
return await this.checkRedirect(checkoutContinue);
} else {
this.redirect();
return false;
}
}
async checkRedirect(checkoutContinue) {
try { try {
await this.checkOrder(this.orderId); await this.check(orderId);
return true; return true;
} catch(err) { } catch(err) {
if (err.exception == 'Vn.Lib.UserError') { Htk.Toast.showError(err.message);
switch(err.code) {
case 'orderConfirmed':
case 'orderNotOwnedByUser':
this.constructor.unload();
await this.redirect();
break;
default:
this.app.hash.setAll({ this.app.hash.setAll({
form: 'ecomerce/checkout', form: 'ecomerce/checkout',
id: this.orderId, id: orderId,
continue: checkoutContinue continue: 'catalog'
}); });
Htk.Toast.showError(err.message);
}
return false; return false;
} else
throw err;
}
}
async redirect() {
const resultSet = await this.app.conn.execQuery(
'SELECT COUNT(*) > 0 FROM myOrder');
if (resultSet.fetchValue()) {
this.app.hash.setAll({form: 'ecomerce/pending'});
Htk.Toast.showMessage(_('Load an order'));
} else {
this.app.hash.setAll({form: 'ecomerce/checkout'});
} }
} }
async load(orderId) { async load(orderId) {
this.loadIntoBasket(orderId); this.loadIntoBasket(orderId);
if (await this.checkRedirect('catalog')) if (!await this.checkRedirect(orderId)) return;
this.app.hash.setAll({ this.app.hash.setAll({
form: 'ecomerce/catalog' form: 'ecomerce/catalog'
}); });

View File

@ -63,22 +63,3 @@ Agencies: Agències
Configuration: Configuració Configuration: Configuració
Account: Compte Account: Compte
Addresses: Adreces Addresses: Adreces
Load an order: Si us plau carrega una comanda pendent a la cistella o en comença una de nova
Old password: Contrasenya antiga
New password: Nova contrasenya
Repeat password: Repetir contrasenya
Requirements: Requisits
Modify: Modificar
Password requirements: Requisits de contrasenya
characters long: caràcters de longitud
alphabetic characters: caràcters alfabètics
capital letters: majúscules
digits: dígits
symbols: símbols
Password changed!: Contrasenya modificada!
Password doesn't meet the requirements: ''
Passwords doesn't match: Les contrasenyes no coincideixen!
Passwords empty: Les contrasenyes en blanc
Change password: Canvia la contrasenya

View File

@ -59,5 +59,3 @@ Agencies: Agencies
Configuration: Configuration Configuration: Configuration
Account: Account Account: Account
Addresses: Addresses Addresses: Addresses
Load an order: Please load a pending order to the cart or start a new one

View File

@ -63,24 +63,3 @@ Agencies: Agencias
Configuration: Configuración Configuration: Configuración
Account: Cuenta Account: Cuenta
Addresses: Direcciones Addresses: Direcciones
Load an order: Por favor carga un pedido pendiente en la cesta o empieza uno nuevo
Old password: Contaseña antigua
New password: Nueva contraseña
Repeat password: Repetir contraseña
Requirements: Requisitos
Modify: Modificar
Password requirements: Requisitos de constraseña
characters long: carácteres de longitud
alphabetic characters: carácteres alfabéticos
capital letters: letras mayúsculas
digits: dígitos
symbols: 'símbolos. Ej: $%&.'
Password changed!: ¡Contraseña modificada!
Password doesn't meet the requirements: >-
La nueva contraseña no reune los requisitos de seguridad necesarios, pulsa en
info para más detalle
Passwords doesn't match: ¡Las contraseñas no coinciden!
Passwords empty: Contraseña vacía
Change password: Cambiar contraseña

View File

@ -5,7 +5,7 @@ Remind me: Retenir mon mot de passe
Log in as guest: Entrez en tant qu'invité Log in as guest: Entrez en tant qu'invité
Login: Se connecter Login: Se connecter
Login mail: info@verdnatura.es Login mail: info@verdnatura.es
Login phone: +33 783 285 437 Login phone: +33 781 533 900
Password forgotten? Push here: Vous avez oublié votre mot de passe? Password forgotten? Push here: Vous avez oublié votre mot de passe?
Yet you are not a customer?: Vous n'êtes pas encore client? Yet you are not a customer?: Vous n'êtes pas encore client?
Sign up: S'inscrire Sign up: S'inscrire
@ -63,22 +63,3 @@ Agencies: Agences
Configuration: Configuration Configuration: Configuration
Account: Compte Account: Compte
Addresses: Adresses Addresses: Adresses
Load an order: Veuillez télécharger une commande en attente dans le panier ou en démarrer une nouvelle
Old password: Ancien mot de passe
New password: Nouveau mot de passe
Repeat password: Répéter le mot de passe
Requirements: Exigences
Modify: Modifier
Password requirements: Mot de passe exigences
characters long: Longs caractères
alphabetic characters: les caractères alphabétiques
capital letters: lettres majuscules
digits: chiffres
symbols: 'symboles. Ej: $%&.'
Password changed!: Mot de passe modifié!
Password doesn't meet the requirements: ''
Passwords doesn't match: Les mots de passe ne correspondent pas!
Passwords empty: ''
Change password: Changer le mot de passe

View File

@ -61,22 +61,3 @@ Agencies: Agências
Configuration: Configuração Configuration: Configuração
Account: Conta Account: Conta
Addresses: Moradas Addresses: Moradas
Load an order: Carregue um pedido pendente no carrinho ou inicie um novo
Old password: Palavra-passe antiga
New password: Nova Palavra-passe
Repeat password: Repetir Palavra-passe
Requirements: Requisitos
Modify: Modificar
Password requirements: Requisitos de Palavra-passe
characters long: caracteres
alphabetic characters: caracteres alfabéticos
capital letters: letras maiúsculas
digits: dígitos
symbols: 'símbolos. Ej: $%&.'
Password changed!: Palavra-passe Modificada!
Password doesn't meet the requirements: Palavra-passe não atende aos requisitos
Passwords doesn't match: As Palavras-Passe não coincidem!
Passwords empty: Palavra-passe vazia
Change password: Mudar Palavra-passe

View File

@ -26,12 +26,6 @@ module.exports = new Class({
self._onSubmit(); self._onSubmit();
return false; return false;
}; };
if(this.hash.$.verificationToken){
this.$.changePassword.conn = this.conn;
this.$.changePassword.token = this.hash.$.verificationToken;
this.$.changePassword.open();
}
} }
,_onConnLoadChange(conn, isLoading) { ,_onConnLoadChange(conn, isLoading) {
@ -100,11 +94,7 @@ module.exports = new Class({
return; return;
} }
await this.conn.post('VnUsers/recoverPassword', { await this._conn.send('user/recover-password', {recoverUser});
user: recoverUser,
app: 'hedera'
});
Htk.Toast.showMessage(_('A mail has been sent wich you can recover your password')); Htk.Toast.showMessage(_('A mail has been sent wich you can recover your password'));
} }
}); });

View File

@ -54,5 +54,4 @@
</div> </div>
</div> </div>
</div> </div>
<htk-change-password id="change-password"/>
</vn> </vn>

View File

@ -141,7 +141,7 @@ module.exports = new Class({
} }
,goToCurrentMonth() { ,goToCurrentMonth() {
var date = Date.vnNew(); var date = new Date();
this.goToMonth(date.getFullYear(), date.getMonth()); this.goToMonth(date.getFullYear(), date.getMonth());
} }
@ -179,7 +179,7 @@ module.exports = new Class({
// Marks the current day // Marks the current day
var today = Date.vnNew(); var today = new Date();
if (this.year == today.getFullYear() if (this.year == today.getFullYear()
&& this.month == today.getMonth()) { && this.month == today.getMonth()) {

View File

@ -1,111 +0,0 @@
var Component = require('vn/component');
var Toast = require('../toast');
var Tpl = require('./ui.xml').default;
/**
* A change password popup.
*/
module.exports = new Class({
Extends: Component,
Tag: 'htk-change-password',
Properties: {
/**
* The token url.
*/
token: {
type: String
,set(value) {
this._token = value;
}
,get() {
return this._token;
}
},
conn: {
type: Db.Connection
,set(x) {
this._conn = x
}
,get() {
return this._conn;
}
},
user: {
type: Object
}
},
initialize(props) {
Component.prototype.initialize.call(this, props);
},
async open() {
this.passwordForm = await this.conn.get('UserPasswords/findOne')
this.loadTemplateFromString(Tpl);
this.$.oldPassword.value = '';
this.$.newPassword.value = '';
this.$.repeatPassword.value = '';
this.$.oldPassword.style.display = this.token ? 'none' : 'block';
this.$.changePassword.open();
if (this.token)
this.$.newPassword.focus();
else
this.$.oldPassword.focus();
},
async onPassModifyClick() {
const form = this.$.changePassword.node;
Vn.Node.disableInputs(form);
try {
const oldPassword = this.$.oldPassword.value;
const newPassword = this.$.newPassword.value;
const repeatedPassword = this.$.repeatPassword.value;
try {
if (newPassword == '' && repeatedPassword == '')
throw new Error(_('Passwords empty'));
if (newPassword !== repeatedPassword)
throw new Error(_('Passwords doesn\'t match'));
} catch (err) {
return Toast.showError(err.message);
}
const params = {newPassword};
try {
if (this.token) {
const headers = {
Authorization: this.token
};
await this.conn.post('VnUsers/reset-password', params, {headers});
} else {
params.userId = this.user.id;
params.oldPassword = oldPassword;
await this.conn.patch(`Accounts/change-password`, params);
}
} catch(err) {
Toast.showError(err.message);
if (this.token)
this.$.newPassword.select();
else
this.$.oldPassword.select();
return;
}
if(this.user)
await this.conn.open(this.user.name, newPassword);
this.$.changePassword.hide();
} finally {
Vn.Node.disableInputs(form, false);
}
Toast.showMessage(_('Password changed!'));
}
});

View File

@ -1,62 +0,0 @@
<vn>
<htk-popup
id="change-password"
modal="true">
<div property="child-node" class="htk-dialog vn-w-xs vn-pa-lg">
<div class="form">
<h5 class="vn-mb-md">
<t>Change password</t>
</h5>
<input
id="old-password"
type="password"
placeholder="_Old password"/>
<input
id="new-password"
type="password"
placeholder="_New password"
autocomplete="new-password"/>
<input
id="repeat-password"
type="password"
placeholder="_Repeat password"
autocomplete="new-password"/>
</div>
<div class="button-bar">
<button class="thin" on-click="this.onPassModifyClick()">
<t>Modify</t>
</button>
<button class="thin" on-click="this.$.passwordInfo.show()">
<t>Requirements</t>
</button>
<div class="clear"/>
</div>
</div>
</htk-popup>
<htk-popup
id="password-info"
modal="true">
<div property="child-node" class="htk-dialog pass-info vn-w-xs vn-pa-lg">
<h5 class="vn-mb-md">
<t>Password requirements</t>
</h5>
<ul>
<li>
{{this.passwordForm.length}} <t>characters long</t>
</li>
<li>
{{this.passwordForm.nAlpha}} <t>alphabetic characters</t>
</li>
<li>
{{this.passwordForm.nUpper}} <t>capital letters</t>
</li>
<li>
{{this.passwordForm.nDigits}} <t>digits</t>
</li>
<li>
{{this.passwordForm.nPunct}} <t>symbols</t>
</li>
</ul>
</div>
</htk-popup>
</vn>

View File

@ -19,7 +19,6 @@ Htk = module.exports = {
,List : require('./list') ,List : require('./list')
,Field : require('./field') ,Field : require('./field')
,Column : require('./column') ,Column : require('./column')
,ChangePassword : require('./change-password')
}; };
var Fields = { var Fields = {

View File

@ -192,7 +192,7 @@ module.exports = new Class({
} }
,_onFileUpload(cell) { ,_onFileUpload(cell) {
this._stamp = Date.vnNew().getTime(); this._stamp = new Date().getTime();
this._refreshSrc(cell); this._refreshSrc(cell);
this.popup.hide(); this.popup.hide();
} }

View File

@ -3,61 +3,69 @@
*/ */
module.exports = module.exports =
{ {
set: function(key, value, days) { set: function (key, value, days)
{
var strCookie = key + '=' + value + ';'; var strCookie = key + '=' + value + ';';
if (days != undefined) { if (days != undefined)
var date = Date.vnNew(); {
date.setTime(date.getTime() + days * 86400000); var date = new Date ();
strCookie += 'expires=' + date.toGMTString(); date.setTime (date.getTime () + days * 86400000);
strCookie += 'expires=' + date.toGMTString ();
} }
document.cookie = strCookie; document.cookie = strCookie;
} }
,unset: function(key) { ,unset: function (key)
this.set(key, '', -1); {
this.set (key, '', -1);
} }
,get: function(key) { ,get: function (key)
var cookie = new String(document.cookie); {
var start = cookie.indexOf(key + '='); var cookie = new String (document.cookie);
var start = cookie.indexOf (key + '=');
if (start != -1) { if (start != -1)
{
var end; var end;
start += key.length + 1; start += key.length + 1;
end = cookie.indexOf(';', start); end = cookie.indexOf (';', start);
if (end > 0) if (end > 0)
return cookie.substring(start, end); return cookie.substring (start, end);
else else
return cookie.substring(start); return cookie.substring (start);
} }
return null; return null;
} }
,getInt: function(key) { ,getInt: function (key)
var value = this.get(key); {
var value = this.get (key);
if (value != null) if (value != null)
return parseInt(value); return parseInt (value);
return null; return null;
} }
,getFloat: function(key) { ,getFloat: function (key)
var value = this.get(key); {
var value = this.get (key);
if (value != null) if (value != null)
return parseFloat(value); return parseFloat (value);
return null; return null;
} }
,check: function(key) { ,check: function (key)
return this.get(key) != null; {
return this.get (key) != null;
} }
}; };

View File

@ -6,22 +6,6 @@ Date.prototype.clone = function() {
return new Date(this.getTime()); return new Date(this.getTime());
} }
Date.vnUTC = () => {
const env = process.env.NODE_ENV;
if (!env || env === 'development')
return new Date(Date.UTC(2001, 0, 1, 11));
return new Date();
};
Date.vnNew = () => {
return new Date(Date.vnUTC());
};
Date.vnNow = () => {
return new Date(Date.vnUTC()).getTime();
};
module.exports = module.exports =
{ {
WDays: [ WDays: [
@ -71,7 +55,7 @@ module.exports =
,'Dec' ,'Dec'
] ]
,tokenD: '%A, %B %e %Y' ,tokenD: '%A, %B %e'
,regexp: new RegExp('%[a-zA-Z]', 'g') ,regexp: new RegExp('%[a-zA-Z]', 'g')
@ -117,7 +101,7 @@ module.exports =
// Year with 4 digits // Year with 4 digits
case 'Y': return d.getFullYear(); case 'Y': return d.getFullYear();
// Complete date // Complete date without year
case 'D': return _(this.tokenD).replace(this.regexp, this.regexpFunc.bind(this, d)); case 'D': return _(this.tokenD).replace(this.regexp, this.regexpFunc.bind(this, d));
} }

View File

@ -85,7 +85,6 @@ module.exports = new Class({
const config = { const config = {
headers: {'Authorization': token} headers: {'Authorization': token}
}; };
await this.send('user/logout', null, config);
await this.post('Accounts/logout', null, config); await this.post('Accounts/logout', null, config);
} }
}, },
@ -140,16 +139,15 @@ module.exports = new Class({
/* /*
* Called when REST response is received. * Called when REST response is received.
*/ */
async sendWithUrl(method, url, params, config) { async sendWithUrl(method, url, params) {
config = Object.assign({}, config, { return this.request({
method, method,
url, url,
data: Vn.Url.makeUri(params) data: Vn.Url.makeUri(params),
}); headers: {
config.headers = Object.assign({}, config.headers, {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
}
}); });
return this.request(config);
}, },
async get(url, config) { async get(url, config) {

View File

@ -36,7 +36,7 @@ Sep: Set
Oct: Oct Oct: Oct
Nov: Nov Nov: Nov
Dec: Des Dec: Des
'%A, %B %e %Y': '%A, %e de %B de %Y' '%A, %B %e': '%A, %e de %B'
Something went wrong: Alguna cosa ha anat malament Something went wrong: Alguna cosa ha anat malament
'The server does not respond, please check your Internet connection': 'El servidor no respon, si us plau comprova la teva connexió a Internet' 'The server does not respond, please check your Internet connection': 'El servidor no respon, si us plau comprova la teva connexió a Internet'
Accept: Acceptar Accept: Acceptar

View File

@ -36,6 +36,6 @@ Sep: Sep
Oct: Oct Oct: Oct
Nov: Nov Nov: Nov
Dec: Dic Dec: Dic
'%A, %B %e %Y': '%A, %B %e %Y' '%A, %B %e': '%A, %B %e'
Something went wrong: Something went wrong Something went wrong: Something went wrong
'The server does not respond, please check your Internet connection': 'The server does not respond, please check you Internet connection' 'The server does not respond, please check your Internet connection': 'The server does not respond, please check you Internet connection'

View File

@ -36,7 +36,7 @@ Sep: Sep
Oct: Oct Oct: Oct
Nov: Nov Nov: Nov
Dec: Dic Dec: Dic
'%A, %B %e %Y': '%A, %e de %B de %Y' '%A, %B %e': '%A, %e de %B'
Something went wrong: Algo salió mal Something went wrong: Algo salió mal
'The server does not respond, please check your Internet connection': 'El servidor no responde, por favor comprueba tu conexión a Internet' 'The server does not respond, please check your Internet connection': 'El servidor no responde, por favor comprueba tu conexión a Internet'
Accept: Aceptar Accept: Aceptar

View File

@ -36,7 +36,7 @@ Sep: Sep
Oct: Oct Oct: Oct
Nov: Nov Nov: Nov
Dec: Déc Dec: Déc
'%A, %B %e %Y': '%A, %B %e %Y' '%A, %B %e': '%A, %B %e'
Something went wrong: Quelque-chose s'est mal passé Something went wrong: Quelque-chose s'est mal passé
'The server does not respond, please check your Internet connection': 'Le serveur ne répond pas, s''il vous plaît vérifier votre connexion Internet' 'The server does not respond, please check your Internet connection': 'Le serveur ne répond pas, s''il vous plaît vérifier votre connexion Internet'
Accept: Accepter Accept: Accepter

View File

@ -36,6 +36,6 @@ Sep: Есд
Oct: Ара Oct: Ара
Nov: Арв Nov: Арв
Dec: Арв Dec: Арв
'%A, %B %e %Y': '%A, %B %e %Y' '%A, %B %e': '%A, %B %e'
Something went wrong: Something went wrong Something went wrong: Something went wrong
'The server does not respond, please check your Internet connection': 'The server does not respond, please check you Internet connection' 'The server does not respond, please check your Internet connection': 'The server does not respond, please check you Internet connection'

View File

@ -36,7 +36,7 @@ Sep: Set
Oct: Out Oct: Out
Nov: Nov Nov: Nov
Dec: Dez Dec: Dez
'%A, %B %e %Y': '%A, %B %e %Y' '%A, %B %e': '%A, %B %e'
Something went wrong: Algo deu errado Something went wrong: Algo deu errado
'The server does not respond, please check your Internet connection': 'O servidor não responde, por favor, verifique sua conexão com a Internet' 'The server does not respond, please check your Internet connection': 'O servidor não responde, por favor, verifique sua conexão com a Internet'
Accept: Aceitar Accept: Aceitar

1678
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "hedera-web", "name": "hedera-web",
"version": "25.4.4", "version": "22.48.10",
"description": "Verdnatura web page", "description": "Verdnatura web page",
"license": "GPL-3.0", "license": "GPL-3.0",
"repository": { "repository": {
@ -21,9 +21,9 @@
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"json-loader": "^0.5.7", "json-loader": "^0.5.7",
"mini-css-extract-plugin": "^2.7.0", "mini-css-extract-plugin": "^2.7.0",
"node-sass": "^9.0.0", "node-sass": "^7.0.1",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
"sass-loader": "^13.3.2", "sass-loader": "^12.6.0",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"webpack": "^5.75.0", "webpack": "^5.75.0",
@ -42,8 +42,7 @@
"scripts": { "scripts": {
"front": "webpack serve --open", "front": "webpack serve --open",
"back": "cd ../salix && gulp backOnly", "back": "cd ../salix && gulp backOnly",
"php": "php -S 127.0.0.1:3001 -t . index.php", "db": "cd ../vn-database && myvc run",
"db": "cd ../vn-database && myt run",
"build": "rm -rf build/ ; webpack", "build": "rm -rf build/ ; webpack",
"clean": "rm -rf build/" "clean": "rm -rf build/"
} }

View File

@ -10,7 +10,7 @@ class Supplant extends Vn\Web\JsonRequest {
'SELECT id FROM account.user WHERE `name` = #', 'SELECT id FROM account.user WHERE `name` = #',
[$_REQUEST['supplantUser']] [$_REQUEST['supplantUser']]
); );
/*
$isClient = $db->getValue( $isClient = $db->getValue(
'SELECT COUNT(*) > 0 FROM vn.client WHERE id = #', 'SELECT COUNT(*) > 0 FROM vn.client WHERE id = #',
[$userId] [$userId]
@ -24,7 +24,7 @@ class Supplant extends Vn\Web\JsonRequest {
); );
if ($hasAccount) if ($hasAccount)
throw new Web\ForbiddenException(s('The user is not impersonable')); throw new Web\ForbiddenException(s('The user is not impersonable'));
*/
return $this->service->createToken($_REQUEST['supplantUser']); return $this->service->createToken($_REQUEST['supplantUser']);
} }
} }

View File

@ -1,7 +1,6 @@
<?php <?php
use Vn\Lib; use Vn\Lib;
use Vn\Lib\Locale;
use Vn\Web\Security; use Vn\Web\Security;
use Vn\Lib\Type; use Vn\Lib\Type;
@ -37,17 +36,13 @@ class Query extends Vn\Web\JsonRequest {
while ($db->moreResults() && $db->nextResult()); while ($db->moreResults() && $db->nextResult());
if ($db->checkWarnings() if ($db->checkWarnings()
&& ($result = $db->query('SHOW WARNINGS'))) { &&($result = $db->query('SHOW WARNINGS'))) {
$sql = $sql = 'SELECT `description`, @warn `code`
'SELECT IFNULL(i.`description`, m.`description`) `description`, @warn `code` FROM `message` WHERE `code` = @warn';
FROM `message` m
LEFT JOIN `messageI18n` i
ON i.`code` = m.`code` AND i.lang = #
WHERE m.`code` = @warn';
while ($row = $result->fetch_object()) { while ($row = $result->fetch_object()) {
if ($row->Code == 1265 if ($row->Code == 1265
&& ($warning = $db->getObject($sql, [Locale::get()]))) &&($warning = $db->getObject($sql)))
trigger_error("{$warning->code}: {$warning->description}", E_USER_WARNING); trigger_error("{$warning->code}: {$warning->description}", E_USER_WARNING);
else else
trigger_error("{$row->Code}: {$row->Message}", E_USER_WARNING); trigger_error("{$row->Code}: {$row->Message}", E_USER_WARNING);

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

@ -101,6 +101,12 @@ class Upload extends Vn\Web\JsonRequest {
$symbolicSrc = "../full/$fileName"; $symbolicSrc = "../full/$fileName";
$image = Image::create($tmpName); $image = Image::create($tmpName);
Image::resizeSave($image, $fullFile, $info['maxHeight'], $info['maxWidth']);
foreach ($info['sizes'] as $size => $i) {
$dstFile = "$collectionPath/$size/$fileName";
Image::resizeSave($image, $dstFile, $i['height'], $i['width'], $i['crop'], $symbolicSrc);
}
$query = $query =
"INSERT INTO `image` "INSERT INTO `image`
@ -115,13 +121,6 @@ class Upload extends Vn\Web\JsonRequest {
'name' => $name 'name' => $name
]); ]);
Image::resizeSave($image, $fullFile, $info['maxHeight'], $info['maxWidth']);
foreach ($info['sizes'] as $size => $i) {
$dstFile = "$collectionPath/$size/$fileName";
Image::resizeSave($image, $dstFile, $i['height'], $i['width'], $i['crop'], $symbolicSrc);
}
imagedestroy($image); imagedestroy($image);
unlink($tmpName); unlink($tmpName);
return TRUE; return TRUE;

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())

40
rest/user/account.php Normal file
View File

@ -0,0 +1,40 @@
<?php
class Account {
static function trySync($db, $userName, $password = NULL) {
$sync = $db->getValue(
'SELECT COUNT(*) > 0 FROM account.userSync WHERE name = #',
[$userName]
);
if ($sync)
self::sync($db, $userName, $password);
}
static function sync($db, $userName, $password = NULL) {
$bcryptPassword = password_hash($password, PASSWORD_BCRYPT);
$userId = $db->getValue(
'SELECT id FROM account.user WHERE `name` = #',
[$userName]
);
$db->query(
'UPDATE account.user SET
bcryptPassword = #
WHERE id = #',
[$bcryptPassword, $userId]
);
$hasAccount = $db->getValue(
'SELECT COUNT(*) > 0
FROM account.user u
JOIN account.account a ON u.id = a.id
WHERE u.name = #',
[$userName]
);
if (!$hasAccount)
$db->query('DELETE FROM account.userSync WHERE name = #',
[$userName]
);
}
}

View File

@ -1,7 +0,0 @@
<?php
class Logout extends Vn\Web\JsonRequest {
function run($db) {
$_SESSION['user'] = null;
}
}

View File

@ -48,13 +48,13 @@ class JsonService extends RestService {
$json = new JsonException(); $json = new JsonException();
if (_ENABLE_DEBUG || $errno & $eUser) { if (_ENABLE_DEBUG || $errno & $eUser)
$json->message = $message; $json->message = $message;
$json->code = $errno; else
} else
$json->message = s('Something went wrong'); $json->message = s('Something went wrong');
if (_ENABLE_DEBUG) { if (_ENABLE_DEBUG) {
$json->code = $errno;
$json->file = $file; $json->file = $file;
$json->line = $line; $json->line = $line;
} }
@ -79,13 +79,13 @@ class JsonService extends RestService {
if (_ENABLE_DEBUG || $e instanceof Lib\UserException) { if (_ENABLE_DEBUG || $e instanceof Lib\UserException) {
$json->exception = get_class($e); $json->exception = get_class($e);
$json->message = $e->getMessage(); $json->message = $e->getMessage();
$json->code = $e->getCode();
} else { } else {
$json->exception = 'Exception'; $json->exception = 'Exception';
$json->message = s('Something went wrong'); $json->message = s('Something went wrong');
} }
if (_ENABLE_DEBUG) { if (_ENABLE_DEBUG) {
$json->code = $e->getCode();
$json->file = $e->getFile(); $json->file = $e->getFile();
$json->line = $e->getLine(); $json->line = $e->getLine();
$json->trace = $e->getTrace(); $json->trace = $e->getTrace();

View File

@ -5,7 +5,6 @@ namespace Vn\Web;
require_once 'libphp-phpmailer/autoload.php'; require_once 'libphp-phpmailer/autoload.php';
use Vn\Lib\UserException; use Vn\Lib\UserException;
use PHPMailer\PHPMailer\PHPMailer;
class Mailer { class Mailer {
private $conf; private $conf;
@ -20,7 +19,7 @@ class Mailer {
function createObject($mailTo, $body, $subject) { function createObject($mailTo, $body, $subject) {
$conf = $this->conf; $conf = $this->conf;
$mail = new PHPMailer(); $mail = new \PHPMailer();
$mail->isSMTP(); $mail->isSMTP();
$mail->Host = $conf->host; $mail->Host = $conf->host;

View File

@ -54,12 +54,8 @@ class RestService extends Service {
if ($e->getCode() == 1644) { if ($e->getCode() == 1644) {
$eMessage = $e->getMessage(); $eMessage = $e->getMessage();
$tMessage = $db->getValue( $tMessage = $db->getValue(
'SELECT IFNULL(i.`description`, m.`description`) `description` 'SELECT `description` FROM `message` WHERE `code` = #',
FROM `message` m [$eMessage]
LEFT JOIN `messageI18n` i
ON i.`code` = m.`code` AND i.lang = #
WHERE m.`code` = #',
[Locale::get(), $eMessage]
); );
if (!$tMessage) $tMessage = $eMessage; if (!$tMessage) $tMessage = $eMessage;
throw new Lib\UserException($tMessage, $eMessage); throw new Lib\UserException($tMessage, $eMessage);

Some files were not shown because too many files have changed in this diff Show More