Compare commits

...

26 Commits

Author SHA1 Message Date
Andreas Palm
95cb03f43b Merge branch 'master' into cleanup 2024-10-23 10:34:00 +02:00
OpenXE
3178f12821 auftrag refresh on list only when no cronjob is set 2024-10-21 20:09:25 +02:00
OpenXE
05d7a394b4 mahnwesen refresh status button reinstated 2024-10-20 13:59:56 +02:00
OpenXE
d92105316e mahnwesen exclude cancelled items 2024-10-18 18:55:37 +02:00
OpenXE
69ef3d4ed6 auftrag refresh only open items 2024-10-18 18:41:17 +02:00
OpenXE
943bac22a3 mahnwesen changed tab behaviour, tabs always visible with counter 2024-10-16 14:30:12 +02:00
OpenXE
bf242623c4 mahnwesen bugfix tab logic 2024-10-15 15:31:21 +02:00
OpenXE
1f7b1af1f1 mahnwesen added tabs 2024-10-14 18:55:46 +02:00
OpenXE
29040e71b5 rechnung schnelleingabe added skonto display 2024-10-05 11:50:25 +02:00
Andreas Palm
87c9feba1e Merge branch 'refs/heads/master' into cleanup 2024-10-04 22:51:33 +02:00
Andreas Palm
b59bd65635 Update primevue to v4 2024-10-04 22:49:08 +02:00
OpenXE
a3255065e9 rechnung schnelleingabe initial 2024-10-04 16:40:36 +02:00
OpenXE
390a2054ec bugfix artikelimport 2024-10-02 17:10:22 +02:00
OpenXE
fe75b1b716 rechnung hide delivery date if = '0000-00-00' 2024-10-02 11:23:53 +02:00
OpenXE
fec1e628f3 autosort navigation 2024-10-01 17:32:20 +02:00
Andreas Palm
eddd41dfd6 Update vite, vue, axios, glob 2024-09-29 23:03:28 +02:00
Andreas Palm
84c34fa151 Merge branch 'refs/heads/master' into cleanup 2024-09-28 23:15:07 +02:00
OpenXE
a358125eae woocommerce ignore_ssl option 2024-09-18 15:50:48 +02:00
OpenXE
6f197e257c shopimport use grouprice of shop 2024-09-17 14:07:01 +02:00
OpenXE
c7b84603d4 Bugfix woocommerce ImportUpdateAuftrag tracking 2024-09-15 16:33:39 +02:00
OpenXE
24f6623016 bugfix OrderStatusUpdateRequest shipments default value empty array 2024-09-15 16:32:38 +02:00
OpenXE
ffaca2a7f3 bugfix woocommerce shoprueckmeldung 2024-09-15 16:06:44 +02:00
OpenXE
1710318e80 bugfix erpapi importauftrag shopextid 2024-09-15 16:05:33 +02:00
OpenXE
6d13973c06 woocommerce bugfix import order with product variation 2024-09-14 12:45:13 +02:00
OpenXE
0b807455ca woocommerce migrated logging from logfile to logger 2024-09-14 12:44:21 +02:00
OpenXE
d494778fad class.remote.php added additional logging 2024-09-14 12:39:50 +02:00
35 changed files with 3374 additions and 900 deletions

View File

@ -1,5 +1,5 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-FileCopyrightText: 2023-2024 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
@ -9,6 +9,7 @@ import {ref, onMounted} from "vue";
import axios from "axios";
import Dialog from "primevue/dialog";
import Listbox from "primevue/listbox";
import Fluid from "primevue/fluid";
import Button from "primevue/button";
import {AlertErrorHandler} from '@res/js/ajaxErrorHandler';
@ -36,17 +37,19 @@ async function save() {
<template>
<Dialog visible modal header="Globale Optionen hinzufügen" style="width: 500px" @update:visible="emit('close')">
<div v-if="model" class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProductOptions" style="padding-top: 5px;">Optionen:</label>
<Listbox multiple
:options="model"
optionGroupLabel="name"
optionGroupChildren="options"
optionLabel="name"
optionValue="id"
listStyle="height: 200px"
v-model="selected" />
</div>
<Fluid>
<div v-if="model" class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProductOptions" style="padding-top: 5px;">Optionen:</label>
<Listbox multiple
:options="model"
option-group-label="name"
option-group-children="options"
option-label="name"
option-value="id"
list-style="height: 200px"
v-model="selected" />
</div>
</Fluid>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="HINZUFÜGEN" @click="save" :disabled="selected.length === 0"/>

View File

@ -1,7 +1,14 @@
<!--
SPDX-FileCopyrightText: 2023-2024 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<script setup>
import Button from "primevue/button";
import Dialog from "primevue/dialog";
import MultiSelect from "primevue/multiselect";
import Fluid from "primevue/fluid";
import {onMounted, ref} from "vue";
import axios from "axios";
import {AlertErrorHandler} from "@res/js/ajaxErrorHandler";
@ -16,27 +23,33 @@ const model = ref({});
onMounted(async () => {
model.value = await axios.get('index.php?module=matrixprodukt&action=artikel&cmd=createMissing', {
params: {...props}
}).then(response => { return {...props, ...response.data}})
}).then(response => {
return {...props, ...response.data}
})
})
async function save() {
await axios.post('index.php?module=matrixprodukt&action=artikel&cmd=createMissing', {...props, ...model.value})
.catch(AlertErrorHandler)
.then(() => {emit('save')});
.then(() => {
emit('save')
});
}
</script>
<template>
<Dialog visible modal header="Variante" style="width: 500px" @update:visible="emit('close')">
<div class="grid gap-1" style="grid-template-columns: 25% 75%;">
<template v-for="group in model.groups">
<label>{{ group.name }}</label>
<MultiSelect v-model="group.selected" :options="group.options" optionLabel="name" optionValue="value" />
</template>
</div>
<Fluid>
<div class="grid gap-1" style="grid-template-columns: 25% 75%;" autofocus>
<template v-for="group in model.groups">
<label>{{ group.name }}</label>
<MultiSelect v-model="group.selected" :options="group.options" option-label="name" option-value="value"/>
</template>
</div>
</Fluid>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="ERSTELLEN" @click="save" />
<Button label="ABBRECHEN" @click="emit('close')"/>
<Button label="ERSTELLEN" @click="save"/>
</template>
</Dialog>
</template>

View File

@ -1,5 +1,5 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-FileCopyrightText: 2023-2024 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
@ -9,6 +9,10 @@ import {ref, onMounted} from "vue";
import axios from "axios";
import Dialog from "primevue/dialog";
import Button from "primevue/button";
import InputText from "primevue/inputtext";
import InputNumber from "primevue/inputnumber";
import Checkbox from "primevue/checkbox";
import Fluid from "primevue/fluid";
import {AlertErrorHandler} from "@res/js/ajaxErrorHandler";
import AutoComplete from "@res/vue/AutoComplete.vue";
@ -44,26 +48,28 @@ async function save() {
</script>
<template>
<Dialog visible modal header="Gruppe anlegen/bearbeiten" style="width: 500px" @update:visible="emit('close')" class="p-fluid">
<div class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProduct_group_name">Name:</label>
<input type="text" v-model="model.name" required />
<label for="matrixProduct_group_nameExternal">Name Extern:</label>
<input type="text" v-model="model.nameExternal" />
<label for="matrixProduct_group_project">Projekt:</label>
<AutoComplete
v-model="model.project"
:optionLabel="item => [item.abkuerzung, item.name].join(' ')"
ajaxFilter="projektname"
forceSelection
/>
<label v-if="articleId" for="matrixProduct_group_sort">Sortierung:</label>
<input v-if="articleId" type="text" v-model="model.sort">
<label for="matrixProduct_group_required">Pflicht:</label>
<input type="checkbox" v-model="model.required" class="justify-self-start">
<label for="matrixProduct_group_active">Aktiv:</label>
<input type="checkbox" v-model="model.active" class="justify-self-start">
</div>
<Dialog visible modal header="Gruppe anlegen/bearbeiten" style="width: 500px" @update:visible="emit('close')">
<Fluid>
<div class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProduct_group_name">Name:</label>
<InputText id="matrixProduct_group_name" v-model="model.name" autofocus required />
<label for="matrixProduct_group_nameExternal">Name Extern:</label>
<InputText id="matrixProduct_group_nameExternal" v-model="model.nameExternal" />
<label for="matrixProduct_group_project">Projekt:</label>
<AutoComplete input-id="matrixProduct_group_project"
v-model="model.project"
:optionLabel="item => [item.abkuerzung, item.name].join(' ')"
ajaxFilter="projektname"
forceSelection
/>
<label v-if="articleId" for="matrixProduct_group_sort">Sortierung:</label>
<InputNumber v-if="articleId" v-model="model.sort" input-id="matrixProduct_group_sort" show-buttons />
<label for="matrixProduct_group_required">Pflicht:</label>
<Checkbox v-model="model.required" binary />
<label for="matrixProduct_group_active">Aktiv:</label>
<Checkbox v-model="model.active" binary />
</div>
</Fluid>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="SPEICHERN" @click="save" :disabled="!model.name"/>

View File

@ -1,5 +1,5 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-FileCopyrightText: 2023-2024 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
@ -9,6 +9,10 @@ import {ref, onMounted} from "vue";
import axios from "axios";
import Button from "primevue/button";
import Dialog from "primevue/dialog";
import Fluid from "primevue/fluid";
import InputText from "primevue/inputtext";
import InputNumber from "primevue/inputnumber";
import Checkbox from "primevue/checkbox";
import {AlertErrorHandler} from "@res/js/ajaxErrorHandler";
const props = defineProps({
@ -43,18 +47,20 @@ async function save() {
<template>
<Dialog visible modal header="Option anlegen/bearbeiten" style="width: 500px" @update:visible="emit('close')">
<div class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProduct_option_name">Name:</label>
<input id="matrixProduct_option_name" type="text" v-model="model.name" required />
<label for="matrixProduct_option_nameExternal">Name Extern:</label>
<input id="matrixProduct_option_nameExternal" type="text" v-model="model.nameExternal" />
<label for="matrixProduct_option_articleNumberSuffix">Artikelnummer-Suffix:</label>
<input id="matrixProduct_option_articleNumberSuffix" type="text" v-model="model.articleNumberSuffix" />
<label for="matrixProduct_option_sort">Sortierung:</label>
<input id="matrixProduct_option_sort" type="text" v-model="model.sort" />
<label for="matrixProduct_option_active">Aktiv:</label>
<input id="matrixProduct_option_active" type="checkbox" v-model="model.active" class="justify-self-start" />
</div>
<Fluid>
<div class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProduct_option_name">Name:</label>
<InputText id="matrixProduct_option_name" v-model="model.name" required autofocus />
<label for="matrixProduct_option_nameExternal">Name Extern:</label>
<InputText id="matrixProduct_option_nameExternal" v-model="model.nameExternal" />
<label for="matrixProduct_option_articleNumberSuffix">Artikelnummer-Suffix:</label>
<InputText id="matrixProduct_option_articleNumberSuffix" v-model="model.articleNumberSuffix" />
<label for="matrixProduct_option_sort">Sortierung:</label>
<InputNumber input-id="matrixProduct_option_sort" v-model="model.sort" show-buttons />
<label for="matrixProduct_option_active">Aktiv:</label>
<Checkbox input-id="matrixProduct_option_active" v-model="model.active" binary />
</div>
</Fluid>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="SPEICHERN" @click="save" :disabled="!model.name" />

View File

@ -1,5 +1,5 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-FileCopyrightText: 2023-2024 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
@ -9,7 +9,9 @@ import {ref, onMounted} from "vue";
import axios from "axios";
import Dialog from "primevue/dialog";
import Button from "primevue/button";
import Dropdown from "primevue/dropdown";
import Fluid from "primevue/fluid";
import InputText from "primevue/inputtext";
import Select from "primevue/select";
import {AlertErrorHandler} from "@res/js/ajaxErrorHandler";
@ -59,24 +61,26 @@ function ready() {
</script>
<template>
<Dialog visible modal header="Übersetzung anlegen/bearbeiten" style="width: 500px" @update:visible="emit('close')" class="p-fluid">
<div class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProduct_nameFrom">DE Name:</label>
<input type="text" v-model="model.nameFrom" required />
<label for="matrixProduct_nameExternalFrom">DE Name Extern:</label>
<input type="text" v-model="model.nameExternalFrom" />
<label for="matrixProduct_languageTo">Sprache:</label>
<Dropdown
v-model="model.languageTo"
:options="languages"
option-label="bezeichnung_de"
option-value="iso"
/>
<label for="matrixProduct_nameTo">Übersetzung Name:</label>
<input type="text" v-model="model.nameTo" required>
<label for="matrixProduct_nameTo">Übersetzung Name Extern:</label>
<input type="text" v-model="model.nameExternalTo" :required="model.nameExternalFrom">
</div>
<Dialog visible modal header="Übersetzung anlegen/bearbeiten" style="width: 500px" @update:visible="emit('close')">
<Fluid>
<div class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProduct_nameFrom">DE Name:</label>
<InputText v-model="model.nameFrom" required autofocus />
<label for="matrixProduct_nameExternalFrom">DE Name Extern:</label>
<InputText v-model="model.nameExternalFrom" />
<label for="matrixProduct_languageTo">Sprache:</label>
<Select
v-model="model.languageTo"
:options="languages"
option-label="bezeichnung_de"
option-value="iso"
/>
<label for="matrixProduct_nameTo">Übersetzung Name:</label>
<InputText v-model="model.nameTo" required />
<label for="matrixProduct_nameTo">Übersetzung Name Extern:</label>
<InputText v-model="model.nameExternalTo" :required="model.nameExternalFrom" />
</div>
</Fluid>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="SPEICHERN" @click="save" :disabled="!ready()"/>

View File

@ -1,5 +1,5 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-FileCopyrightText: 2023-2024 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
@ -8,7 +8,8 @@ SPDX-License-Identifier: LicenseRef-EGPL-3.1
import AutoComplete from "@res/vue/AutoComplete.vue";
import Button from "primevue/button";
import Dialog from "primevue/dialog";
import Dropdown from "primevue/dropdown";
import Select from "primevue/select";
import Fluid from "primevue/fluid";
import {onMounted, ref} from "vue";
import axios from "axios";
import {AlertErrorHandler} from "@res/js/ajaxErrorHandler";
@ -41,18 +42,21 @@ const buttons = {
<template>
<Dialog visible modal header="Variante" style="width: 500px" @update:visible="emit('close')">
<div class="grid gap-1" style="grid-template-columns: 25% 75%;">
<label>Artikel</label>
<AutoComplete v-model="model.variant"
:optionLabel="(item) => [item.nummer, item.name].join(' ')"
ajax-filter="artikelnummer"
forceSelection
/>
<template v-for="group in model.groups">
<label>{{ group.name }}</label>
<Dropdown v-model="group.selected" :options="group.options" optionLabel="name" optionValue="value" />
</template>
</div>
<Fluid>
<div class="grid gap-1" style="grid-template-columns: 25% 75%;">
<label>Artikel</label>
<AutoComplete v-model="model.variant"
:option-label="(item) => [item.nummer, item.name].join(' ')"
ajax-filter="artikelnummer"
force-selection
autofocus
/>
<template v-for="group in model.groups">
<label>{{ group.name }}</label>
<Select v-model="group.selected" :options="group.options" option-label="name" option-value="value" />
</template>
</div>
</Fluid>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="SPEICHERN" @click="save" />

View File

@ -1,8 +1,8 @@
// SPDX-FileCopyrightText: 2023 Andreas Palm
// SPDX-FileCopyrightText: 2023-2024 Andreas Palm
//
// SPDX-License-Identifier: LicenseRef-EGPL-3.1
import App from "./App.vue";
import {createVueApp} from "@res/js/vue";
const app = createVueApp(App).mount('#vueapp')
createVueApp(App).mount('#vueapp')

View File

@ -26,7 +26,7 @@ class OrderStatusUpdateRequest
/**
* @var Shipment[] list of shipments for this order
*/
public array $shipments;
public array $shipments = array();
public function getTrackingNumberList() : array {
$list = [];
@ -45,4 +45,4 @@ class OrderStatusUpdateRequest
}
return $list;
}
}
}

805
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,10 +13,13 @@
"raml2html-werk-theme": "^1.3.4"
},
"dependencies": {
"@primevue/icons": "^4.0.7",
"@primevue/themes": "^4.0.7",
"@vitejs/plugin-vue": "^5",
"axios": "^1",
"glob": "^10.3.12",
"primevue": "^3",
"glob": "^11",
"primeicons": "^7.0.0",
"primevue": "^4",
"vite": "^5",
"vue": "^3"
}

View File

@ -194,6 +194,11 @@ class HTMLInput
name=\"{$this->name}\" value=\"{$this->value}\" size=\"{$this->size}\"
maxlength=\"{$this->maxlength}\" {$this->readonly} {$this->disabled}>";
break;
case "money":
$html = "<input type=\"number\" step=\"any\" id=\"{$this->id}\" class=\"{$this->class}\" tabindex=\"{$this->tabindex}\"
name=\"{$this->name}\" value=\"".preg_replace("/\"/","&quot;",$this->value)."\" size=\"{$this->size}\" placeholder=\"{$this->placeholder}\"
maxlength=\"{$this->maxlength}\" {$this->readonly} {$this->disabled} [COMMONREADONLYINPUT]>";
break;
}
return $html;

View File

@ -28,10 +28,6 @@
gap: 0.25rem;
}
.grid label {
.grid label, .grid .p-checkbox {
padding-top: 5px;
}
.grid input[type=text] {
width: 100%;
}

View File

@ -3,10 +3,23 @@
// SPDX-License-Identifier: LicenseRef-EGPL-3.1
import '@res/css/vue.css';
import '@res/css/primevue/_base.css';
import {createApp} from "vue";
import PrimeVue from "primevue/config";
import Aura from '@primevue/themes/aura';
import {definePreset} from "@primevue/themes";
const OpenXePreset = definePreset(Aura, {
});
export function createVueApp(rootComponent, rootProps) {
return createApp(rootComponent, rootProps).use(PrimeVue);
return createApp(rootComponent, rootProps)
.use(PrimeVue, {
theme: {
preset: OpenXePreset,
options: {
darkModeSelector: '.openXeDarkMode'
}
}
});
}

View File

@ -8,12 +8,13 @@ SPDX-License-Identifier: AGPL-3.0-only
import {ref} from "vue";
import AutoComplete from "primevue/autocomplete";
import axios from "axios";
import SearchIcon from "primevue/icons/search";
import SearchIcon from "@primevue/icons/search";
const props = defineProps({
ajaxFilter: String,
modelValue: null,
forceSelection: Boolean
forceSelection: Boolean,
inputId: String,
});
const emit = defineEmits(['update:modelValue']);
@ -42,6 +43,7 @@ async function search(event) {
dataKey="id"
:forceSelection="forceSelection"
dropdown
:input-id="inputId"
>
<template #dropdownicon>
<SearchIcon />

View File

@ -1,11 +1,11 @@
{
"classes/Modules/MatrixProduct/www/js/entry.js": {
"file": "modules/MatrixProduct-9c7DrvjY.js",
"file": "modules/MatrixProduct-BFgn1wkj.js",
"name": "modules/MatrixProduct",
"src": "classes/Modules/MatrixProduct/www/js/entry.js",
"isEntry": true,
"css": [
"assets/MatrixProduct-4Oiurn1N.css"
"assets/MatrixProduct-DbDUAZJF.css"
]
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.vueAction{cursor:pointer}.flex{display:flex}.grid{display:grid}.flex-align-center{align-items:center}.justify-self-start{justify-self:start}.gap-1{gap:.25rem}.grid label,.grid .p-checkbox{padding-top:5px}

File diff suppressed because one or more lines are too long

2218
www/dist/modules/MatrixProduct-BFgn1wkj.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -7209,6 +7209,20 @@ title: 'Abschicken',
$navarray['menu']['admin'][$menu]['sec'][] = array('Wiedervorlage','wiedervorlage','list');
$navarray['menu']['admin'][$menu]['sec'][] = array('Wiki','wiki','list');
$navarray['menu']['admin'][$menu]['sec'][] = array('Zeiterfassung','zeiterfassung','create');
foreach ($navarray['menu']['admin'] as $key => $menuitem) {
usort($menuitem['sec'], function ($a, $b) {
$a_val = $a[0];
$b_val = $b[0];
if($a_val > $b_val) return 1;
if($a_val < $b_val) return -1;
return 0;
});
$navarray['menu']['admin'][$key] = $menuitem;
}
// Always the last entry
$navarray['menu']['admin'][$menu]['sec'][] = array('Abmelden','welcome','logout');
return $this->CalculateNavigation($navarray);
@ -13797,6 +13811,20 @@ function SendPaypalFromAuftrag($auftrag, $test = false)
}
}
function ReplaceKonto($db,$value,$fromform = null) {
$value = $this->app->DB->real_escape_string($value);
if ($db) {
$konto = explode(' ',$value)[0];
$kontoid = $this->app->DB->Select("SELECT id FROM konten WHERE kurzbezeichnung = '$konto' LIMIT 1");
return($kontoid);
} else {
$konto = $this->app->DB->Select("SELECT CONCAT(kurzbezeichnung,' ',bezeichnung) FROM konten WHERE id = '$value' LIMIT 1");
return($konto);
}
}
// @refactor FormHelper Komponente
function ReplaceLieferant($db,$value,$fromform)
{
@ -18526,7 +18554,26 @@ function CheckShopTabelle($artikel)
if(!empty($anummer)){
$value['articleid'] = $anummer;
}
$ap = $this->AddAuftragPositionNummer($auftrag,$value['articleid'],$value['quantity'],$projekt,"",true, $doctype, $warenkorb['articlelist'][$key]);
$gruppenpreis = $this->GetVerkaufspreisGruppe($j_id,$value['quantity'],$shopexportArr['preisgruppe'],$waehrung);
if ($gruppenpreis) {
$ap = $this->AddPositionManuellPreisNummer(
$doctype,
$auftrag,
$projekt,
$value['articleid'],
$value['quantity'],
$value['name'],
$gruppenpreis,
$j_umsatzsteuer,
$value['rabatt'],
$shop,
$waehrung,
$warenkorb['articlelist'][$key],
$warenkorb['articlelist']
);
} else {
$ap = $this->AddAuftragPositionNummer($auftrag,$value['articleid'],$value['quantity'],$projekt,"",true, $doctype, $warenkorb['articlelist'][$key]);
}
if(isset($value['webid'])){
$this->app->DB->Update("UPDATE $doctype"."_position SET webid = '".$this->app->DB->real_escape_string($value['webid'])."' WHERE id = '$ap' LIMIT 1");
}
@ -19040,7 +19087,7 @@ function CheckShopTabelle($artikel)
}
}
return array("status" => true, "$auftragid" => $auftrag);
return array("status" => true, "auftragid" => $auftrag);
}

View File

@ -2240,13 +2240,12 @@ class Remote {
$this->app->erp->AuftragProtokoll($orderId, 'Versandmeldung an Shop fehlgeschlagen', $bearbeiter);
$this->logger->error('Versandmeldung an Shop fehlgeschlagen',
[
'orderId' => $orderId,
'shopId' => $shopId,
'message' => $response->getMessage()
]
[
'orderId' => $orderId,
'shopId' => $shopId,
'message' => $response->getMessage()
]
);
return;
}
@ -2486,7 +2485,21 @@ class Remote {
$shoptyp = $this->app->DB->Select("SELECT shoptyp FROM shopexport WHERE id='$id' LIMIT 1");
$modulename = trim($this->app->DB->Select("SELECT modulename FROM shopexport WHERE id='$id' LIMIT 1"), '.');
$isActionAuth = $action === 'auth';
$exception = null;
$this->logger->debug(
'RemoteCommand (Shop '.$id.") ".$action,
[
'shop' => $id,
'action' => $action,
'data' => $data
]
);
if ($shoptyp === 'custom') {
$error = null;
if ($modulename != '') {
$file = dirname(__DIR__) . '/plugins/external/shopimporter/' . $modulename;
@ -2507,29 +2520,37 @@ class Remote {
$method = $this->getMethod($obj, $action);
if (method_exists($obj, $method)) {
$ret = $obj->$method();
$this->logger->debug('RemoteCommand result (Shop '.$id.') '.$modulename.' '.$action,
[
'shop' => $id,
'action' => $action,
'data' => $data,
'result' => $ret
]
);
if (!empty($this->app->stringcleaner)) {
$this->app->stringcleaner->XMLArray_clean($ret);
}
} elseif ($isActionAuth) {
return 'Fehler: Importer konnte nicht initialisiert werden';
$error = 'Fehler: Importer konnte nicht initialisiert werden';
}
} elseif ($isActionAuth) {
return 'Fehler: Importer konnte nicht initialisiert werden';
$error = 'Fehler: Importer konnte nicht initialisiert werden';
}
} elseif ($isActionAuth) {
return 'Fehler: Importer konnte nicht initialisiert werden';
$error = 'Fehler: Importer konnte nicht initialisiert werden';
}
} elseif ($isActionAuth) {
return 'Fehler: Datei ' . $file . ' existiert nicht';
$error = 'Fehler: Datei ' . $file . ' existiert nicht';
}
} elseif ($isActionAuth) {
return 'Fehler: Schnittstelle nicht aktiv';
$error = 'Fehler: Schnittstelle nicht aktiv';
}
} else {
$error = 'Fehler: Kein Modul angegeben';
}
return '';
}
if ($shoptyp === 'intern') {
else if ($shoptyp === 'intern') {
if ($modulename != '') {
if ($this->app->erp->ModulVorhanden($modulename)) {
$obj = $this->app->erp->LoadModul($modulename);
@ -2538,94 +2559,57 @@ class Remote {
$obj->getKonfig($id, $data);
}
$method = 'Import' . $action;
if (method_exists($obj, $method)) {
try {
$ret = $obj->$method();
} catch (Exception $e) {
$exception = $e;
if ($isActionAuth) {
return 'Fehler Auth: ' . $e->getMessage();
$error = 'Fehler Auth: ' . $e->getMessage();
} else {
$error = 'Fehler: ' . $e->getMessage();
}
return 'Fehler: ' . $e->getMessage();
}
$this->logger->debug('RemoteCommand result (Shop '.$id.') '.$modulename.' '.$action,
[
'shop' => $id,
'action' => $action,
'data' => $data,
'result' => $ret
]
);
if (!empty($this->app->stringcleaner)) {
$this->app->stringcleaner->XMLArray_clean($ret);
}
$this->parseReturn($ret, $id, $action);
return $ret;
} else {
return 'Fehler: Funktion nicht implementiert: ' . $method;
$error = 'Fehler: Funktion nicht implementiert: ' . $method;
}
} elseif ($isActionAuth) {
return 'Fehler: Importer konnte nicht initialisiert werden';
$error = 'Fehler: Importer konnte nicht initialisiert werden';
}
} elseif ($isActionAuth) {
return 'Fehler: Dieses Modul ist nicht verf&uuml;gbar';
$error = 'Fehler: Dieses Modul ist nicht verf&uuml;gbar';
}
} elseif ($isActionAuth) {
return 'Fehler: Kein Modul vorhanden';
$error = 'Fehler: Kein Modul vorhanden';
} else {
$error = 'Fehler: Kein Modul angegeben';
}
return '';
}
$shopexport = $this->app->DB->SelectRow("SELECT * FROM shopexport WHERE id='$id' LIMIT 1");
if ($shopexport) {
if ($shopexport['shoptyp'] === 'intern' || $shopexport['shoptyp'] === 'custom') {
return '';
}
$token = $shopexport['token'];
$url = $shopexport['url'];
$z = $shopexport['passwort'];
$bezeichnung = $shopexport['bezeichnung'];
} else {
$token = '';
$z = '';
$url = '';
}
if ($isActionAuth) {
if ($token === '' || strlen($z) < 32 || $url === '') {
return 'Fehler: Bitte Zugangsdaten pr&uuml;fen';
}
} elseif ($token === '' || strlen($z) < 32 || $url === '' || !$this->app->DB->Select("SELECT id FROM shopexport WHERE id = '$id' AND aktiv = 1 LIMIT 1")) {
return '';
}
$tmp = parse_url($url);
$tmp['host'] = rtrim($tmp['host'], '/');
$tmp['path'] = rtrim($tmp['path'], '/') . '/';
$aes = new AES($z);
$token = base64_encode($aes->encrypt(serialize($token)));
$client = new HttpClient($tmp['host'], stripos($url, 'https') === 0 ? 443 : 80);
$geturl = $tmp['path'] . 'index.php?module=import&action=' . $action . '&challenge=' . (isset($challenge) ? $challenge : '');
//Kein Fragezeichen vor module=import...
if (false !== stripos($bezeichnung, 'woocommerce')) {
$geturl = $tmp['path'] . 'module=import&action=' . $action . '&challenge=' . (isset($challenge) ? $challenge : '');
}
if (false !== stripos($bezeichnung, 'shopware plugin')) {
$geturl = $tmp['path'] . 'wawisionimporter/?smodule=import&saction=' . $action . '&challenge=' . (isset($challenge) ? $challenge : '');
}
$post_data['token'] = $token;
$post_data['data'] = base64_encode(serialize($data));
$client->timeout = 120;
if (!$client->post($geturl, $post_data)) {
$this->logger->error('An error occurred',
[
'error' => $client->getError()
]
if ($error) {
$this->logger->error('RemoteCommand error (Shop '.$id.') '.$modulename.' '.$action,
[
'error' => $error,
'exception' => $exception
]
);
throw new Exception('An error occurred: ' . $client->getError());
//return 'Netzwerkverbindung von WaWison zu Shopimporter fehlgeschlagen: '.$client->getError();
}
$ret = unserialize(base64_decode($client->getContent()));
if (!empty($this->app->stringcleaner)) {
$this->app->stringcleaner->XMLArray_clean($ret);
}
$this->parseReturn($ret, $id, $action);
return($error);
}
return $ret;
// Dead code removed here 2024-09-13
}
/**

View File

@ -232,7 +232,7 @@ class RechnungPDF extends BriefpapierCustom {
FROM lieferschein WHERE id='$lieferscheinid' LIMIT 1");
if($datumlieferschein=="00.00.0000") $datumlieferschein = $datum;
if($lieferdatum=="00.00.0000") $lieferdatum = $datum;
if($lieferdatum=="00.00.0000") $lieferdatum = "";
if($mahnwesen_datum=="00.00.0000") $mahnwesen_datum = "";
$bearbeiteremail = $this->app->DB->Select("SELECT b.email FROM rechnung r LEFT JOIN adresse b ON b.id=r.bearbeiterid WHERE r.id='$id' LIMIT 1");

View File

@ -4014,6 +4014,17 @@ select a.kundennummer, (SELECT name FROM adresse a2 WHERE a2.kundennummer = a.ku
}
break;
break;
case "konto":
$cmd = $this->app->Secure->GetGET("cmd");
$arr = $this->app->DB->SelectArr("
SELECT CONCAT(kurzbezeichnung,' ',bezeichnung) as name FROM konten
WHERE (kurzbezeichnung LIKE '%$term%' OR bezeichnung LIKE '%$term%') ORDER by kurzbezeichnung");
$carr = !empty($arr)?count($arr):0;
for($i = 0; $i < $carr; $i++)
$newarr[] = $arr[$i]['name'];
break;
case "datevkonto":
$arr = $this->app->DB->SelectArr("SELECT DISTINCT t.gegenkonto FROM
( (SELECT concat(datevkonto, ' ',bezeichnung) as gegenkonto FROM konten WHERE datevkonto <> 0 AND datevkonto <> '' AND aktiv = 1)

View File

@ -6689,12 +6689,14 @@ Die Gesamtsumme stimmt nicht mehr mit urspr&uuml;nglich festgelegten Betrag '.
public function AuftragList()
{
// refresh all open items
$openids = $this->app->DB->SelectArr("SELECT id from auftrag WHERE status <> 'abgeschlossen'");
foreach ($openids as $openid) {
$this->app->erp->AuftragAutoversandBerechnen($openid['id']);
}
// refresh all open items if no cronjob is set
if (!$this->app->DB->Select("SELECT id FROM prozessstarter WHERE parameter = 'autoversand_berechnung' AND aktiv = 1 LIMIT 1")) {
$openids = $this->app->DB->SelectArr("SELECT id from auftrag WHERE status <>'abgeschlossen' and status <>'storniert' and status <>'angelegt'");
foreach ($openids as $openid) {
$this->app->erp->AuftragAutoversandBerechnen($openid['id']);
}
}
if($this->app->Secure->GetPOST('ausfuehren') && $this->app->erp->RechteVorhanden('auftrag', 'edit'))
{
$drucker = $this->app->Secure->GetPOST('seldrucker');

View File

@ -1112,13 +1112,19 @@
<fieldset>
<legend>{|Finanzbuchhaltung Einstellungen|}</legend>
<table width="100%">
<tr>
<td width="300">Buchungen erzeugen ab Datum:</td><td colspan="3"><input type="text" id= "fibu_buchungen_startdatum" name="fibu_buchungen_startdatum" size="10" value="[FIBU_BUCHUNGEN_STARTDATUM]"><i>F&uuml;r die Nutzung mit dem Modul Buchhaltung-Buchungen (Zahlungseingang, Zahlungsstatus, Mahnwesen)</i></td>
</tr>
<tr>
<td width="300">Konto f&uuml;r Rechnung-Skontobuchungen:</td><td colspan="3"><input type="text" id= "rechnung_skonto_kontorahmen" name="rechnung_skonto_kontorahmen" size="10" value="[RECHNUNG_SKONTO_KONTORAHMEN]"><i>Auf dieses Sachkonto werden Skontobuchungen mithilfe der Funktion "Zahlungsstatus berechnen" im Rechnungsmodul gebucht</i></td>
</tr>
</table>
<td width="300">Buchungen erzeugen ab Datum:</td>
<td colspan="3"><input type="text" id="fibu_buchungen_startdatum" name="fibu_buchungen_startdatum" size="10" value="[FIBU_BUCHUNGEN_STARTDATUM]"><i>F&uuml;r die Nutzung mit dem Modul Buchhaltung-Buchungen (Zahlungseingang, Zahlungsstatus, Mahnwesen)</i></td>
</tr>
<tr>
<td width="300">Sachkonto f&uuml;r Rechnung-Skontobuchungen:</td>
<td colspan="3"><input type="text" id="rechnung_skonto_kontorahmen" name="rechnung_skonto_kontorahmen" size="20" value="[RECHNUNG_SKONTO_KONTORAHMEN]"><i>Auf dieses Sachkonto werden Skontobuchungen mithilfe der Funktion "Zahlungsstatus berechnen" im Rechnungsmodul gebucht</i></td>
</tr>
<tr>
<td width="300">Gesch&auml;ftskonto f&uuml;r Schnelleingabe:</td>
<td colspan="3"><input type="text" id="rechnung_schnelleingabe_konto" name="rechnung_schnelleingabe_konto" size="20" value="[RECHNUNG_SCHNELLEINGABE_KONTO]"><i>Auf dieses Gesch&auml;ftskonto werden Zahlungen mithilfe der Funktion "Schnelleingabe" im Rechnungsmodul gebucht</i></td>
</tr>
</table>
</fieldset>
</div>
</div>

View File

@ -4,7 +4,7 @@
</ul>
<div id="tabs-1">
<form method="post" action="#">
<div class="filter-box filter-usersave">
<div class="filter-box filter-usersave" [ZU_MAHNEN_HIDDEN]>
<div class="filter-block filter-inline">
<div class="filter-title">{|Filter|}</div>
<ul class="filter-list">

View File

@ -1044,7 +1044,7 @@ class Firmendaten {
'arbeitsnachweis_header','arbeitsnachweis_footer','provisionsgutschrift_header','provisionsgutschrift_footer','proformarechnung_header','proformarechnung_footer','eu_lieferung_vermerk','export_lieferung_vermerk'
,'wareneingang_kamera_waage','layout_iconbar','passwort','host','port','mailssl','signatur','email','absendername','bcc1','bcc2','bcc3'
,'firmenfarbe','name','strasse','plz','ort','steuernummer','projekt','steuer_positionen_export','tabsnavigationfarbe','tabsnavigationfarbeschrift'
,"buchhaltung_berater","buchhaltung_mandant","buchhaltung_wj_beginn","buchhaltung_sachkontenlaenge", "fibu_buchungen_startdatum", "rechnung_skonto_kontorahmen"
,"buchhaltung_berater","buchhaltung_mandant","buchhaltung_wj_beginn","buchhaltung_sachkontenlaenge", "fibu_buchungen_startdatum", "rechnung_skonto_kontorahmen", "rechnung_schnelleingabe_konto"
);
if(isset($sql2a)){
@ -1384,6 +1384,7 @@ class Firmendaten {
$this->app->YUI->DatePicker('fibu_buchungen_startdatum');
$this->app->YUI->AutoComplete('rechnung_skonto_kontorahmen', 'sachkonto');
$this->app->YUI->AutoComplete('rechnung_schnelleingabe_konto', 'konto');
$this->app->Tpl->Parse('PAGE','firmendaten.tpl');
}
@ -1853,8 +1854,7 @@ class Firmendaten {
// Fibu
$this->app->Tpl->Set('FIBU_BUCHUNGEN_STARTDATUM', $this->app->erp->ReplaceDatum(false,$data[0]['fibu_buchungen_startdatum'],false));
$this->app->Tpl->Set('RECHNUNG_SKONTO_KONTORAHMEN', $this->app->erp->ReplaceKontorahmen(false,$data[0]['rechnung_skonto_kontorahmen']));
$this->app->Tpl->Set('RECHNUNG_SCHNELLEINGABE_KONTO', $this->app->erp->ReplaceKonto(false,$data[0]['rechnung_schnelleingabe_konto']));
}
}
@ -2299,6 +2299,7 @@ class Firmendaten {
$data['fibu_buchungen_startdatum'] = $this->app->erp->ReplaceDatum(true,$this->app->Secure->POST["fibu_buchungen_startdatum"],false);
$data['rechnung_skonto_kontorahmen'] = $this->app->erp->ReplaceKontorahmen(true,$this->app->Secure->POST["rechnung_skonto_kontorahmen"]);
$data['rechnung_schnelleingabe_konto'] = $this->app->erp->ReplaceKonto(true,$this->app->Secure->POST["rechnung_schnelleingabe_konto"]);
return $data;
}

View File

@ -4008,7 +4008,8 @@ class Importvorlage extends GenImportvorlage {
$feldnameohnepsrache = str_replace($sprachenSet,'',$feldnameohnepsrache);
}
if(in_array($feldnameohnepsrache, $erlaubtefelder,false) && !in_array($sprachenSet,$zuImportierendeSprachen[$sprache],false)){
$haystack = $zuImportierendeSprachen[$sprache]?$zuImportierendeSprachen[$sprache]:array();
if(in_array($feldnameohnepsrache, $erlaubtefelder,false) && !in_array($sprachenSet,$haystack,false)){
$zuImportierendeSprachen[$sprache][] = $sprachenSet;
}
}

View File

@ -14,7 +14,8 @@ class Mahnwesen {
return;
$this->app->ActionHandlerInit($this);
$this->app->ActionHandler("list", "mahnwesen_list");
$this->app->ActionHandler("list", "mahnwesen_list");
$this->app->ActionHandler("stufe_list", "mahnwesen_stufe_list");
$this->app->ActionHandler("create", "mahnwesen_edit"); // This automatically adds a "New" button
$this->app->ActionHandler("edit", "mahnwesen_edit");
$this->app->ActionHandler("einstellungen", "mahnwesen_einstellungen");
@ -40,6 +41,8 @@ class Mahnwesen {
// columns that are aligned right (numbers etc)
// $alignright = array(4,5,6,7,8);
$mahnwesen_stufe_filter = $this->app->DB->real_escape_string($this->app->User->GetParameter('mahnwesen_stufe_filter'));
$faellig_datum = "DATE_ADD(r.datum, INTERVAL r.zahlungszieltage DAY)";
$faellig_tage = "DATEDIFF(CURRENT_DATE,DATE_ADD(r.datum, INTERVAL r.zahlungszieltage DAY))";
$mahn_druck = "if(m.druck,'Ja','')";
@ -56,6 +59,8 @@ class Mahnwesen {
$menu = "<table cellpadding=0 cellspacing=0><tr><td nowrap>" . "<a href=\"index.php?module=rechnung&action=edit&id=%value%\"><img src=\"./themes/{$app->Conf->WFconf['defaulttheme']}/images/edit.svg\" border=\"0\"></a>" . "</td></tr></table>";
$sql_tables = "rechnung r LEFT JOIN projekt p ON p.id=r.projekt LEFT JOIN adresse adr ON r.adresse=adr.id LEFT JOIN auftrag au ON au.id = r.auftragid LEFT JOIN mahnwesen m ON r.mahnwesen = m.id";
$sql = "SELECT SQL_CALC_FOUND_ROWS
r.id,
$dropnbox,
@ -81,9 +86,13 @@ class Mahnwesen {
if(r.mahnwesen_gesperrt,'Ja',''),
REPLACE(r.mahnwesen_internebemerkung,'\r\n','<br> '),
r.id
FROM rechnung r LEFT JOIN projekt p ON p.id=r.projekt LEFT JOIN adresse adr ON r.adresse=adr.id LEFT JOIN auftrag au ON au.id = r.auftragid LEFT JOIN mahnwesen m ON r.mahnwesen = m.id";
FROM ".$sql_tables;
$where = " r.belegnr <> ''";
$where = " r.belegnr <> '' AND r.status <> 'storniert'";
if (!empty($mahnwesen_stufe_filter)) {
$where .= " AND m.id = '".$mahnwesen_stufe_filter."' AND r.versendet_mahnwesen ";
}
// Toggle filters
$this->app->Tpl->Add('JQUERYREADY', "$('#zu_mahnen').click( function() { fnFilterColumn1( 0 ); } );");
@ -108,7 +117,7 @@ class Mahnwesen {
}
$more_data1 = $app->Secure->GetGET("more_data1");
if ($more_data1 == 1) {
if ($more_data1 == 1 && empty($mahnwesen_stufe_filter)) {
$where .= " AND NOT r.versendet_mahnwesen AND r.mahnwesen <> ''";
} else {
}
@ -126,8 +135,7 @@ class Mahnwesen {
}
// END Toggle filters
$count = "SELECT count(DISTINCT id) FROM rechnung r WHERE $where";
$count = "SELECT count(DISTINCT r.id) FROM ".$sql_tables." WHERE $where";
// $groupby = "";
break;
@ -175,69 +183,14 @@ class Mahnwesen {
}
return $erg;
}
// For Tab-highlighting
function mahnwesen_stufe_list() {
$this->mahnwesen_list();
}
function mahnwesen_list() {
$this->app->erp->MenuEintrag("index.php?module=mahnwesen&action=list", "&Uuml;bersicht");
// $this->app->erp->MenuEintrag("index.php?module=mahnwesen&action=create", "Neu anlegen");
// $this->app->erp->MenuEintrag("index.php?module=mahnwesen&action=einstellungen", "Einstellungen");
if($this->app->Secure->GetPOST('mahnstufe_berechnen') && $this->app->erp->RechteVorhanden('rechnung', 'edit')) {
$this->app->erp->rechnung_zahlstatus_berechnen();
$sql = "
SELECT
r.id, r.mahnwesen, rid_mid.mahnwesen_neu
FROM
rechnung r
INNER JOIN
(
SELECT
id_tage.id,
m.id AS mahnwesen_neu
FROM
mahnwesen m
INNER JOIN(
SELECT
id,
MAX(tage) AS tage
FROM
(
SELECT
r.id,
m.tage
FROM
rechnung r
INNER JOIN mahnwesen m ON
DATEDIFF(
CURRENT_DATE,
DATE_ADD(
r.datum,
INTERVAL r.zahlungszieltage DAY
)
) > m.tage
WHERE
r.zahlungsstatus = 'offen'
ORDER BY
`r`.`id` ASC
) temp
GROUP BY
id
) id_tage
ON
m.tage = id_tage.tage
) rid_mid
ON r.id = rid_mid.id
";
$offene_rechnungen = $this->app->DB->SelectArr($sql);
foreach ($offene_rechnungen as $offene_rechnung) {
if ($offene_rechnung['mahnwesen'] != $offene_rechnung['mahnwesen_neu']) {
$sql = "UPDATE rechnung set mahnwesen = ".$offene_rechnung['mahnwesen_neu'].", versendet_mahnwesen = 0 WHERE id = ".$offene_rechnung['id'];
$this->app->DB->Update($sql);
}
}
}
if($this->app->Secure->GetPOST('sel_aktion') && $this->app->erp->RechteVorhanden('rechnung', 'edit'))
{
@ -354,6 +307,101 @@ class Mahnwesen {
}
} // ende ausfuehren
// Refresh status
if($this->app->Secure->GetPOST('mahnstufe_berechnen') && $this->app->erp->RechteVorhanden('rechnung', 'edit')) {
$this->app->erp->rechnung_zahlstatus_berechnen();
}
// Create tabs
$sql = "
SELECT
r.id,
r.mahnwesen,
r.versendet_mahnwesen,
rid_mid.mahnwesen_neu,
rid_mid.name
FROM
rechnung r
INNER JOIN
(
SELECT
id_tage.id,
m.id AS mahnwesen_neu,
m.name,
m.tage
FROM
mahnwesen m
INNER JOIN(
SELECT
id,
MAX(tage) AS tage
FROM
(
SELECT
r.id,
m.tage
FROM
rechnung r
INNER JOIN mahnwesen m ON
DATEDIFF(
CURRENT_DATE,
DATE_ADD(
r.datum,
INTERVAL r.zahlungszieltage DAY
)
) > m.tage
WHERE
r.zahlungsstatus = 'offen'
ORDER BY
`r`.`id` ASC
) temp
GROUP BY
id
) id_tage
ON
m.tage = id_tage.tage
) rid_mid
ON r.id = rid_mid.id
ORDER BY rid_mid.tage
";
$offene_rechnungen = $this->app->DB->SelectArr($sql);
foreach ($offene_rechnungen as $offene_rechnung) {
if ($offene_rechnung['mahnwesen'] != $offene_rechnung['mahnwesen_neu']) {
$sql = "UPDATE rechnung set mahnwesen = ".$offene_rechnung['mahnwesen_neu'].", versendet_mahnwesen = 0 WHERE id = ".$offene_rechnung['id'];
$this->app->DB->Update($sql);
}
}
$menus = $this->app->DB->SelectArr("
SELECT
m.id mahnung,
m.name,
SUM(if(r.versendet_mahnwesen = 1,1,0)) anzahl
FROM
mahnwesen m
LEFT JOIN rechnung r ON
m.id = r.mahnwesen
WHERE
r.id IS NULL OR
(
r.zahlungsstatus <> 'bezahlt' AND
r.mahnwesen_gesperrt <> 1
)
GROUP BY
m.id
ORDER BY
m.tage ASC
");
foreach ($menus as $menu) {
$suffix = "";
if ($menu['anzahl']) {
$suffix = " (".$menu['anzahl'].")";
}
$this->app->erp->MenuEintrag("index.php?module=mahnwesen&action=stufe_list&stufe=".$menu['mahnung'], $this->app->DB->real_escape_string($menu['name']).$suffix);
}
if (!empty($msg)) {
$this->app->Tpl->Set('MESSAGE', $msg);
}
@ -364,6 +412,13 @@ class Mahnwesen {
$this->app->Tpl->Set('SELDRUCKER', $this->app->erp->GetSelectDrucker($this->app->User->GetParameter('rechnung_list_drucker')));
$mahnwesen_stufe_filter = $this->app->Secure->GetGET('stufe');
$this->app->User->SetParameter('mahnwesen_stufe_filter', $mahnwesen_stufe_filter);
if (!empty($mahnwesen_stufe_filter)) {
$this->app->Tpl->Set('KURZUEBERSCHRIFT2',"Stufe: ".$this->app->DB->Select("SELECT name FROM mahnwesen WHERE id = ".$mahnwesen_stufe_filter." LIMIT 1"));
$this->app->Tpl->Set('ZU_MAHNEN_HIDDEN', 'hidden');
}
$this->app->YUI->TableSearch('TAB1', 'mahnwesen_list', "show", "", "", basename(__FILE__), __CLASS__);
$this->app->Tpl->Parse('PAGE', "mahnwesen_list.tpl");
}

View File

@ -1081,6 +1081,7 @@ class Rechnung extends GenRechnung
$intern = true;
$freigabe=$intern;
}
$allowedFrm = true;
$showDefault = true;
$this->app->erp->CheckVertrieb($id,'rechnung');
@ -1096,6 +1097,7 @@ class Rechnung extends GenRechnung
if($belegnr=='')
{
$this->app->erp->BelegFreigabe('rechnung',$id);
$this->rechnung_zahlstatus_berechnen($id);
if($intern) {
return 1;
}
@ -1119,7 +1121,7 @@ class Rechnung extends GenRechnung
jetzt freigegeben werden? <input type=\"button\" class=\"btnImportantLarge\" value=\"Jetzt freigeben\" onclick=\"window.location.href='index.php?module=rechnung&action=freigabe&id=$id&freigabe=$id'\">
</div>");
}
$this->RechnungMenu();
$this->app->Tpl->Parse('PAGE','tabview.tpl');
}
@ -1613,18 +1615,18 @@ class Rechnung extends GenRechnung
$invoiceArr = $this->app->DB->SelectRow(
sprintf(
'SELECT zahlungsweise,zahlungszieltage,dta_datei,status,schreibschutz FROM rechnung WHERE id= %d LIMIT 1',
'SELECT zahlungsweise,zahlungszieltage,dta_datei,status,zahlungsstatus,schreibschutz FROM rechnung WHERE id= %d LIMIT 1',
(int)$id
)
);
$zahlungsweise= $invoiceArr['zahlungsweise'];
$zahlungszieltage= $invoiceArr['zahlungszieltage'];
$zahlungsstatus= $invoiceArr['zahlungsstatus'];
if($zahlungsweise==='rechnung' && $zahlungszieltage<1)
{
$this->app->Tpl->Add('MESSAGE',"<div class=\"info\">Hinweis: F&auml;lligkeit auf \"sofort\", da Zahlungsziel in Tagen auf 0 Tage gesetzt ist!</div>");
}
$status= $invoiceArr['status'];
$schreibschutz= $invoiceArr['schreibschutz'];
if($status !== 'angelegt' && $status !== 'angelegta' && $status !== 'a')
@ -1661,12 +1663,15 @@ class Rechnung extends GenRechnung
$bonuspunkte = $rechnungarr['bonuspunkte'];//$this->app->DB->Select("SELECT bonuspunkte FROM rechnung WHERE id='$id' LIMIT 1");
$soll = $rechnungarr['soll'];//$this->app->DB->Select("SELECT soll FROM rechnung WHERE id='$id' LIMIT 1");
$projekt = $rechnungarr['projekt'];
$skontosoll = $this->app->DB->Select("SELECT TRUNCATE(soll*(1-(zahlungszielskonto/100)),2) as skontosoll FROM rechnung where id = '".$id."' LIMIT 1");
}
$this->app->Tpl->Set('PUNKTE',"<input type=\"text\" name=\"punkte\" value=\"$punkte\" size=\"10\" readonly>");
$this->app->Tpl->Set('BONUSPUNKTE',"<input type=\"text\" name=\"punkte\" value=\"$bonuspunkte\" size=\"10\" readonly>");
$this->app->Tpl->Set('SOLL',"$soll"."<input type=\"hidden\" id=\"soll_tmp\" value=\"$soll\">");
$this->app->Tpl->Set('SKONTOSOLL',$skontosoll);
if($schreibschutz!='1')// && $this->app->erp->RechteVorhanden("rechnung","schreibschutz"))
{
@ -1743,10 +1748,7 @@ class Rechnung extends GenRechnung
if($zahlungsweise==='vorkasse' || $zahlungsweise==='kreditkarte' || $zahlungsweise==='paypal' || $zahlungsweise==='bar') {
$this->app->Tpl->Set('VORKASSE','');
}
$ist = $this->app->erp->EUR($this->app->erp->GetSaldoDokument($id,'rechnung')['betrag']);
$this->app->Tpl->Set('ISTDB',$ist);
if($schreibschutz=="1" && $this->app->erp->RechteVorhanden('rechnung','schreibschutz'))
{
$this->app->Tpl->Set('MESSAGE',"<div class=\"warning\">Diese Rechnung ist schreibgesch&uuml;tzt und darf daher nicht mehr bearbeitet werden!&nbsp;<input type=\"button\" value=\"Schreibschutz entfernen\" onclick=\"if(!confirm('Soll der Schreibschutz f&uuml;r diese Rechnung wirklich entfernt werden? Die gespeicherte Rechnung wird &uuml;berschrieben!')) return false;else window.location.href='index.php?module=rechnung&action=schreibschutz&id=$id';\"></div>");
@ -1756,15 +1758,20 @@ class Rechnung extends GenRechnung
$this->app->erp->CommonReadonly();
}
if($schreibschutz=='1' && $this->app->erp->RechteVorhanden('rechnung','manuellbezahltmarkiert') && $zahlungsstatus=="offen")
{
$this->app->erp->RemoveReadonly('bezahlt_am');
$this->app->erp->RemoveReadonly('zahlbetrag');
$this->app->erp->RemoveReadonly('zahlungsstatus');
}
if($schreibschutz=='1' && $this->app->erp->RechteVorhanden('rechnung','mahnwesen'))
{
$this->app->erp->RemoveReadonly('mahnwesen_datum');
$this->app->erp->RemoveReadonly('mahnwesen_gesperrt');
$this->app->erp->RemoveReadonly('mahnwesen_internebemerkung');
$this->app->erp->RemoveReadonly('zahlungsstatus');
$this->app->erp->RemoveReadonly('mahnwesenfestsetzen');
$this->app->erp->RemoveReadonly('mahnwesen');
$this->app->erp->RemoveReadonly('bezahlt_am');
/*
'ist' should not be edited manually
@ -1795,9 +1802,9 @@ class Rechnung extends GenRechnung
if($speichern!='' && $this->app->erp->RechteVorhanden('rechnung','mahnwesen'))
{
$mahnwesen_datum = $this->app->Secure->GetPOST('mahnwesen_datum');
$bezahlt_am = $this->app->Secure->GetPOST('bezahlt_am');
$zahlbetrag = $this->app->Secure->GetPOST('zahlbetrag');
$mahnwesen_gesperrt = $this->app->Secure->GetPOST('mahnwesen_gesperrt');
$mahnwesen_internebemerkung = $this->app->Secure->GetPOST('mahnwesen_internebemerkung');
$zahlungsstatus = $this->app->Secure->GetPOST('zahlungsstatus');
@ -1823,14 +1830,77 @@ class Rechnung extends GenRechnung
/* if($mahnwesenfestsetzen=='1')
{*/
$this->app->DB->Update("UPDATE rechnung SET mahnwesen_internebemerkung='$mahnwesen_internebemerkung',zahlungsstatus='$zahlungsstatus',versendet_mahnwesen='$versendet',
mahnwesen_gesperrt='$mahnwesen_gesperrt',mahnwesen_datum='$mahnwesen_datum', mahnwesenfestsetzen='$mahnwesenfestsetzen',internebemerkung='$internebemerkung',
mahnwesen='$mahnwesen',skonto_gegeben='$skonto_gegeben',bezahlt_am='$bezahlt_am' WHERE id='$id' LIMIT 1");
$this->app->DB->Update("
UPDATE rechnung SET
mahnwesen_internebemerkung='$mahnwesen_internebemerkung',
zahlungsstatus='$zahlungsstatus',
versendet_mahnwesen='$versendet',
mahnwesen_gesperrt='$mahnwesen_gesperrt',
mahnwesen_datum='$mahnwesen_datum',
mahnwesenfestsetzen='$mahnwesenfestsetzen',
internebemerkung='$internebemerkung'
WHERE id='$id' LIMIT 1");
/* } else {
$this->app->DB->Update("UPDATE rechnung SET mahnwesen='$mahnwesen', mahnwesenfestsetzen='$mahnwesenfestsetzen', mahnwesen_internebemerkung='$mahnwesen_internebemerkung', mahnwesen_gesperrt='$mahnwesen_gesperrt',mahnwesen_datum='$mahnwesen_datum' WHERE id='$id' LIMIT 1");
}*/
}
if ($zahlungsstatus == 'bezahlt') {
$this->app->Tpl->Set('SCHNELLEINGABE_HIDDEN', 'hidden');
$this->app->Tpl->Set('SCHNELLEINGABE_TOOLTIP_HIDDEN', 'hidden');
}
$saldo = $this->app->erp->GetSaldoDokument($id,'rechnung');
if (empty($saldo)) {
$this->app->Tpl->Set('SCHNELLEINGABE_HIDDEN', 'hidden');
$this->app->Tpl->Set('SCHNELLEINGABE_TOOLTIP_HIDDEN', 'hidden');
} else {
$ist = $this->app->erp->EUR($saldo['betrag']);
$this->app->Tpl->Set('ISTDB',$ist);
if ($ist > $soll) {
$this->app->Tpl->addMessage('error','Rechnung ist überzahlt!');
}
}
$rechnung_schnelleingabe_konto = $this->app->erp->Firmendaten('rechnung_schnelleingabe_konto');
if (!empty($rechnung_schnelleingabe_konto)) {
$this->app->Tpl->Set('SCHNELLEINGABE_TOOLTIP_HIDDEN', 'hidden');
if ($speichern!='' && $this->app->erp->RechteVorhanden('rechnung','manuellbezahltmarkiert') && !empty($zahlbetrag)) {
if ($bezahlt_am == '0000-00-00') {
$bezahlt_am = date('Y-m-d');
}
$sql = "
INSERT INTO kontoauszuege
(
konto,
buchung,
importdatum,
buchungstext,
soll,
waehrung,
bearbeiter
)
VALUES
(
$rechnung_schnelleingabe_konto,
'$bezahlt_am',
'".date("Y-m-d")."',
'Rechnung ".$nummer." Schnelleingabe',
$zahlbetrag,
'EUR',
'".$this->app->User->GetName()."'
)
";
$this->app->DB->Insert($sql);
$kontoauszug = $this->app->DB->GetInsertID();
$this->app->erp->fibu_buchungen_buchen("kontoauszuege",$kontoauszug, "rechnung", $id, -$zahlbetrag, 'EUR', $bezahlt_am, "Rechnung ".$nummer." Schnelleingabe");
$this->rechnung_zahlstatus_berechnen($id);
}
} else {
$this->app->Tpl->Set('SCHNELLEINGABE_HIDDEN', 'hidden');
}
if($status=='')
$this->app->DB->Update("UPDATE rechnung SET status='angelegt' WHERE id='$id' LIMIT 1");
@ -2749,9 +2819,14 @@ class Rechnung extends GenRechnung
* Recalculate the payments status with skonto
*/
function rechnung_zahlstatus_berechnen() {
function rechnung_zahlstatus_berechnen($rechnung_id = null) {
// START RECALCULATE
$this->app->erp->fibu_rebuild_tables();
if (!empty($rechnung_id)) {
$condition = " AND id = '".$rechnung_id."'";
}
$offene_rechnungen = $this->app->DB->SelectArr(" SELECT
id,
soll,
@ -2768,9 +2843,9 @@ class Rechnung extends GenRechnung
rechnung
WHERE
belegnr <> '' AND zahlungsstatus = 'offen'
");
".$condition);
foreach ($offene_rechnungen as $offene_rechnung) {
foreach ($offene_rechnungen as $offene_rechnung) {
$saldo = $this->app->erp->GetSaldoDokument($offene_rechnung['id'],'rechnung');
if (!empty($saldo)) {
if ($saldo['waehrung'] == $offene_rechnung['waehrung']) {
@ -2781,12 +2856,13 @@ class Rechnung extends GenRechnung
// Check overall value
if ($saldo['betrag'] == 0) {
// ok -> will be marked as paid
} else if ($skontorelevante_zahlungen >= $offene_rechnung['skontosoll']) {
} else if (abs($skontorelevante_zahlungen-$offene_rechnung['skontosoll']) <= 0.01) {
// Skonto ok -> book difference
$sachkonto = $this->app->erp->Firmendaten('rechnung_skonto_kontorahmen');
if (!empty($sachkonto)) {
$this->app->erp->fibu_buchungen_buchen('rechnung',$offene_rechnung['id'],'kontorahmen',$sachkonto,-$saldo['betrag'],$offene_rechnung['waehrung'],date('Y-m-d'),'');
if (!empty($sachkonto)) {
$this->app->erp->fibu_buchungen_buchen('rechnung',$offene_rechnung['id'],'kontorahmen',$sachkonto,$offene_rechnung['soll']-$skontorelevante_zahlungen,$offene_rechnung['waehrung'],date('Y-m-d'),'');
$offene_rechnung['ist'] = $offene_rechnung['soll'];
$saldo['betrag'] = 0;
} else {
}
} else if ($offene_rechnung['faellig']) {
@ -2800,7 +2876,7 @@ class Rechnung extends GenRechnung
SET
ist = ".$saldo['betrag']."+soll,
zahlungsstatus = IF(".$saldo['betrag']." = 0,'bezahlt','offen')
WHERE id=".$offene_rechnung['id'];
WHERE id=".$offene_rechnung['id'];
$this->app->DB->Update($sql);
}
}

View File

@ -16,10 +16,10 @@
use Xentral\Components\Http\JsonResponse;
use Xentral\Modules\Onlineshop\Data\OrderStatus;
use Xentral\Modules\Onlineshop\Data\OrderStatusUpdateRequest;
use Xentral\Components\Logger\Logger;
class Shopimporter_Woocommerce extends ShopimporterBase
{
// protected $canexport = false;
public $intern = false;
@ -55,16 +55,17 @@ class Shopimporter_Woocommerce extends ShopimporterBase
*/
protected $app;
protected $dump;
/** @var Logger $logger */
public $logger;
public function __construct($app, $intern = false)
{
$this->app=$app;
$this->intern = true;
$this->logger = $app->Container->get('Logger');
}
public function ImportList()
{
$msg = $this->app->erp->base64_url_encode('<div class="info">Sie k&ouml;nnen hier die Shops einstellen</div>');
@ -72,8 +73,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
exit;
}
/**
* This function returns the number of orders which have not yet been imported
*/
@ -83,7 +82,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
// We set per_page to 100 - this could lead to a situation where there are more than
// 100 new Orders, but we still only return 100.
// Array containing additional settings, namely 'ab_nummer' (containting the next order number to get)
// and 'holeallestati' (an integer)
$tmp = $this->CatchRemoteCommand('data');
@ -97,7 +95,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
if ($number_from) {
// Number-based import is selected
// The WooCommerce API doenst allow for a proper "greater than id n" request.
// we fake this behavior by creating an array that contains 'many' (~ 1000) consecutive
// ids that are greater than $from_number and use this array with the 'include' property
@ -115,7 +112,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
'include' => implode(",",$fakeGreaterThanIds),
]);
} else {
// fetch posts by status
@ -129,7 +125,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
return (!empty($pendingOrders)?count($pendingOrders):0);
}
/**
* Calling this function queries the api for pending orders and returns them
* as an array.
@ -141,7 +136,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
*/
public function ImportGetAuftrag()
{
// Array containing additional settings, namely 'ab_nummer' (containting the next order number to get)
// and 'holeallestati' (an integer)
$tmp = $this->CatchRemoteCommand('data');
@ -155,7 +149,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
if ($number_from) {
// Number-based import is selected
// The WooCommerce API doenst allow for a proper "greater than id n" request.
// we fake this behavior by creating an array that contains 'many' (~ 1000) consecutive
// ids that are greater than $from_number and use this array with the 'include' property
@ -175,7 +168,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
'orderby' => 'id'
]);
} else {
// fetch posts by status
@ -188,7 +180,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
}
// Return an empty array in case there are no orders to import
if ((!empty($pendingOrders)?count($pendingOrders):0) === 0) {
return null;
@ -211,13 +202,9 @@ class Shopimporter_Woocommerce extends ShopimporterBase
'warenkorb' => base64_encode(serialize($order)),
];
}
return $tmp;
}
// This function searches the wcOrder for the specified WC Meta key
// and returns it if found, null otherise
public function get_wc_meta($wcOrder, $meta_key) {
@ -231,15 +218,11 @@ class Shopimporter_Woocommerce extends ShopimporterBase
return $value;
}
// Parse the given WooCommerce order, return a Xentral array-represented order.
// Overload this method whenever additional attributes are required.
public function parseOrder($wcOrder) {
$order = array();
$order['auftragsdaten'] = $wcOrder;
@ -273,7 +256,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
}
}
if(!empty($wcOrder->subshop)){
$order['subshop'] = $wcOrder->subshop;
}
@ -302,7 +284,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
}
//
//
// Coupon Codes
//
@ -333,7 +314,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
$seperateShippingAddress = !self::compareObjects(
$wcOrder->billing,
$wcOrder->shipping,
@ -360,7 +340,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
$order['lieferadresse_land'] = $wcOrder->shipping->country;
}
// VAT stuff
$vatId = $this->get_wc_meta($wcOrder, "_billing_ustid");
@ -375,7 +354,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
$order['zahlungsweise'] = $wcOrder->payment_method;
$order['lieferung'] = $wcOrder->shipping_lines[0]->method_id;
return $order;
}
@ -408,7 +386,7 @@ class Shopimporter_Woocommerce extends ShopimporterBase
// The item could be a variable product in which case we have to retrieve the sku of the variation product
if (!empty($wcOrderItem->variation_id)) {
$variation_product_sku = $this->getSKUByShopId($wcOrderItem->id,$wcOrderItem->variation_id);
$variation_product_sku = $this->getSKUByShopId($wcOrderItem->product_id,$wcOrderItem->variation_id);
if (!empty($variation_product_sku)) {
$orderItem['articleid'] = $variation_product_sku;
}
@ -417,7 +395,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
return $orderItem;
}
/**
* Sets the Order status to processing, meaning we've successfully imported
* the order into our DB. This prevents the order from beeing imported again.
@ -432,7 +409,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
]);
}
return 'ok';
}
@ -446,16 +422,25 @@ class Shopimporter_Woocommerce extends ShopimporterBase
{
/** @var OrderStatusUpdateRequest $data */
$data = $this->CatchRemoteCommand('data');
if ($data->orderStatus !== OrderStatus::Completed)
return;
$trackingCode = $data->shipments[0]?->trackingNumber;
if (isset($data->shipments)) {
$trackingCode = $data->shipments[0]?->trackingNumber;
}
if (!empty($trackingCode)) {
$this->client->post('orders/'.$data->orderId.'/notes', [
$this->client->post('orders/'.$data->shopOrderId.'/notes', [
'note' => 'Tracking Code: ' . $trackingCode
]);
$this->WooCommerceLog("Tracking Code Rückmeldung für Auftrag: $data->orderId", $trackingCode);
$this->logger->info("WooCommerce Tracking Code Rückmeldung für Auftrag: ".$data->orderId,
[
'orderId' => $data->shopOrderId,
'trackingCode' => $trackingCode
]
);
}
$updateData = [
@ -463,7 +448,7 @@ class Shopimporter_Woocommerce extends ShopimporterBase
'meta_data' => [
[
'key' => 'tracking_code',
'value' => $data->shipments[0]?->trackingNumber
'value' => $trackingCode
],
[
'key' => 'shipping_carrier',
@ -471,13 +456,17 @@ class Shopimporter_Woocommerce extends ShopimporterBase
]
],
];
$this->client->put('orders/'.$data->orderId, $updateData);
$this->WooCommerceLog("Statusrückmeldung 'completed' für Auftrag: $data->orderId", $this->statusCompleted );
$this->client->put('orders/'.$data->shopOrderId, $updateData);
$this->logger->info("WooCommerce Statusrückmeldung 'completed' für Auftrag: ".$data->orderId,
[
'orderId' => $data->shopOrderId,
'status' => $this->statusCompleted
]
);
return 'ok';
}
/**
* This function syncs the current stock to the remote WooCommerce shop
* @return int
@ -524,8 +513,8 @@ class Shopimporter_Woocommerce extends ShopimporterBase
if (empty($remoteIdInformation['id'])) {
// The online shop doesnt know this article, write to log and continue with next product
$this->WooCommerceLog("Artikel $nummer wurde im Online-Shop nicht gefunden! Falsche Artikelnummer im Shop hinterlegt?");
$this->logger->error("WooCommerce Artikel $nummer wurde im Online-Shop nicht gefunden! Falsche Artikelnummer im Shop hinterlegt?");
continue;
}
@ -541,21 +530,19 @@ class Shopimporter_Woocommerce extends ShopimporterBase
}else{
$result = $this->client->put('products/' . $remoteIdInformation['id'], $updateProductParams);
}
$this->WooCommerceLog("WooCommerce Lagerzahlenübertragung für Artikel: $nummer / $remoteIdInformation[id] - Anzahl: $lageranzahl", $result);
$this->logger->error("WooCommerce Lagerzahlenübertragung für Artikel: $nummer / $remoteIdInformation[id] - Anzahl: $lageranzahl",
[
'result' => $result
]
);
$anzahl++;
}
return $anzahl;
}
public function ImportStorniereAuftrag() {
$orderId = $this->CatchRemoteCommand('data')['auftrag'];
if (!empty($orderId)) {
$this->client->put('orders/'.$orderId, [
'status' => 'cancelled',
@ -563,22 +550,13 @@ class Shopimporter_Woocommerce extends ShopimporterBase
} else {
return 'failed';
}
return 'ok';
}
public function ImportSendList() {
$tmp = $this->catchRemoteCommand('data');
$anzahl = 0;
for($i=0;$i<(!empty($tmp)?count($tmp):0);$i++){
$artikel = $tmp[$i]['artikel'];
$nummer = $tmp[$i]['nummer'];
if(!empty($tmp[$i]['artikelnummer_fremdnummern'][0]['nummer'])){
@ -604,7 +582,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
$dim_width = $tmp[$i]['breite'];
$dim_height = $tmp[$i]['hoehe'];
// Sanitize dimensions
if (self::emptyString($weight_kg))
$weight_kg = null;
@ -619,11 +596,9 @@ class Shopimporter_Woocommerce extends ShopimporterBase
$dim_height = null;
$meta_desc = $tmp[$i]['metadescription_de'];
$meta_title = $tmp[$i]['metatitle_de'];
$pseudopreis = $tmp[$i]['pseudopreis'];//*1.19;
if($pseudopreis <= $preis)$pseudopreis = $preis;
$steuersatz = $tmp[$i]['steuersatz'];
@ -657,7 +632,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
];
// Attributes that are used for both updating an existing product as well as creating a new one
$commonProductAtts = [
'name' => $name_de,
@ -675,7 +649,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
'meta_data' => $commonMetaData,
];
if($lageranzahl===0){
$commonProductAtts['stock_status'] = 'outofstock';
$commonProductAtts['manage_stock'] = true;
@ -693,30 +666,22 @@ class Shopimporter_Woocommerce extends ShopimporterBase
$commonProductAtts['stock_quantity'] = (int)$lageranzahl;
}
if(!is_null($product_id)) {
// Such a product already appears to exist, so we update it
$this->client->put('products/'.$product_id, array_merge([
], $commonProductAtts));
$this->WooCommerceLog("WooCommerce Artikel geändert für Artikel: $nummer / $product_id");
$this->logger->info("WooCommerce Artikel geändert für Artikel: $nummer / $product_id");
}
else{
// create a new product
$product_id = $this->client->post('products/', array_merge([
'sku' => $nummer,
], $commonProductAtts))->id;
$this->WooCommerceLog("WooCommerce neuer Artikel angelegt: $nummer");
$this->logger->info("WooCommerce neuer Artikel angelegt: $nummer");
}
// TODO: Kategoriebaum und Bilder werden noch nicht uebertragen
// if(isset($tmp[$i]['kompletter_kategorienbaum'])){
@ -729,13 +694,10 @@ class Shopimporter_Woocommerce extends ShopimporterBase
// $this->save_images($dateien, $product_id);
// }
// Update the associated product categories
$chosenCats = array();
if(isset($tmp[$i]['kategorien']) || isset($tmp[$i]['kategoriename'])){
$kategorien = $tmp[$i]['kategorien'];
if (!($kategorien) && !self::emptyString($tmp[$i]['kategoriename'])) {
$kategorien = array(
@ -745,11 +707,9 @@ class Shopimporter_Woocommerce extends ShopimporterBase
);
}
if((!empty($kategorien)?count($kategorien):0)>0){
// Retrive all WC categories via API
$allWooCommerceCategories = $this->client->get('products/categories', ['per_page' => '100']);
$searchWpCategories = [];
foreach($allWooCommerceCategories as $a){
$searchWpCategories[$a->id] = $a->name;
@ -764,13 +724,10 @@ class Shopimporter_Woocommerce extends ShopimporterBase
// If WC has a matching category. We match based on name!
if(array_search($wawi_cat_name,array_values($searchWpCategories)) !== false) {
// get id of that WC Category
$wcCatId = array_search($wawi_cat_name,$searchWpCategories);
} else {
// No matching category exists
$wcCatId = $this->client->post('products/categories', [
'name' => $wawi_cat_name,
@ -778,14 +735,12 @@ class Shopimporter_Woocommerce extends ShopimporterBase
}
if ($wcCatId) {
// update category. We first retrieve the product and append the new product category, not replace the entire category array.
$alreadyAssignedWCCats = $this->client->get('products/'.$product_id, [
'per_page' => 1,
])->categories;
// Get ids of existing categories
$existingCategoryIds = [];
foreach ($alreadyAssignedWCCats as $cat) {
@ -814,16 +769,10 @@ class Shopimporter_Woocommerce extends ShopimporterBase
$anzahl++;
}
return $anzahl;
// return array($product_id,$anzahl,$nummer,$steuersatz, $preis);
}
/**
* Checks the connection to the WooCommerce API by trying a simple API request
*
@ -839,7 +788,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
}
}
/**
* This is called by class.remote.php, initializes some class variables from the DB
* @param [type] $shopid [description]
@ -858,6 +806,7 @@ class Shopimporter_Woocommerce extends ShopimporterBase
}
$this->protokoll = $preferences['felder']['protokoll'];
$this->ssl_ignore = $preferences['felder']['ssl_ignore'];
$ImportWooCommerceApiSecret = $preferences['felder']['ImportWoocommerceApiSecret'];
$ImportWooCommerceApiKey = $preferences['felder']['ImportWoocommerceApiKey'];
$ImportWooCommerceApiUrl = $preferences['felder']['ImportWoocommerceApiUrl'];
@ -876,8 +825,9 @@ class Shopimporter_Woocommerce extends ShopimporterBase
$ImportWooCommerceApiKey,
//WooCommerce API Secret
$ImportWooCommerceApiSecret,
["query_string_auth" => true]
["query_string_auth" => true],
$this->logger,
$this->ssl_ignore
);
}
@ -919,8 +869,9 @@ class Shopimporter_Woocommerce extends ShopimporterBase
$ImportWooCommerceApiUrl,
$ImportWooCommerceApiKey,
$ImportWooCommerceApiSecret,
['query_string_auth' => true]
['query_string_auth' => true],
$this->logger,
$this->ssl_ignore
);
$auth = $this->ImportAuth();
@ -931,7 +882,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
return null;
}
/**
* @return array[]
*/
@ -987,7 +937,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
* @throws WCHttpClientException
*/
private function getShopIdBySKU($sku) {
// Retrieve the product with the given sku.
// Note: We limit the result set to 1 (per_page=1), so this doesnt work
// if there are multiple products with the same sku. should not happen in practice anyway
@ -1005,7 +954,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
return null;
}
private function getSKUByShopId($articleid, $variationid) {
$product = $this->client->get("products/$articleid/variations/$variationid");
@ -1023,7 +971,8 @@ class Shopimporter_Woocommerce extends ShopimporterBase
'ausblenden'=>array('abholmodus'=>array('zeitbereich')),
'archiv'=>array('ab_nummer'),
'felder'=>array(
'protokoll'=>array('typ'=>'checkbox','bezeichnung'=>'Protokollierung im Logfile:'),
// 'protokoll'=>array('typ'=>'checkbox','bezeichnung'=>'Protokollierung im Logfile:'),
'ssl_ignore'=>array('typ'=>'checkbox','bezeichnung'=>'SSL-Prüfung abschalten:','info' => 'Nur für Testzwecke!'),
'ImportWoocommerceApiKey'=>array('typ'=>'text','bezeichnung'=>'{|API Key:','size'=>60),
'ImportWoocommerceApiSecret'=>array('typ'=>'text','bezeichnung'=>'{|API Secret|}:','size'=>60),
'ImportWoocommerceApiUrl'=>array('typ'=>'text','bezeichnung'=>'{|API Url|}:','size'=>40),
@ -1034,20 +983,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
));
}
/**
* Writes data to the syslog
* @param [type] $nachricht message that will be logged
* @param string $dump php array or object, printed using print_r
*/
public function WooCommerceLog($nachricht, $dump = '')
{
if($this->protokoll){
$this->app->erp->LogFile($nachricht, print_r($dump, true));
}
}
/**
* Compares two Objects and returns true if every variable in items
* is the same in $a and $b
@ -1068,8 +1003,6 @@ class Shopimporter_Woocommerce extends ShopimporterBase
return true;
}
/**
* Returns true when the string entered is empty, after stripping whitespace
* @param String $string input
@ -1081,11 +1014,8 @@ class Shopimporter_Woocommerce extends ShopimporterBase
}
class WCClient
{
/**
* WooCommerce REST API WCClient version.
*/
@ -1097,7 +1027,12 @@ class WCClient
* @var WCHttpClient
*/
public $http;
/** @var Logger $logger */
public $logger;
public $ssl_ignore = false;
/**
* Initialize client.
*
@ -1108,9 +1043,10 @@ class WCClient
*
* @throws WCHttpClientException
*/
public function __construct($url, $consumerKey, $consumerSecret, $options = [])
public function __construct($url, $consumerKey, $consumerSecret, $options = [], $logger, $ssl_ignore)
{
$this->http = new WCHttpClient($url, $consumerKey, $consumerSecret, $options);
$this->http = new WCHttpClient($url, $consumerKey, $consumerSecret, $options, $logger, $ssl_ignore);
$this->logger = $logger;
}
/**
@ -1190,7 +1126,6 @@ class WCClient
class WCResponse
{
/**
* WCResponse code.
*
@ -1289,7 +1224,6 @@ class WCResponse
class WCOptions
{
/**
* Default WooCommerce REST API version.
*/
@ -1423,7 +1357,6 @@ class WCOptions
class WCRequest
{
/**
* WCRequest url.
*
@ -1596,7 +1529,6 @@ class WCRequest
class WCOAuth
{
/**
* OAuth signature method algorithm.
*/
@ -1866,7 +1798,7 @@ class WCHttpClientException extends \Exception
* @var WCResponse
*/
private $response;
/**
* Initialize exception.
*
@ -1879,7 +1811,7 @@ class WCHttpClientException extends \Exception
{
parent::__construct($message, $code);
$this->request = $request;
$this->request = $request;
$this->response = $response;
}
@ -1906,7 +1838,6 @@ class WCHttpClientException extends \Exception
class WCHttpClient
{
/**
* cURL handle.
*
@ -1962,6 +1893,11 @@ class WCHttpClient
* @var string
*/
private $responseHeaders;
/** @var Logger $logger */
public $logger;
public $ssl_ignore = false;
/**
* Initialize HTTP client.
@ -1973,7 +1909,7 @@ class WCHttpClient
*
* @throws WCHttpClientException
*/
public function __construct($url, $consumerKey, $consumerSecret, $options)
public function __construct($url, $consumerKey, $consumerSecret, $options, $logger, $ssl_ignore)
{
if (!function_exists('curl_version')) {
throw new WCHttpClientException('cURL is NOT installed on this server', -1, new WCRequest(), new WCResponse());
@ -1983,6 +1919,8 @@ class WCHttpClient
$this->url = $this->buildApiUrl($url);
$this->consumerKey = $consumerKey;
$this->consumerSecret = $consumerSecret;
$this->logger = $logger;
$this->ssl_ignore = $ssl_ignore;
}
/**
@ -2171,7 +2109,6 @@ class WCHttpClient
*/
protected function createResponse()
{
// Set response headers.
$this->responseHeaders = '';
curl_setopt($this->ch, CURLOPT_HEADERFUNCTION, function ($_, $headers) {
@ -2195,7 +2132,10 @@ class WCHttpClient
*/
protected function setDefaultCurlSettings()
{
$verifySsl = $this->options->verifySsl();
if (!$this->ssl_ignore) {
$verifySsl = $this->options->verifySsl();
}
$timeout = $this->options->getTimeout();
$followRedirects = $this->options->getFollowRedirects();
@ -2235,6 +2175,13 @@ class WCHttpClient
$errorMessage = $errors->message;
$errorCode = $errors->code;
}
$this->logger->error('WooCommerce Error',
[
'request' => $this->request,
'response' => $this->response
]
);
throw new WCHttpClientException(
sprintf('Error: %s [%s]', $errorMessage, $errorCode),
@ -2294,8 +2241,6 @@ class WCHttpClient
public function request($endpoint, $method, $data = [], $parameters = [])
{
// Initialize cURL.
$this->ch = curl_init();
@ -2308,7 +2253,6 @@ class WCHttpClient
// Get response.
$response = $this->createResponse();
// Check for cURL errors.
if (curl_errno($this->ch)) {
throw new WCHttpClientException('cURL Error: ' . \curl_error($this->ch), 0, $request, $response);

View File

@ -9,6 +9,7 @@ body {
html {
box-sizing: border-box;
font-size: 11px;
}
*,

View File

@ -125,6 +125,9 @@ class WidgetGenrechnung
$field = new HTMLInput("bezahlt_am","text","","10","","","","","","","","0","","");
$this->form->NewField($field);
$field = new HTMLInput("zahlbetrag","money","","10","","","","","","","","0","","");
$this->form->NewField($field);
$field = new HTMLInput("ist","text","","10","","","","","","","","0","","");
$this->form->NewField($field);

View File

@ -104,7 +104,9 @@
<div class="inside inside-full-height">
[MAHNWESENIF]
<fieldset>
<legend>{|Zahlungsstatus|}</legend>
<legend>
{|Zahlungsstatus|}&nbsp;<img class="wawitooltipicon" src="themes/new/images/tooltip_grau.png" title="Zur Nutzung der Schnelleingabe muss in den Grundeinstellungen das Geschäftskonto für Schnelleingabe und optional das Sachkonto für Rechnung-Skontobuchungen gesetzt werden." [SCHNELLEINGABE_TOOLTIP_HIDDEN]>
</legend>
<table class="tablemahnwesenfestsetzen">
<tr>
<td width="200">{|Zahlungsstatus|}:</td>
@ -119,15 +121,23 @@
<td>
{|SOLL|}:
</td>
<td>
<td align="right">
[SOLL]
</td>
</tr>
<tr>
<td>
{|SKONTOSOLL|}:
</td>
<td align="right">
[SKONTOSOLL]
</td>
</tr>
<tr>
<td>
{|FEHLT|}:
</td>
<td id="istdb">
<td id="istdb" align="right">
[ISTDB]
</td>
</tr>
@ -135,6 +145,23 @@
</td>
</tr>
</table>
</fieldset>
<fieldset [SCHNELLEINGABE_HIDDEN]>
<legend>{|Schnelleingabe|}</legend>
<table>
<tr>
<td width="200">{|Bezahlt am|}:</td>
<td width="70%">[BEZAHLT_AM][MSGBEZAHLT_AM]
</td>
</tr>
<tr>
<td>{|Zahlbetrag|}:</td>
<td>[ZAHLBETRAG][MSGZAHLBETRAG]</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>{|Mahnwesen|}</legend>
<table>
<tr>
<td width="200">{|Mahnstufe|}:</td>

View File

@ -84,6 +84,7 @@ class WidgetRechnung extends WidgetGenRechnung
$this->app->erp->AnzeigeAbweichendeBezeichnung("rechnung");
$this->form->ReplaceFunction("ist",$this,"ReplaceDecimal");
$this->form->ReplaceFunction("zahlbetrag",$this,"ReplaceDecimal");
$this->form->ReplaceFunction("kurs",$this,"ReplaceBetrag");
$this->form->ReplaceFunction("skonto_gegeben",$this,"ReplaceDecimal");