Initial commit: CLEO ERP avec améliorations debug
- Configuration du debug conditionnel pour dev/recette - Fonction debug() globale avec niveaux - Logging des requêtes SQL - Handlers d'exceptions et d'erreurs globaux 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
BIN
._.DS_Store
Normal file
22
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#3399ff",
|
||||
"activityBar.background": "#3399ff",
|
||||
"activityBar.foreground": "#15202b",
|
||||
"activityBar.inactiveForeground": "#15202b99",
|
||||
"activityBarBadge.background": "#bf0060",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"commandCenter.border": "#e7e7e799",
|
||||
"sash.hoverBorder": "#3399ff",
|
||||
"statusBar.background": "#007fff",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#3399ff",
|
||||
"statusBarItem.remoteBackground": "#007fff",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#007fff",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#007fff99",
|
||||
"titleBar.inactiveForeground": "#e7e7e799"
|
||||
},
|
||||
"peacock.color": "#007fff"
|
||||
}
|
||||
64
CLAUDE.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
CLEO is a PHP-based ERP (Enterprise Resource Planning) web application built with a custom MVC framework called "d6". It's a multi-tenant system designed for business management with modules for clients, quotes, markets, invoicing, and more.
|
||||
|
||||
## Key Commands
|
||||
|
||||
### Development Setup
|
||||
```bash
|
||||
# Install PHP dependencies
|
||||
composer install
|
||||
|
||||
# Deploy to development environment (Incus container)
|
||||
./deploy-cleo-dev.sh
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### MVC Structure
|
||||
The application follows a strict MVC pattern with naming conventions:
|
||||
- **Controllers** (`/controllers/`): Prefixed with 'c' (e.g., `cclients.php`)
|
||||
- **Models** (`/models/`): Prefixed with 'm' (e.g., `mclients.php`)
|
||||
- **Views** (`/views/`): Prefixed with 'v' (e.g., `vclients.php`)
|
||||
- **AJAX Controllers**: Prefixed with 'cjx' (e.g., `cjxchat.php`)
|
||||
|
||||
### Request Flow
|
||||
1. `index.php` - Entry point that bootstraps the application
|
||||
2. `config/init.php` - Sets up constants and loads configuration
|
||||
3. `pub/res/d6/router.php` - Routes requests to appropriate controllers
|
||||
4. Controller loads model and view based on naming convention
|
||||
|
||||
### Database Architecture
|
||||
- Multi-database setup with three main databases:
|
||||
- `frontal` - Main application data
|
||||
- `group` - Group/organization data
|
||||
- `general` - Shared/general data
|
||||
- Database connections are managed through `config/conf.php`
|
||||
- Multi-tenant support based on HTTP host detection
|
||||
|
||||
### Key Framework Components (d6)
|
||||
Located in `/pub/res/d6/`:
|
||||
- `router.php` - Request routing
|
||||
- `session.php` - Session management
|
||||
- `tools.php` - Utility functions
|
||||
- `controller.php` - Base controller class
|
||||
- `model.php` - Base model with database operations
|
||||
|
||||
### Deployment
|
||||
- Target: Incus containers on Debian 12
|
||||
- Web server: nginx
|
||||
- Deployment script handles:
|
||||
- File sync via rsync
|
||||
- Container file transfer
|
||||
- Permission setup (nginx:nginx ownership)
|
||||
- Production URL: dcleo.unikoffice.com
|
||||
|
||||
### Important Configuration
|
||||
- Development mode detected by IP whitelist in `config/conf.php`
|
||||
- Entity/tenant selection based on domain (e.g., abc.cleo.com → entity 'abc')
|
||||
- File uploads stored in `/pub/files/upload/`
|
||||
- Session data likely stored server-side with custom handler
|
||||
7
composer.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"require": {
|
||||
"phpmailer/phpmailer": "^6.8",
|
||||
"phpoffice/phpspreadsheet": "^1.28",
|
||||
"phpseclib/phpseclib": "~3.0"
|
||||
}
|
||||
}
|
||||
969
composer.lock
generated
Normal file
@@ -0,0 +1,969 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "62fd28f0dded186386da0edf2ec52eea",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/pcre",
|
||||
"version": "3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/pcre.git",
|
||||
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
|
||||
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan": "<1.11.10"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.12 || ^2",
|
||||
"phpstan/phpstan-strict-rules": "^1 || ^2",
|
||||
"phpunit/phpunit": "^8 || ^9"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Composer\\Pcre\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be",
|
||||
"homepage": "http://seld.be"
|
||||
}
|
||||
],
|
||||
"description": "PCRE wrapping library that offers type-safe preg_* replacements.",
|
||||
"keywords": [
|
||||
"PCRE",
|
||||
"preg",
|
||||
"regex",
|
||||
"regular expression"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/composer/pcre/issues",
|
||||
"source": "https://github.com/composer/pcre/tree/3.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://packagist.com",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/composer",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-12T16:29:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
"version": "v4.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ezyang/htmlpurifier.git",
|
||||
"reference": "cb56001e54359df7ae76dc522d08845dc741621b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b",
|
||||
"reference": "cb56001e54359df7ae76dc522d08845dc741621b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cerdic/css-tidy": "^1.7 || ^2.0",
|
||||
"simpletest/simpletest": "dev-master"
|
||||
},
|
||||
"suggest": {
|
||||
"cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
|
||||
"ext-bcmath": "Used for unit conversion and imagecrash protection",
|
||||
"ext-iconv": "Converts text to and from non-UTF-8 encodings",
|
||||
"ext-tidy": "Used for pretty-printing HTML"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"library/HTMLPurifier.composer.php"
|
||||
],
|
||||
"psr-0": {
|
||||
"HTMLPurifier": "library/"
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/library/HTMLPurifier/Language/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Edward Z. Yang",
|
||||
"email": "admin@htmlpurifier.org",
|
||||
"homepage": "http://ezyang.com"
|
||||
}
|
||||
],
|
||||
"description": "Standards compliant HTML filter written in PHP",
|
||||
"homepage": "http://htmlpurifier.org/",
|
||||
"keywords": [
|
||||
"html"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ezyang/htmlpurifier/issues",
|
||||
"source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0"
|
||||
},
|
||||
"time": "2024-11-01T03:51:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maennchen/zipstream-php",
|
||||
"version": "3.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/maennchen/ZipStream-PHP.git",
|
||||
"reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f",
|
||||
"reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"ext-zlib": "*",
|
||||
"php-64bit": "^8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"brianium/paratest": "^7.7",
|
||||
"ext-zip": "*",
|
||||
"friendsofphp/php-cs-fixer": "^3.16",
|
||||
"guzzlehttp/guzzle": "^7.5",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"php-coveralls/php-coveralls": "^2.5",
|
||||
"phpunit/phpunit": "^11.0",
|
||||
"vimeo/psalm": "^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"guzzlehttp/psr7": "^2.4",
|
||||
"psr/http-message": "^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ZipStream\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paul Duncan",
|
||||
"email": "pabs@pablotron.org"
|
||||
},
|
||||
{
|
||||
"name": "Jonatan Männchen",
|
||||
"email": "jonatan@maennchen.ch"
|
||||
},
|
||||
{
|
||||
"name": "Jesse Donat",
|
||||
"email": "donatj@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "András Kolesár",
|
||||
"email": "kolesar@kolesar.hu"
|
||||
}
|
||||
],
|
||||
"description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
|
||||
"keywords": [
|
||||
"stream",
|
||||
"zip"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/maennchen/ZipStream-PHP/issues",
|
||||
"source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/maennchen",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-27T12:07:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "markbaker/complex",
|
||||
"version": "3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MarkBaker/PHPComplex.git",
|
||||
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
|
||||
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Complex\\": "classes/src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"email": "mark@lange.demon.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "PHP Class for working with complex numbers",
|
||||
"homepage": "https://github.com/MarkBaker/PHPComplex",
|
||||
"keywords": [
|
||||
"complex",
|
||||
"mathematics"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/MarkBaker/PHPComplex/issues",
|
||||
"source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2"
|
||||
},
|
||||
"time": "2022-12-06T16:21:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "markbaker/matrix",
|
||||
"version": "3.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MarkBaker/PHPMatrix.git",
|
||||
"reference": "728434227fe21be27ff6d86621a1b13107a2562c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
|
||||
"reference": "728434227fe21be27ff6d86621a1b13107a2562c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpdocumentor/phpdocumentor": "2.*",
|
||||
"phploc/phploc": "^4.0",
|
||||
"phpmd/phpmd": "2.*",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||
"sebastian/phpcpd": "^4.0",
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Matrix\\": "classes/src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"email": "mark@demon-angel.eu"
|
||||
}
|
||||
],
|
||||
"description": "PHP Class for working with matrices",
|
||||
"homepage": "https://github.com/MarkBaker/PHPMatrix",
|
||||
"keywords": [
|
||||
"mathematics",
|
||||
"matrix",
|
||||
"vector"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/MarkBaker/PHPMatrix/issues",
|
||||
"source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1"
|
||||
},
|
||||
"time": "2022-12-02T22:17:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/constant_time_encoding",
|
||||
"version": "v3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/constant_time_encoding.git",
|
||||
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512",
|
||||
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9",
|
||||
"vimeo/psalm": "^4|^5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ParagonIE\\ConstantTime\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Steve 'Sc00bz' Thomas",
|
||||
"email": "steve@tobtu.com",
|
||||
"homepage": "https://www.tobtu.com",
|
||||
"role": "Original Developer"
|
||||
}
|
||||
],
|
||||
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
|
||||
"keywords": [
|
||||
"base16",
|
||||
"base32",
|
||||
"base32_decode",
|
||||
"base32_encode",
|
||||
"base64",
|
||||
"base64_decode",
|
||||
"base64_encode",
|
||||
"bin2hex",
|
||||
"encoding",
|
||||
"hex",
|
||||
"hex2bin",
|
||||
"rfc4648"
|
||||
],
|
||||
"support": {
|
||||
"email": "info@paragonie.com",
|
||||
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
|
||||
"source": "https://github.com/paragonie/constant_time_encoding"
|
||||
},
|
||||
"time": "2024-05-08T12:36:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "v9.99.100",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
|
||||
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">= 7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.*|5.*",
|
||||
"vimeo/psalm": "^1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||
},
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
|
||||
"keywords": [
|
||||
"csprng",
|
||||
"polyfill",
|
||||
"pseudorandom",
|
||||
"random"
|
||||
],
|
||||
"support": {
|
||||
"email": "info@paragonie.com",
|
||||
"issues": "https://github.com/paragonie/random_compat/issues",
|
||||
"source": "https://github.com/paragonie/random_compat"
|
||||
},
|
||||
"time": "2020-10-15T08:29:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpmailer/phpmailer",
|
||||
"version": "v6.9.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||
"reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2f5c94fe7493efc213f643c23b1b1c249d40f47e",
|
||||
"reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-filter": "*",
|
||||
"ext-hash": "*",
|
||||
"php": ">=5.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
|
||||
"doctrine/annotations": "^1.2.6 || ^1.13.3",
|
||||
"php-parallel-lint/php-console-highlighter": "^1.0.0",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.3.2",
|
||||
"phpcompatibility/php-compatibility": "^9.3.5",
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"squizlabs/php_codesniffer": "^3.7.2",
|
||||
"yoast/phpunit-polyfills": "^1.0.4"
|
||||
},
|
||||
"suggest": {
|
||||
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
|
||||
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
|
||||
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
|
||||
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
|
||||
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
|
||||
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
|
||||
"psr/log": "For optional PSR-3 debug logging",
|
||||
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
|
||||
"thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PHPMailer\\PHPMailer\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1-only"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marcus Bointon",
|
||||
"email": "phpmailer@synchromedia.co.uk"
|
||||
},
|
||||
{
|
||||
"name": "Jim Jagielski",
|
||||
"email": "jimjag@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Andy Prevost",
|
||||
"email": "codeworxtech@users.sourceforge.net"
|
||||
},
|
||||
{
|
||||
"name": "Brent R. Matzelle"
|
||||
}
|
||||
],
|
||||
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
||||
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Synchro",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-24T18:04:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpspreadsheet",
|
||||
"version": "1.29.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
|
||||
"reference": "c80041b1628c4f18030407134fe88303661d4e4e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/c80041b1628c4f18030407134fe88303661d4e4e",
|
||||
"reference": "c80041b1628c4f18030407134fe88303661d4e4e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer/pcre": "^1||^2||^3",
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": "*",
|
||||
"ezyang/htmlpurifier": "^4.15",
|
||||
"maennchen/zipstream-php": "^2.1 || ^3.0",
|
||||
"markbaker/complex": "^3.0",
|
||||
"markbaker/matrix": "^3.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-main",
|
||||
"dompdf/dompdf": "^1.0 || ^2.0 || ^3.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"mitoteam/jpgraph": "^10.3",
|
||||
"mpdf/mpdf": "^8.1.1",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpunit/phpunit": "^8.5 || ^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.7",
|
||||
"tecnickcom/tcpdf": "^6.5"
|
||||
},
|
||||
"suggest": {
|
||||
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
|
||||
"ext-intl": "PHP Internationalization Functions",
|
||||
"mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
|
||||
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
|
||||
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maarten Balliauw",
|
||||
"homepage": "https://blog.maartenballiauw.be"
|
||||
},
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"homepage": "https://markbakeruk.net"
|
||||
},
|
||||
{
|
||||
"name": "Franck Lefevre",
|
||||
"homepage": "https://rootslabs.net"
|
||||
},
|
||||
{
|
||||
"name": "Erik Tilt"
|
||||
},
|
||||
{
|
||||
"name": "Adrien Crivelli"
|
||||
}
|
||||
],
|
||||
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
|
||||
"homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
|
||||
"keywords": [
|
||||
"OpenXML",
|
||||
"excel",
|
||||
"gnumeric",
|
||||
"ods",
|
||||
"php",
|
||||
"spreadsheet",
|
||||
"xls",
|
||||
"xlsx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
|
||||
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.10"
|
||||
},
|
||||
"time": "2025-02-08T02:56:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "3.0.43",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02",
|
||||
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"paragonie/constant_time_encoding": "^1|^2|^3",
|
||||
"paragonie/random_compat": "^1.4|^2.0|^9.99.99",
|
||||
"php": ">=5.6.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "Install the DOM extension to load XML formatted public keys.",
|
||||
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
|
||||
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
|
||||
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
|
||||
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"phpseclib/bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"phpseclib3\\": "phpseclib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jim Wigginton",
|
||||
"email": "terrafrost@php.net",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Patrick Monnerat",
|
||||
"email": "pm@datasphere.ch",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Andreas Fischer",
|
||||
"email": "bantu@phpbb.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Hans-Jürgen Petrich",
|
||||
"email": "petrich@tronic-media.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "graham@alt-three.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
|
||||
"homepage": "http://phpseclib.sourceforge.net",
|
||||
"keywords": [
|
||||
"BigInteger",
|
||||
"aes",
|
||||
"asn.1",
|
||||
"asn1",
|
||||
"blowfish",
|
||||
"crypto",
|
||||
"cryptography",
|
||||
"encryption",
|
||||
"rsa",
|
||||
"security",
|
||||
"sftp",
|
||||
"signature",
|
||||
"signing",
|
||||
"ssh",
|
||||
"twofish",
|
||||
"x.509",
|
||||
"x509"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.43"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/terrafrost",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/phpseclib",
|
||||
"type": "patreon"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-14T21:12:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-client.git",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP clients",
|
||||
"homepage": "https://github.com/php-fig/http-client",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-client",
|
||||
"psr",
|
||||
"psr-18"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-client"
|
||||
},
|
||||
"time": "2023-09-23T14:17:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-factory"
|
||||
},
|
||||
"time": "2024-04-15T12:06:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/2.0"
|
||||
},
|
||||
"time": "2023-04-04T09:54:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/simple-cache",
|
||||
"version": "3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/simple-cache.git",
|
||||
"reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
|
||||
"reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\SimpleCache\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interfaces for simple caching",
|
||||
"keywords": [
|
||||
"cache",
|
||||
"caching",
|
||||
"psr",
|
||||
"psr-16",
|
||||
"simple-cache"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
|
||||
},
|
||||
"time": "2021-10-29T13:26:27+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
148
config/conf.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
class Conf
|
||||
{
|
||||
const admin = 1; // TRUE ou FALSE pour indiquer si l'application est admin ou non
|
||||
const intra = 1; // Est-ce un intranet privé TRUE 1, ou un site public FALSE 0
|
||||
const erp = 1; //! Est-ce un ERP ? Utile pour la gestion documentaire avec les paths spéciaux pour l'ERP
|
||||
const magazine = 0; //! Est-ce qu'on veut transformer les PDF en JPG pour la lecture Magazine dans le d6tools.upload ?
|
||||
|
||||
public $_appname;
|
||||
public $_appscript;
|
||||
public $_appversion;
|
||||
public $_appenv;
|
||||
public $_apptitle;
|
||||
|
||||
public $_brandname;
|
||||
public $_brandadresse1;
|
||||
public $_brandadresse2;
|
||||
public $_brandcp;
|
||||
public $_brandville;
|
||||
public $_brandtel;
|
||||
public $_brandemail;
|
||||
public $_brandlogo;
|
||||
public $_brandgroupe;
|
||||
public $_brandmulti;
|
||||
|
||||
public $_piwikid;
|
||||
public $_googlid;
|
||||
|
||||
public $_excludeIp = "82.67.142.214"; //! IP à exclure pour le comptage des visites et pour le debug
|
||||
public $_clientIp;
|
||||
public $_devIp = false;
|
||||
|
||||
//! Configuration du debug
|
||||
public $_debug_level = 0; //! 0=off, 1=errors, 2=warnings, 3=info, 4=debug
|
||||
public $_log_sql = false; //! Logger les requêtes SQL
|
||||
public $_log_performance = false; //! Logger les temps d'exécution
|
||||
public $_log_file_path = ''; //! Chemin du fichier de log
|
||||
|
||||
public $_pathupload = "/pub/files/upload/"; //! le path de base pour les uploads
|
||||
|
||||
//! les infos de connexion de la base de données
|
||||
public $_dbhost = 'localhost';
|
||||
public $_dbname = 'uof_frontal';
|
||||
public $_dbuser = 'uof_front_user';
|
||||
public $_dbpass = 'd66,UnikOffice.User';
|
||||
|
||||
public $_dbghost = 'localhost';
|
||||
public $_dbgname = '';
|
||||
public $_dbguser = 'uof_linet_user';
|
||||
public $_dbgpass = 'd66,UOF-LinetRH.User';
|
||||
|
||||
public $_dbuhost = 'localhost';
|
||||
public $_dbuname = '';
|
||||
public $_dbuuser = 'uof_linet_user';
|
||||
public $_dbupass = 'd66,UOF-LinetRH.User';
|
||||
|
||||
public $_tbusers = ""; // Spécifie la table des users de cette application, par défaut uof_frontal.users, mais sur Linet c'est dans uof_linet.commerciaux
|
||||
|
||||
//! les infos de l'entité de l'utilisateur
|
||||
public $_entite = '';
|
||||
|
||||
//! indique si c'est une nouvelle version pour les tests de nouveaux modules et librairies
|
||||
public $_new_version = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
//! on va chercher la configuration de l'application dans la table ce_frontal.y_conf
|
||||
$mysqli = new mysqli($this->_dbhost, $this->_dbuser, $this->_dbpass, $this->_dbname);
|
||||
$sql = 'SELECT * FROM y_conf WHERE admin=' . self::admin . ' AND active=1 LIMIT 1;';
|
||||
$mysqli->set_charset("utf8");
|
||||
$res = $mysqli->query($sql);
|
||||
$resconf = $res->fetch_assoc();
|
||||
$this->_appenv = $resconf["appenv"];
|
||||
$this->_appversion = $resconf["appversion"];
|
||||
$this->_appscript = $resconf["appscript"]; //! le script à appeler par défaut si l'utilisateur n'est pas reconnu
|
||||
|
||||
$this->_brandgroupe = $resconf["brandgroupe"];
|
||||
$this->_brandmulti = $resconf["brandmulti"];
|
||||
|
||||
//! On va chercher les infos de base de cette appname dans ce_frontal.users_entites en fonction du http_host
|
||||
$http_host = $_SERVER['HTTP_HOST'];
|
||||
error_log("http_host : ".$http_host);
|
||||
$sql = 'SELECT * FROM users_entites WHERE http_host LIKE "%' . $http_host . '%" AND active=1 LIMIT 1;';
|
||||
$res = $mysqli->query($sql);
|
||||
$mysqli->close();
|
||||
$resentite = $res->fetch_assoc();
|
||||
if (empty($resentite)) {
|
||||
//! on ne trouve pas ce http_host, on part sur la demo
|
||||
$this->_appname = "udo_demo";
|
||||
$mysqli = new mysqli($this->_dbhost, $this->_dbuser, $this->_dbpass, $this->_dbname);
|
||||
$sql = 'SELECT * FROM users_entites WHERE rowid=1;'; // appname="' . $this->_appname . '" AND active=1 LIMIT 1;';
|
||||
$res = $mysqli->query($sql);
|
||||
$mysqli->close();
|
||||
$resentite = $res->fetch_assoc();
|
||||
}
|
||||
$this->_entite = $resentite;
|
||||
$this->_appname = $resentite["appname"];
|
||||
$this->_apptitle = $resentite["libelle"];
|
||||
$this->_brandname = $resentite["libelle"];
|
||||
$this->_brandadresse1 = $resentite["adresse1"];
|
||||
$this->_brandadresse2 = $resentite["adresse2"];
|
||||
$this->_brandcp = $resentite["cp"];
|
||||
$this->_brandville = $resentite["ville"];
|
||||
$this->_brandtel = $resentite["tel1"];
|
||||
$this->_brandemail = $resentite["email"];
|
||||
$this->_brandlogo = $resentite["appname"];
|
||||
|
||||
$this->_dbgname = $resentite["groupebase"];
|
||||
$this->_dbuname = $resentite["genbase"];
|
||||
$this->_tbusers = $resentite["table_users_gen"]; //! Spécifie la table des users de cette application, par défaut dans uof_frontal.users
|
||||
|
||||
if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
|
||||
$this->_clientIp = $_SERVER["HTTP_CLIENT_IP"];
|
||||
} elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
|
||||
$this->_clientIp = $_SERVER["HTTP_X_FORWARDED_FOR"];
|
||||
} else {
|
||||
$this->_clientIp = $_SERVER["REMOTE_ADDR"];
|
||||
}
|
||||
// Active le debug uniquement pour dev et recette
|
||||
if (strpos($http_host, 'dcleo.unikoffice.com') !== false || strpos($http_host, 'rcleo.unikoffice.com') !== false) {
|
||||
ini_set('error_reporting', -1);
|
||||
ini_set('display_errors', '1');
|
||||
$this->_devIp = true;
|
||||
|
||||
// Configuration avancée du debug pour dev/recette
|
||||
$this->_debug_level = 4; // Niveau debug complet
|
||||
$this->_log_sql = true; // Logger les requêtes SQL
|
||||
$this->_log_performance = true; // Mesurer les performances
|
||||
$this->_log_file_path = dirname(__DIR__) . '/' . $this->_appname . '_debug.log';
|
||||
|
||||
// Options PHP supplémentaires pour le debug
|
||||
ini_set('log_errors', '1');
|
||||
ini_set('error_log', $this->_log_file_path);
|
||||
ini_set('display_startup_errors', '1');
|
||||
ini_set('track_errors', '1');
|
||||
ini_set('html_errors', '1');
|
||||
ini_set('xmlrpc_errors', '0');
|
||||
} else {
|
||||
ini_set('error_reporting', 0);
|
||||
ini_set('display_errors', '0');
|
||||
ini_set('log_errors', '0');
|
||||
$this->_debug_level = 0;
|
||||
$this->_log_sql = false;
|
||||
$this->_log_performance = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
131
config/init.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
$tpsdebut = microtime(true);
|
||||
setlocale(LC_ALL, 'fr_FR');
|
||||
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('LIBROOT', dirname(__FILE__));
|
||||
define('ROOT', dirname(LIBROOT));
|
||||
define('CONFROOT', ROOT . DS . 'config');
|
||||
define('CONTROOT', ROOT . DS . 'controllers');
|
||||
define('MODROOT', ROOT . DS . 'models');
|
||||
define('VIEWROOT', ROOT . DS . 'views');
|
||||
define('LAYROOT', VIEWROOT . DS . 'layouts');
|
||||
|
||||
require_once CONFROOT . DS . 'conf.php';
|
||||
$Conf = new Conf();
|
||||
|
||||
define('RESROOT', ROOT . DS . 'pub' . DS . 'res');
|
||||
define('FMKROOT', RESROOT . DS . 'd6');
|
||||
define('BLOCKROOT', FMKROOT . DS . 'blocks');
|
||||
|
||||
//! Chargement de la nouvelle version du d6tools allégée
|
||||
require_once FMKROOT . DS . 'd6_tools.php';
|
||||
|
||||
//! Chargement des fichiers spécifiques au projet
|
||||
require_once FMKROOT . DS . 'lib_cleo.php';
|
||||
|
||||
//! Handler d'exceptions global
|
||||
function exception_handler($exception) {
|
||||
global $Conf;
|
||||
|
||||
$error_data = array(
|
||||
'type' => 'EXCEPTION',
|
||||
'message' => $exception->getMessage(),
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'code' => $exception->getCode(),
|
||||
'trace' => $exception->getTraceAsString()
|
||||
);
|
||||
|
||||
// Logger l'exception
|
||||
if (isset($Conf->_debug_level) && $Conf->_debug_level > 0) {
|
||||
debug($error_data, "UNCAUGHT_EXCEPTION", 1);
|
||||
}
|
||||
|
||||
// Logger dans la table z_logs
|
||||
eLog(0, "Exception non gérée: " . $exception->getMessage() . " dans " . $exception->getFile() . ":" . $exception->getLine());
|
||||
|
||||
// Afficher une erreur propre à l'utilisateur
|
||||
if (isset($Conf->_devIp) && $Conf->_devIp && ini_get('display_errors')) {
|
||||
// En mode dev, afficher les détails
|
||||
echo "<div style='background:#fee; border:2px solid #c00; padding:20px; margin:20px; font-family:monospace;'>";
|
||||
echo "<h2 style='color:#c00;'>Exception non gérée</h2>";
|
||||
echo "<p><strong>Message:</strong> " . htmlspecialchars($exception->getMessage()) . "</p>";
|
||||
echo "<p><strong>Fichier:</strong> " . htmlspecialchars($exception->getFile()) . " ligne " . $exception->getLine() . "</p>";
|
||||
echo "<p><strong>Code:</strong> " . $exception->getCode() . "</p>";
|
||||
echo "<pre style='background:#fff; padding:10px; overflow:auto;'>" . htmlspecialchars($exception->getTraceAsString()) . "</pre>";
|
||||
echo "</div>";
|
||||
} else {
|
||||
// En production, afficher un message générique
|
||||
echo "<div style='text-align:center; padding:50px;'>";
|
||||
echo "<h2>Une erreur est survenue</h2>";
|
||||
echo "<p>Nous nous excusons pour la gêne occasionnée. L'erreur a été enregistrée.</p>";
|
||||
echo "<p><a href='/'>Retour à l'accueil</a></p>";
|
||||
echo "</div>";
|
||||
}
|
||||
}
|
||||
|
||||
//! Handler d'erreurs global
|
||||
function error_handler($errno, $errstr, $errfile, $errline) {
|
||||
global $Conf;
|
||||
|
||||
// Vérifier si l'erreur doit être rapportée selon error_reporting
|
||||
if (!(error_reporting() & $errno)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$error_types = array(
|
||||
E_ERROR => 'ERROR',
|
||||
E_WARNING => 'WARNING',
|
||||
E_PARSE => 'PARSE',
|
||||
E_NOTICE => 'NOTICE',
|
||||
E_CORE_ERROR => 'CORE_ERROR',
|
||||
E_CORE_WARNING => 'CORE_WARNING',
|
||||
E_COMPILE_ERROR => 'COMPILE_ERROR',
|
||||
E_COMPILE_WARNING => 'COMPILE_WARNING',
|
||||
E_USER_ERROR => 'USER_ERROR',
|
||||
E_USER_WARNING => 'USER_WARNING',
|
||||
E_USER_NOTICE => 'USER_NOTICE',
|
||||
E_STRICT => 'STRICT',
|
||||
E_RECOVERABLE_ERROR => 'RECOVERABLE_ERROR',
|
||||
E_DEPRECATED => 'DEPRECATED',
|
||||
E_USER_DEPRECATED => 'USER_DEPRECATED'
|
||||
);
|
||||
|
||||
$error_type = isset($error_types[$errno]) ? $error_types[$errno] : 'UNKNOWN';
|
||||
|
||||
$error_data = array(
|
||||
'type' => $error_type,
|
||||
'errno' => $errno,
|
||||
'message' => $errstr,
|
||||
'file' => $errfile,
|
||||
'line' => $errline
|
||||
);
|
||||
|
||||
// Déterminer le niveau de debug pour ce type d'erreur
|
||||
$debug_level = 4; // Par défaut niveau le plus bas
|
||||
if (in_array($errno, array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR))) {
|
||||
$debug_level = 1; // Erreurs critiques
|
||||
} elseif (in_array($errno, array(E_WARNING, E_CORE_WARNING, E_COMPILE_WARNING, E_USER_WARNING))) {
|
||||
$debug_level = 2; // Warnings
|
||||
} elseif (in_array($errno, array(E_NOTICE, E_USER_NOTICE))) {
|
||||
$debug_level = 3; // Notices
|
||||
}
|
||||
|
||||
// Logger l'erreur
|
||||
if (isset($Conf->_debug_level) && $Conf->_debug_level >= $debug_level) {
|
||||
debug($error_data, "PHP_ERROR", $debug_level);
|
||||
}
|
||||
|
||||
// Pour les erreurs critiques, logger aussi dans z_logs
|
||||
if ($debug_level == 1) {
|
||||
eLog(0, "Erreur PHP $error_type: $errstr dans $errfile:$errline");
|
||||
}
|
||||
|
||||
// Ne pas exécuter le handler d'erreur PHP interne
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Enregistrer les handlers
|
||||
set_exception_handler('exception_handler');
|
||||
set_error_handler('error_handler');
|
||||
4
controllers/caccueil.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
$aModel=array();
|
||||
require_once $Route->_model;
|
||||
require_once $Route->_view;
|
||||
4
controllers/cclients.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
$aModel=array();
|
||||
require_once $Route->_model;
|
||||
require_once $Route->_view;
|
||||
4
controllers/cdevis.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
$aModel=array();
|
||||
require_once $Route->_model;
|
||||
require_once $Route->_view;
|
||||
3
controllers/cexpxls.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
global $Route;
|
||||
require_once $Route->_model;
|
||||
4
controllers/cinterface.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
$aModel=array();
|
||||
require_once $Route->_model;
|
||||
require_once $Route->_view;
|
||||
45
controllers/cjxchat.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
global $Session;
|
||||
global $Conf;
|
||||
global $Route;
|
||||
|
||||
//! on va chercher la data de la Session au format tableau
|
||||
$session_data = $Session->get_data();
|
||||
$fk_user = $Session->_user["rowid"];
|
||||
|
||||
switch ($Route->_action) {
|
||||
case "refresh":
|
||||
//! Rafraîchit la liste des messages
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
$sql = 'SELECT dh.*, u.libelle, u.prenom FROM devis_histo dh LEFT JOIN users u ON dh.fk_user=u.rowid WHERE dh.fk_devis=' . $cid . ' ORDER BY dh.date_histo DESC;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
} else {
|
||||
echo "Erreur : pas de client";
|
||||
}
|
||||
break;
|
||||
|
||||
case "save_message":
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
$mes = nettoie_input($data->message);
|
||||
$fku = nettoie_input($data->fkuser);
|
||||
|
||||
$sql = 'INSERT INTO devis_histo (fk_devis, fk_user, commentaire, date_histo) VALUES (' . $cid . ', ' . $fku . ', "' . $mes . '", "' . date("Y-m-d H:i:s") . '");';
|
||||
eLog($sql);
|
||||
qSQL($sql, "gen");
|
||||
|
||||
$ret = array('ret' => "ok");
|
||||
echo json_encode($ret);
|
||||
} else {
|
||||
$ret = array('ret' => "ko");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
exit();
|
||||
1230
controllers/cjxdevis.php
Normal file
254
controllers/cjxexport.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use phpseclib3\Net\SFTP;
|
||||
|
||||
global $Route, $Session;
|
||||
$fk_user = $Session->_user['rowid'];
|
||||
|
||||
function generateXML($data) {
|
||||
$writer = new XMLWriter();
|
||||
$writer->openMemory();
|
||||
$writer->setIndent(true);
|
||||
$writer->setIndentString(' ');
|
||||
$writer->startDocument('1.0', 'UTF-8');
|
||||
|
||||
$writer->startElement('cleodevis');
|
||||
|
||||
// Éléments simples
|
||||
$simpleElements = ['id', 'opportunite', 'date_demande', 'date_remise', 'marche', 'num_marche', 'nom_marche', 'photos', 'commentaire_rr', 'speciaux', 'total_devis_ht', 'total_devis_ht_remise', 'marge_totale'];
|
||||
foreach ($simpleElements as $element) {
|
||||
$writer->writeElement($element, $data[$element]);
|
||||
}
|
||||
|
||||
// Client
|
||||
$writer->startElement('client');
|
||||
foreach ($data['client'] as $key => $value) {
|
||||
if ($key === 'contact') {
|
||||
$writer->startElement('contact');
|
||||
foreach ($value as $contactKey => $contactValue) {
|
||||
$writer->writeElement($contactKey, $contactValue);
|
||||
}
|
||||
$writer->endElement(); // contact
|
||||
} else {
|
||||
$writer->writeElement($key, $value);
|
||||
}
|
||||
}
|
||||
$writer->endElement(); // client
|
||||
|
||||
// Produits
|
||||
$writer->startElement('produits');
|
||||
foreach ($data['produits'] as $produit) {
|
||||
$writer->startElement('produit');
|
||||
$writer->writeAttribute('id', $produit['id']);
|
||||
foreach ($produit as $key => $value) {
|
||||
if ($key !== 'id') {
|
||||
$writer->writeElement($key, $value);
|
||||
}
|
||||
}
|
||||
$writer->endElement(); // produit
|
||||
}
|
||||
$writer->endElement(); // produits
|
||||
|
||||
// Produits spéciaux
|
||||
$writer->startElement('produits_speciaux');
|
||||
foreach ($data['produits_speciaux'] as $key => $value) {
|
||||
if ($key === 'produit_special') {
|
||||
foreach ($value as $produitSpecial) {
|
||||
$writer->startElement('produit_special');
|
||||
$writer->writeAttribute('id', $produitSpecial['id']);
|
||||
foreach ($produitSpecial as $psKey => $psValue) {
|
||||
if ($psKey !== 'id') {
|
||||
$writer->writeElement($psKey, $psValue);
|
||||
}
|
||||
}
|
||||
$writer->endElement(); // produit_special
|
||||
}
|
||||
} else {
|
||||
$writer->writeElement($key, $value);
|
||||
}
|
||||
}
|
||||
$writer->endElement(); // produits_speciaux
|
||||
$writer->endElement(); // cleodevis
|
||||
return $writer->outputMemory();
|
||||
}
|
||||
|
||||
function uploadToSftp($localPath, $filename) {
|
||||
eLog("Début de l'upload sur SFTP pour le fichier $localPath $filename");
|
||||
$sftp = [
|
||||
'host' => 'e1exshxstxsftp.blob.core.windows.net',
|
||||
'port' => 22,
|
||||
'username' => 'e1exshxstxsftp.edi-cleo.edicleousr',
|
||||
'password' => 'QYDavy+wIy7AoDnezPv+KC/vF+zG1q6n',
|
||||
'remote_path' => '/'
|
||||
];
|
||||
|
||||
// On va chercher le fichier XML en path absolu
|
||||
$localPath = __DIR__ . '/../' . $localPath;
|
||||
$xmlPathAndName = $localPath . DS . $filename;
|
||||
try {
|
||||
$sftp_client = new SFTP($sftp['host'], $sftp['port']);
|
||||
|
||||
if (!$sftp_client->login($sftp['username'], $sftp['password'])) {
|
||||
throw new Exception("Authentification SFTP échouée");
|
||||
}
|
||||
|
||||
$remote_file = $sftp['remote_path'] . $filename;
|
||||
|
||||
// Debug des chemins
|
||||
error_log("Chemin local absolu : " . $xmlPathAndName);
|
||||
error_log("Chemin distant : " . $remote_file);
|
||||
|
||||
// Vérification explicite du fichier
|
||||
if (!file_exists($xmlPathAndName)) {
|
||||
throw new Exception("Le fichier n'existe pas à l'emplacement : " . $xmlPathAndName);
|
||||
}
|
||||
|
||||
// Essayez d'utiliser fopen au lieu de file_get_contents
|
||||
$handle = fopen($xmlPathAndName, 'r');
|
||||
if ($handle === false) {
|
||||
throw new Exception("Impossible d'ouvrir le fichier en lecture");
|
||||
}
|
||||
|
||||
if (!$sftp_client->put($remote_file, $handle)) {
|
||||
throw new Exception("Échec de l'envoi du fichier sur le serveur distant");
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
// Vérifiez la taille du fichier distant après l'envoi
|
||||
$remoteSize = $sftp_client->filesize($remote_file);
|
||||
if ($remoteSize === 0) {
|
||||
throw new Exception("Le fichier distant est vide après l'envoi");
|
||||
} else {
|
||||
error_log('Taille du fichier distant après l\'envoi : ' . $remoteSize);
|
||||
}
|
||||
eLog('Fichier envoyé avec succès : ' . $remote_file . ', taille sur le serveur : ' . $remoteSize);
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
error_log("Erreur SFTP: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($Route->_action) {
|
||||
case "xml_devis":
|
||||
$cid = nettoie_input($Route->_param1);
|
||||
eLog("Export XML SAP Devis : " . $cid);
|
||||
$sql = 'SELECT d.num_opportunite, d.date_demande, d.date_remise, d.fk_client, m.libelle AS lib_marche, m.numero AS num_marche, m.nom AS nom_marche, d.chk_devis_photos, d.chk_speciaux, d.commentaire AS commentaire_rr, ';
|
||||
$sql .= 'd.montant_total_ht as total_devis_ht, d.montant_total_ht_remise AS total_devis_ht_remise, d.marge_totale ';
|
||||
$sql .= 'FROM devis d LEFT JOIN marches m ON d.fk_marche=m.rowid WHERE d.rowid = ' . $cid . ';';
|
||||
$dataDevis = getinfos($sql);
|
||||
if ($dataDevis) {
|
||||
$dataDevis = $dataDevis[0];
|
||||
if ($dataDevis["fk_client"] == 0) {
|
||||
// Pas de client issu de la table clients mais un client saisi manuellement
|
||||
$sql = 'SELECT "0" AS code, d.lib_new_client AS etablissement, d.adresse1_new_client AS adresse1, d.adresse2_new_client AS adresse2, d.adresse3_new_client AS adresse3, ';
|
||||
$sql .= 'cp_new_client AS codepostal, ville_new_client AS ville FROM devis d WHERE d.rowid = ' . $cid . ';';
|
||||
$dataClient = getinfos($sql);
|
||||
$sql = 'SELECT d.contact_new_nom AS nom, d.contact_new_prenom AS prenom, d.contact_new_fonction AS fonction, d.new_telephone AS fixe, d.new_mobile AS mobile, d.new_email AS email FROM devis d WHERE d.rowid = ' . $cid . ';';
|
||||
$dataContact = getinfos($sql);
|
||||
} else {
|
||||
$sql = 'SELECT c.code, c.libelle AS etablissement, c.adresse1, c.adresse2, c.adresse3, c.cp AS codepostal, c.ville FROM clients c WHERE c.rowid = ' . $dataDevis["fk_client"] . ';';
|
||||
$dataClient = getinfos($sql);
|
||||
$sql = 'SELECT c.contact_nom AS nom, c.contact_prenom AS prenom, c.contact_fonction AS fonction, c.telephone AS fixe, c.mobile, c.email FROM clients c WHERE c.rowid = ' . $dataDevis["fk_client"] . ';';
|
||||
$dataContact = getinfos($sql);
|
||||
}
|
||||
$dataClient = $dataClient[0];
|
||||
// On ajoute l'array contact dans l'array client
|
||||
$dataClient['contact'] = $dataContact[0];
|
||||
|
||||
$sql = 'SELECT dp.fk_produit AS id, dp.code, dp.libelle AS designation, dp.prix_vente, dp.qte AS quantite, dp.remise, dp.pu_vente_remise AS pu_vente_avec_remise, dp.totalht AS total_ht, dp.marge, dp.commentaire ';
|
||||
$sql .= 'FROM devis_produits dp WHERE dp.fk_devis = ' . $cid . ' ORDER BY dp.ordre;';
|
||||
$dataProduits = getinfos($sql);
|
||||
|
||||
// $sql = 'SELECT fk_product, qty, prix_unitaire, remise, total_ht, total_ttc FROM lignes_speciales WHERE fk_devis = $cid';
|
||||
$dataProduitsSpeciaux = array(); // getinfos($sql);
|
||||
|
||||
$data = [
|
||||
'id' => $cid,
|
||||
'opportunite' => $dataDevis['num_opportunite'],
|
||||
'date_demande' => $dataDevis['date_demande'],
|
||||
'date_remise' => $dataDevis['date_remise'],
|
||||
'marche' => $dataDevis['lib_marche'],
|
||||
'num_marche' => $dataDevis['num_marche'],
|
||||
'nom_marche' => $dataDevis['nom_marche'],
|
||||
'photos' => $dataDevis['chk_devis_photos'],
|
||||
'commentaire_rr' => $dataDevis['commentaire_rr'],
|
||||
'speciaux' => $dataDevis['chk_speciaux'],
|
||||
'total_devis_ht' => $dataDevis['total_devis_ht'],
|
||||
'total_devis_ht_remise' => $dataDevis['total_devis_ht_remise'],
|
||||
'marge_totale' => $dataDevis['marge_totale'],
|
||||
'client' => $dataClient,
|
||||
'produits' => $dataProduits,
|
||||
'produits_speciaux' => $dataProduitsSpeciaux
|
||||
];
|
||||
|
||||
// ob_start();
|
||||
// $xmlName = 'dv' . $cid . '_' . date("Y_m_d_H_i") . '.xml';
|
||||
// $xml = generateXML($data);
|
||||
// ob_end_clean();
|
||||
|
||||
// $xmlPath = 'pub/files/upload/devis';
|
||||
// $xmlPathAndName = $xmlPath . DS . $xmlName;
|
||||
// file_put_contents($xmlPathAndName, $xml);
|
||||
|
||||
|
||||
// Définition des chemins
|
||||
$xmlName = 'dv' . $cid . '_' . date("Y_m_d_H_i") . '.xml';
|
||||
$xmlPath = 'pub/files/upload/devis';
|
||||
$xmlPathAndName = $xmlPath . DS . $xmlName;
|
||||
|
||||
// Génération du XML sans output buffering
|
||||
$xml = generateXML($data);
|
||||
|
||||
// Vérification du contenu XML avant écriture
|
||||
if (empty($xml)) {
|
||||
throw new Exception("Le XML généré est vide");
|
||||
}
|
||||
|
||||
// Écriture du fichier avec vérification
|
||||
if (file_put_contents($xmlPathAndName, $xml) === false) {
|
||||
throw new Exception("Échec de l'écriture du fichier XML");
|
||||
}
|
||||
|
||||
// Vérification après écriture
|
||||
clearstatcache(true, $xmlPathAndName);
|
||||
if (!file_exists($xmlPathAndName) || filesize($xmlPathAndName) === 0) {
|
||||
throw new Exception("Le fichier XML n'a pas été créé correctement");
|
||||
}
|
||||
|
||||
// Pour debug
|
||||
error_log("Taille du XML généré : " . strlen($xml));
|
||||
error_log("Taille du fichier créé : " . filesize($xmlPathAndName));
|
||||
|
||||
$sql = 'SELECT m.rowid FROM medias m WHERE m.support_rowid = ' . $cid . ' AND support="devis_xml_sap";';
|
||||
$media = getinfos($sql);
|
||||
if ($media) {
|
||||
$sql = 'UPDATE medias SET dir0="pub/files/upload/devis/", fichier="' . $xmlName . '", type_fichier="xml", date_modif="' . date("Y-m-d H:i:s") . '", fk_user_modif=' . $fk_user . ' WHERE rowid = ' . $media[0]['rowid'] . ';';
|
||||
} else {
|
||||
$sql = 'INSERT INTO medias (support, dir0, fichier, type_fichier, support_rowid, date_creat, fk_user_creat) VALUES ("devis_xml_sap", "pub/files/upload/devis/", "' . $xmlName . '", "xml", ' . $cid . ', "' . date("Y-m-d H:i:s") . '", ' . $fk_user . ');';
|
||||
}
|
||||
qSQL($sql);
|
||||
|
||||
// Transférer via SFTP
|
||||
if (!uploadToSftp($xmlPath, $xmlName)) {
|
||||
throw new Exception("Erreur lors du transfert SFTP du fichier $xmlPath, $xmlName");
|
||||
} else {
|
||||
eLog("Fichier XML $xmlName a bien été transféré avec succès sur SFTP.");
|
||||
}
|
||||
|
||||
header("Content-type: application/xml");
|
||||
header('Content-Disposition: attachment; filename="' . $xmlName . '"');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
echo $xml;
|
||||
exit();
|
||||
} else {
|
||||
eLog("xml_devis devis not found");
|
||||
}
|
||||
break;
|
||||
}
|
||||
exit;
|
||||
601
controllers/cjximport.php
Normal file
@@ -0,0 +1,601 @@
|
||||
<?php
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Csv;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
global $Conf;
|
||||
global $Session;
|
||||
global $Route;
|
||||
|
||||
$fkUser = $Session->_user["rowid"];
|
||||
|
||||
function nettoie_text($input)
|
||||
{
|
||||
$retour = trim($input); // on supprime les espaces en début et fin de chaîne
|
||||
$retour = str_replace('"', '', $retour); // on remplace les doubles guillemets par rien
|
||||
$retour = str_replace(" ", " ", $retour); // on remplace les doubles espaces par un simple espace
|
||||
return $retour;
|
||||
}
|
||||
|
||||
function formate_date($sdate)
|
||||
{
|
||||
//! $sdate au format 1. 1. 1900 && 26. 4. 2019
|
||||
$sday = "";
|
||||
$smonth = "";
|
||||
$syear = "";
|
||||
$ladate = "";
|
||||
$pospoint = strpos($sdate, ". ");
|
||||
if ($pospoint !== FALSE) {
|
||||
$sday = substr($sdate, 0, $pospoint);
|
||||
if (strlen($sday) == 1) {
|
||||
$sday = "0" . $sday;
|
||||
}
|
||||
$sdate = substr($sdate, $pospoint + 2);
|
||||
$pospoint = strpos($sdate, ". ");
|
||||
if ($pospoint !== FALSE) {
|
||||
$smonth = substr($sdate, 0, $pospoint);
|
||||
if (strlen($smonth) == 1) {
|
||||
$smonth = "0" . $smonth;
|
||||
}
|
||||
$syear = substr($sdate, $pospoint + 2);
|
||||
}
|
||||
}
|
||||
if ($sday != "" && $syear != "1900") {
|
||||
$ladate = $syear . "-" . $smonth . "-" . $sday;
|
||||
}
|
||||
eLog("formate_date : " . $sdate . " => " . $ladate);
|
||||
return $ladate;
|
||||
}
|
||||
|
||||
|
||||
switch ($Route->_action) {
|
||||
case "upload_clients":
|
||||
//! Importation du fichier CSV des clients SAP
|
||||
|
||||
$upLoadDir = ROOT . $Conf->_pathupload . "import" . DS;
|
||||
// on crée le dossier et son arborescence s'il n'existe pas
|
||||
if (!file_exists($upLoadDir)) {
|
||||
mkdir($upLoadDir, 0777, true);
|
||||
}
|
||||
eLog("upLoadDir : " . $upLoadDir);
|
||||
$erreur = 0;
|
||||
$messageErreur = "";
|
||||
$size_max = 10297150; // 10 Mo
|
||||
foreach ($_FILES as $file) {
|
||||
$size_file = filesize($file['tmp_name']);
|
||||
if ($size_file > $size_max) {
|
||||
$message = "Vous avez dépassé la taille de fichier autorisée";
|
||||
eLog("problème de taille de fichier : " . $size_file . " pour un max de " . $size_max);
|
||||
} else {
|
||||
// on remplace les espaces par des _ dans le nom du fichier sur le serveur
|
||||
$fileName = str_normalize(basename($file["name"]), true);
|
||||
if (strrpos($fileName, ".") === false) {
|
||||
// pas d'extension
|
||||
$typ = "";
|
||||
} else {
|
||||
$typ = substr($fileName, strrpos($fileName, ".") + 1);
|
||||
}
|
||||
eLog("Import fichier : " . $fileName);
|
||||
if (move_uploaded_file($file['tmp_name'], $upLoadDir . $fileName)) {
|
||||
eLog("Importation Clients : Fichier " . $upLoadDir . $fileName . " uploadé");
|
||||
|
||||
$row = 1;
|
||||
$headers = true;
|
||||
$separateur = ";";
|
||||
|
||||
$import_libelle = "Importation du fichier Clients SAP";
|
||||
$sql_prepa = "UPDATE clients SET chk_import=0;";
|
||||
$sql_final1 = "UPDATE clients SET active=chk_import;";
|
||||
$sql_final2 = "UPDATE clients SET chk_import=0;";
|
||||
|
||||
// Structure du fichier CSV
|
||||
$colonnes = array('code', 'libelle', 'siret', 'adresse1', 'adresse2', 'adresse3', 'cp', 'ville', 'fk_type', 'contact_civilite', 'contact_nom', 'contact_prenom', 'contact_fonction', 'telephone', 'mobile', 'email');
|
||||
|
||||
$nbColonnes = count($colonnes);
|
||||
$nomLog = 'csv-' . date("Y") . '-' . date("m") . '-' . date("d") . '-' . date("H") . '-' . date("i") . '-' . date("s") . '.log';
|
||||
$log = $upLoadDir . $nomLog;
|
||||
eLog("création du fichier log : " . $log);
|
||||
$source = $upLoadDir . $fileName;
|
||||
$fhlog = fopen($log, "a");
|
||||
fwrite($fhlog, "importCSV " . $import_libelle . " : " . $source . "\r\n");
|
||||
|
||||
//! on lance la requête de préparation
|
||||
if ($sql_prepa != "") {
|
||||
qSQL($sql_prepa);
|
||||
}
|
||||
|
||||
$row = 1;
|
||||
|
||||
if (($handle = fopen($source, "r")) !== FALSE) {
|
||||
while (($data = fgetcsv($handle, 500, ";")) !== FALSE) {
|
||||
$num = count($data);
|
||||
if ($headers && $row == 1) {
|
||||
// ce csv contient une ligne d'entêtes de colonnes, on ne traite pas cette ligne
|
||||
} else {
|
||||
eLog("lecture de la ligne " . $row . " avec " . $num . " colonnes");
|
||||
if ($num == $nbColonnes) {
|
||||
// on a le bon nombre de colonnes, on peut continuer
|
||||
|
||||
$code = nettoie_text($data[0]); // on remplace les doubles guillemets par rien
|
||||
$libelle = nettoie_text($data[1]); // on remplace les doubles espaces par un simple espace
|
||||
$siret = nettoie_text($data[2]);
|
||||
$adresse1 = nettoie_text($data[3]);
|
||||
$adresse2 = nettoie_text($data[4]);
|
||||
$adresse3 = nettoie_text($data[5]);
|
||||
$cp = trim($data[6]);
|
||||
if (strlen($cp) == 4) {
|
||||
$cp = "0" . $cp;
|
||||
}
|
||||
$ville = nettoie_text($data[7]);
|
||||
$fkType = $data[8];
|
||||
$contactNom = nettoie_text($data[10]);
|
||||
$contactPrenom = nettoie_text($data[11]);
|
||||
$contactFonction = nettoie_text($data[12]);
|
||||
$telephone = $data[13];
|
||||
$mobile = $data[14];
|
||||
$email = nettoie_text($data[15]);
|
||||
|
||||
$sql = "SELECT c.* FROM clients c WHERE c.code='" . $code . "';";
|
||||
$record = getinfos($sql, "gen");
|
||||
switch (count($record)) {
|
||||
case 0:
|
||||
//! Code client non trouvé = nouveau client
|
||||
$sql = 'INSERT INTO clients SET code="' . $code . '", libelle="' . $libelle . '", siret="' . $siret . '", adresse1="' . $adresse1 . '", adresse2="' . $adresse2 . '", adresse3="' . $adresse3 . '", cp="' . $cp . '", ville="' . $ville . '", ';
|
||||
$sql .= 'type_client="' . $fkType . '", contact_nom="' . $contactNom . '", contact_prenom="' . $contactPrenom . '", contact_fonction="' . $contactFonction . '", telephone="' . $telephone . '", mobile="' . $mobile . '", email="' . $email . '", chk_import=1;';
|
||||
fwrite($fhlog, $row . "---" . $sql . "\r\n");
|
||||
$fkClient = qSQL($sql, "gen", true);
|
||||
fwrite($fhlog, "--- Ajout fait\r\n");
|
||||
$message = "Importation Clients SAP : Le client " . $libelle . " vient d'être créé en " . $ville . " (" . $cp . ")";
|
||||
$sql = 'INSERT INTO notifications SET dateheure="' . date("Y-m-d H:i:s") . '", fk_user=' . $fkUser . ', action="Création fiche", theme="Fiche Client", message="' . $message . '";';
|
||||
qSQL($sql, "gen");
|
||||
|
||||
fwrite($fhlog, "--- Fin Creation ---" . "\r\n");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
//! Un seul enregistrement trouvé : on met à jour le client
|
||||
$rec = $record[0];
|
||||
|
||||
$sql = 'UPDATE clients SET libelle="' . $libelle . '", siret="' . $siret . '", adresse1="' . $adresse1 . '", adresse2="' . $adresse2 . '", adresse3="' . $adresse3 . '", cp="' . $cp . '", ville="' . $ville . '", ';
|
||||
$sql .= 'type_client="' . $fkType . '", contact_nom="' . $contactNom . '", contact_prenom="' . $contactPrenom . '", contact_fonction="' . $contactFonction . '", telephone="' . $telephone . '", mobile="' . $mobile . '", email="' . $email . '", chk_import=1 ';
|
||||
$sql .= 'WHERE code="' . $code . '";';
|
||||
qSQL($sql);
|
||||
fwrite($fhlog, $row . "---" . $sql . "\r\n");
|
||||
fwrite($fhlog, "--- Fin MaJ ---" . "\r\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Plusieurs lignes trouvées pour le même code : Erreur !!
|
||||
$messageErreur = "Erreur Ligne " . $row . ", le code " . $code . " a été trouvé " . count($record) . " fois !! ****************************";
|
||||
fwrite($fhlog, $messageErreur . "\r\n");
|
||||
$erreur++;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$messageErreur = "Erreur Ligne " . $row . ", " . $nbColonnes . " colonnes attendues, mais " . $num . " trouvées !! ***************************";
|
||||
fwrite($fhlog, $messageErreur . "\r\n");
|
||||
$erreur++;
|
||||
}
|
||||
}
|
||||
$row++;
|
||||
} // end while
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
fwrite($fhlog, "Fichier CSV fermé\r\n");
|
||||
//! Enfin on exécute les requêtes de fin
|
||||
if ($sql_final1 != "") {
|
||||
qSQL($sql_final1);
|
||||
fwrite($fhlog, "Requete finale 1 executee : " . $sql_final1 . "\r\n");
|
||||
}
|
||||
if ($sql_final2 != "") {
|
||||
qSQL($sql_final2);
|
||||
fwrite($fhlog, "Requete finale 2 executee : " . $sql_final2 . "\r\n");
|
||||
}
|
||||
fclose($fhlog);
|
||||
if ($erreur > 0) {
|
||||
$dest = "support@unikoffice.com";
|
||||
$sujet = $Conf->_appname . " Import clients : erreurs trouvées";
|
||||
$message = "Message automatique de CLEO 1 (jximport/upload_clients)<br/>Le fichier " . $upLoadDir . $fileName . " a été importé et a montré des erreurs lors de son importation.<br/>Merci";
|
||||
envoieMail($dest, $sujet, $message);
|
||||
}
|
||||
$message = "Importation terminée avec succès pour " . $row . " lignes";
|
||||
eLog($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($erreur == 0) {
|
||||
$reponse = array('ret' => "ok", 'msg' => "Importation terminée avec succès pour " . $row . " lignes");
|
||||
} else {
|
||||
$reponse = array('ret' => "ko", 'msg' => $messageErreur);
|
||||
}
|
||||
echo json_encode($reponse);
|
||||
break;
|
||||
|
||||
case "upload_marche_produits":
|
||||
//! Importation du fichier CSV des produits d'un marché
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
$upLoadDir = ROOT . $Conf->_pathupload . "import" . DS;
|
||||
// on crée le dossier et son arborescence s'il n'existe pas
|
||||
if (!file_exists($upLoadDir)) {
|
||||
mkdir($upLoadDir, 0777, true);
|
||||
}
|
||||
|
||||
ini_set('max_execution_time', '600'); // 600 seconds = 10 minutes
|
||||
|
||||
// if (isset($_POST["importIdMarche"])) {
|
||||
$erreur = "";
|
||||
$fileName = "";
|
||||
$size_max = 10500500; // 10 Mo
|
||||
foreach ($_FILES as $file) {
|
||||
$size_file = filesize($file['tmp_name']);
|
||||
if ($size_file > $size_max) {
|
||||
$erreur = "Vous avez dépassé la taille de fichier autorisée : " . $size_file . " pour un max de " . $size_max;
|
||||
eLog("problème de taille de fichier : " . $size_file . " pour un max de " . $size_max);
|
||||
} else {
|
||||
// on remplace les espaces par des _ dans le nom du fichier sur le serveur
|
||||
$fileName = str_normalize(basename($file["name"]), true);
|
||||
if (strrpos($fileName, ".") === false) {
|
||||
// pas d'extension
|
||||
$typ = "";
|
||||
} else {
|
||||
$typ = substr($fileName, strrpos($fileName, ".") + 1);
|
||||
}
|
||||
eLog("Import fichier : " . $upLoadDir . $fileName);
|
||||
if (move_uploaded_file($file['tmp_name'], $upLoadDir . $fileName)) {
|
||||
eLog("Importation Produits Marché : Fichier " . $upLoadDir . $fileName . " uploadé");
|
||||
|
||||
// Chemin et nom du fichier CSV à importer
|
||||
$source = $upLoadDir . $fileName;
|
||||
|
||||
// L'utilisateur qui importe est celui qui est connecté
|
||||
$fkUser = $Session->_user["rowid"];
|
||||
|
||||
// Structure du fichier CSV
|
||||
$row = 1; // une ligne d'en-tête
|
||||
$headers = true;
|
||||
$separateur = ";";
|
||||
// Item No.;ItemName;Item Group;ListName;List Price;Discount in %;Quantity
|
||||
$colonnes = array('code', 'libelle', 'groupe', 'liste', 'prix', 'prc_discount', 'quantite');
|
||||
$nbColonnes = count($colonnes);
|
||||
|
||||
// Libellé de l'importation
|
||||
$import_libelle = "Importation du fichier CSV des produits d'un marché";
|
||||
|
||||
// Fichier log utilisé pour le debug
|
||||
$nomLog = 'csv-' . date("Y") . '-' . date("m") . '-' . date("d") . '-' . date("H") . '-' . date("i") . '-' . date("s") . '.log';
|
||||
$log = $upLoadDir . $nomLog;
|
||||
$fhlog = fopen($log, "a");
|
||||
fwrite($fhlog, "importCSV " . $import_libelle . " : " . $source . "\r\n");
|
||||
|
||||
//! on détecte l'encodage du fichier CSV
|
||||
$codOrigin = mb_detect_encoding(file_get_contents($source), "Windows-1252, UTF-8, ISO-8859-1, ISO-8859-15", true);
|
||||
fwrite($fhlog, "Encodage détecté : " . $codOrigin . "\r\n");
|
||||
|
||||
|
||||
//! on charge les listes tarifaires par marché
|
||||
$sql = 'SELECT l.rowid, l.fk_marche, l.mot_cle, l.terme_achat, l.terme_vente FROM marches_listes l LEFT JOIN marches m ON l.fk_marche=m.rowid WHERE m.active=1;';
|
||||
$marchesListes = getinfos($sql, "gen");
|
||||
//! Tableaux des marchés mis à jour pour effectuer en fin d'import la purge des produits non importés de ces marchés
|
||||
$idMarches = array();
|
||||
$idMarche = 0; // le marché en cours d'importation
|
||||
$termeAchat = "ACHAT";
|
||||
$termeVente = "VENTE";
|
||||
//! tableaux des marché-code-incrément discount pour gérer dans quel discount on enregistre la paire discount-quantité (de 1 à 6)
|
||||
$lstMarchesCodesDiscount = array();
|
||||
|
||||
//! on lance la requête de préparation
|
||||
$sql = 'UPDATE produits SET chk_import=0;';
|
||||
qSQL($sql, "gen");
|
||||
fwrite($fhlog, "Requête de preparation terminée : " . $sql . "\r\n");
|
||||
|
||||
//! On récupère le contenu de la table produits pour vérifier si le produit existe ou non à chaque ligne
|
||||
$sql = 'SELECT rowid, CONCAT(fk_marche, "-", code) AS marchecode FROM produits;';
|
||||
$produitsExistants = getinfos($sql, "gen");
|
||||
|
||||
//! on récupère le nombre de lignes de ce fichier CSV pour alimenter la progress bar
|
||||
$totUpload = 0;
|
||||
$fh = fopen($source, 'rb') or die("ERROR OPENING DATA");
|
||||
while (fgets($fh) !== false) $totUpload++;
|
||||
fclose($fh);
|
||||
|
||||
if (($handle = fopen($source, "rb")) !== FALSE) {
|
||||
fwrite($fhlog, "Fichier CSV ouvert : démarrage de la lecture ligne par ligne\r\n");
|
||||
eLog("jximport démarrage de la lecture du fichier csv ligne par ligne");
|
||||
|
||||
while (($data = fgetcsv($handle, 700, $separateur)) !== FALSE) {
|
||||
$num = count($data);
|
||||
if ($headers && $row == 1) {
|
||||
// ce csv contient une ligne d'entêtes de colonnes, on ne traite pas cette ligne
|
||||
fwrite($fhlog, "Ok lecture CSV 1ère ligne en-tête\r\n");
|
||||
} else {
|
||||
// on traite la ligne
|
||||
$code = trim($data[0]);
|
||||
if (strlen($code) > 0) {
|
||||
// on ne traite pas la ligne si le code est vide : dernière ligne du fichier ?
|
||||
$libelle = str_replace('"', '', trim($data[1])); // on remplace les doubles guillemets par rien
|
||||
// on réencode en ISO 8859-1 pour éviter les problèmes d'accent
|
||||
if ($codOrigin == "UTF-8") {
|
||||
$libelle = utf8_decode($libelle);
|
||||
}
|
||||
if ($codOrigin != "ISO-8859-1") {
|
||||
// Convertir en ISO 8859-1
|
||||
$libelle = iconv($codOrigin, "ISO-8859-15//IGNORE", $libelle);
|
||||
}
|
||||
|
||||
$groupe = str_replace(" ", " ", trim($data[2])); // on remplace les doubles espaces par un simple espace
|
||||
$liste = trim($data[3]);
|
||||
|
||||
if ($idMarche == 0) {
|
||||
foreach ($marchesListes as $corr) {
|
||||
$lmc = strlen($corr["mot_cle"]);
|
||||
if ($lmc > 0) {
|
||||
$posMotCle = strpos($liste, $corr["mot_cle"]);
|
||||
if ($posMotCle !== false) {
|
||||
//! le mot clé est bien dans $liste
|
||||
$idMarche = $corr["fk_marche"];
|
||||
$termeAchat = $corr["terme_achat"];
|
||||
$termeVente = $corr["terme_vente"];
|
||||
|
||||
//! on rajoute ce fk_marche dans la liste des marchés traités pour effectuer la purge des chk_import=0 à la fin
|
||||
if (!in_array($idMarche, $idMarches)) {
|
||||
$idMarches[] = $idMarche;
|
||||
// C'est la 1ère ligne de produit pour ce marché :on supprime d'office tous les prix d'achat et de vente des produits de ce marché
|
||||
$sql = 'UPDATE produits p SET p.prix_achat_net=0, p.prix_vente=0, p.prc_discount_1=0, p.quantite_1=0, p.prc_discount_2=0, p.quantite_2=0, p.prc_discount_3=0, p.quantite_3=0, p.prc_discount_4=0, p.quantite_4=0, p.prc_discount_5=0, p.quantite_5=0, p.prc_discount_6=0, p.quantite_6=0 WHERE fk_marche=' . $idMarche;
|
||||
eLog("Import produits marché : suppression des prix d'achat et de vente pour ce marché : " . $idMarche);
|
||||
fwrite($fhlog, "Nettoyage des infos produits du marché " . $idMarche . "\r\n");
|
||||
eLog($sql);
|
||||
qSQL($sql, "gen");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($idMarche == 0) {
|
||||
//! on n'a pas trouvé de marché correspondant à la liste
|
||||
$erreur = "ligne " . $row . " : pas de marché correspondant à la liste (" . $liste . ")";
|
||||
fwrite($fhlog, $erreur . "\r\n");
|
||||
break;
|
||||
}
|
||||
// fwrite($fhlog, "ligne " . $row . " : c'est le marché " . $fkMarche . " avec pour achat (" . $termeAchat . ") et pour vente (" . $termeVente . ")\r\n");
|
||||
fwrite($fhlog, "ligne " . $row . " : code (" . $code . ") libelle (" . $libelle . ") groupe (" . $groupe . ") liste (" . $liste . ")\r\n");
|
||||
|
||||
// pour le prix, on vérifie qu'il n'y ait pas de séparateur de milliers avec un . ou un espace, sinon on le l'enlève (on ne le fait que pour les chaînes d'un longueur > 6)
|
||||
// Ex : 16,63 on ne traite pas, 1.663,63 ou 1 663,63 on traite
|
||||
if (strlen($data[4]) > 6) {
|
||||
$data[4] = str_replace(".", "", $data[4]);
|
||||
$data[4] = str_replace(" ", "", $data[4]);
|
||||
}
|
||||
// ensuite on remplace à chaque fois la virgule de la décimale par un point pour le prix
|
||||
$prix = floatval(str_replace(",", ".", trim($data[4])));
|
||||
$prc_discount = floatval(str_replace(",", ".", trim($data[5])));
|
||||
$quantite = intval(str_replace(",", ".", trim($data[6])));
|
||||
|
||||
$set = 'libelle="' . $libelle . '", groupe="' . $groupe . '", liste="' . $liste . '", ';
|
||||
$posHa = strpos($liste, $termeAchat);
|
||||
if ($posHa !== false) {
|
||||
$set .= 'prix_achat_net=' . $prix . ', ';
|
||||
} else {
|
||||
$posVe = strpos($liste, $termeVente);
|
||||
if ($posVe !== false) {
|
||||
// ce n'est que pour la vente que l'on active le produit (on affiche ce produit que s'il est à vendre)
|
||||
$set .= 'prix_vente=' . $prix . ', active=1, chk_import=1, ';
|
||||
} else {
|
||||
$erreur = 'Erreur sur la ligne ' . $row . ' où la LISTNAME ne contient ni ACHAT ni VENTE';
|
||||
fwrite($fhlog, $erreur . "\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($prc_discount > 0) {
|
||||
$lstMarchesCodesDiscounts[] = $idMarche . '-' . $code . '-';
|
||||
$incDiscount = array_count_values($lstMarchesCodesDiscounts)[$idMarche . '-' . $code . '-'];
|
||||
if ($incDiscount < 7) {
|
||||
$set .= 'prc_discount_' . $incDiscount . '=' . $prc_discount . ', quantite_' . $incDiscount . '=' . $quantite . ', ';
|
||||
} else {
|
||||
$erreur = 'Erreur sur la ligne ' . $row . ' où le nombre de discount > 0 est supérieur à 6 : code produit (' . $code . ')';
|
||||
fwrite($fhlog, $erreur . "\r\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// pas de % discount sur cette ligne
|
||||
if ($posHa !== false) {
|
||||
// et on est sur une ligne Achat, donc on met tous les prc_discount et qte à 0
|
||||
$set .= 'prc_discount_1=0, prc_discount_2=0, prc_discount_3=0, prc_discount_4=0, prc_discount_5=0, prc_discount_6=0, quantite_1=0, quantite_2=0, quantite_3=0, quantite_4=0, quantite_5=0, quantite_6=0, ';
|
||||
}
|
||||
}
|
||||
// on enlève la virgule de fin
|
||||
$set = substr($set, 0, strlen($set) - 2);
|
||||
|
||||
//! On regarde si ce produit existe déjà sur ce marché
|
||||
$idExistant = 0;
|
||||
$aRechercher = $idMarche . '-' . $code;
|
||||
foreach ($produitsExistants as $produitExistant) {
|
||||
if ($produitExistant["marchecode"] == $aRechercher) {
|
||||
$idExistant = $produitExistant["rowid"];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($idExistant > 0) {
|
||||
// le produit existe déjà, on le met à jour
|
||||
$sql = 'UPDATE produits SET ' . $set . ' WHERE rowid=' . $idExistant . ';';
|
||||
fwrite($fhlog, $row . " update : " . $sql . "\r\n");
|
||||
qSQL($sql, "gen");
|
||||
} else {
|
||||
// le produit n'existe pas, on le crée
|
||||
$sql = 'INSERT INTO produits SET fk_marche=' . $idMarche . ', code="' . $code . '", ' . $set . ';';
|
||||
fwrite($fhlog, $row . " insert : " . $sql . "\r\n");
|
||||
$newId = qSQL($sql, "gen", true);
|
||||
// on ajoute le produit à la liste des produits existants, pour gérer le cas où on a plusieurs fois le même produit dans le fichier (cas de plusieurs discounts)
|
||||
$produitsExistants[] = array("rowid" => $newId, "marchecode" => $idMarche . '-' . $code);
|
||||
}
|
||||
// eLog("jximport ligne " . $row . " : " . $sql);
|
||||
fwrite($fhlog, "ligne " . $row . " : import terminé\r\n");
|
||||
}
|
||||
}
|
||||
$row++;
|
||||
// echo json_encode(['progress' => ($row / $totUpload) * 100]);
|
||||
// ob_flush();
|
||||
// flush();
|
||||
// usleep(50);
|
||||
} // end while
|
||||
fclose($handle);
|
||||
fwrite($fhlog, "Fichier CSV fermé\r\n");
|
||||
} // end if fopen
|
||||
|
||||
fwrite($fhlog, "Fin de la boucle\r\n");
|
||||
// Enfin, on désactive les produits qui n'ont pas été importés dans le ou les marchés traités
|
||||
// la règle est qu'on traite un marché en entier et non partiellement
|
||||
// on commence par récupérer la liste des marchés traités
|
||||
foreach ($idMarches as $idMarch) {
|
||||
$sql = 'UPDATE produits SET active=0 WHERE fk_marche=' . $idMarch . ' AND chk_import=0;';
|
||||
eLog($sql);
|
||||
fwrite($fhlog, "Désactivation des produits non importés : " . $sql . "\r\n");
|
||||
qSQL($sql, "gen");
|
||||
// et on enregistre la date de l'import au niveau de chaque marché concerné
|
||||
$sql = 'UPDATE marches SET date_import="' . date("Y-m-d H:i:s") . '" WHERE rowid=' . $idMarch . ';';
|
||||
eLog($sql);
|
||||
fwrite($fhlog, "Mise à jour de la date d'import dans marches : " . $sql . "\r\n");
|
||||
qSQL($sql, "gen");
|
||||
}
|
||||
fwrite($fhlog, "Fin de l'importation et fermeture du fichier log\r\n");
|
||||
fclose($fhlog);
|
||||
} // end if move_uploaded
|
||||
} // end if size_max
|
||||
} // end foreach file uploaded
|
||||
|
||||
// ob_clean();
|
||||
if ($fileName == "") {
|
||||
$ret = array('ret' => "ko", 'msg' => "Aucun fichier à importer");
|
||||
} else {
|
||||
if ($erreur == "") {
|
||||
$ret = array('ret' => "ok", 'msg' => "L'importation est terminée et s'est bien déroulée");
|
||||
} else {
|
||||
$ret = array('ret' => "ko", 'msg' => $erreur);
|
||||
}
|
||||
}
|
||||
// } else {
|
||||
// $ret = array('ret' => "ko", 'msg' => "Pas d'idMarche");
|
||||
// }
|
||||
echo json_encode($ret);
|
||||
break;
|
||||
|
||||
case "upload_sap_pdf":
|
||||
if (isset($_POST["cid"])) {
|
||||
$cid = nettoie_input($_POST["cid"]);
|
||||
eLog("upload_sap_pdf: " . $cid);
|
||||
|
||||
$upDir = ROOT . $Conf->_pathupload . "devis" . DS;
|
||||
if (!is_dir($upDir)) {
|
||||
mkdir($upDir, 0777, true);
|
||||
}
|
||||
eLog("Dossier upload : " . $upDir);
|
||||
$ret = array('ret' => "ko", 'msg' => "Erreur Aucun PDF SAP à importer");
|
||||
foreach ($_FILES as $file) {
|
||||
$size_file = filesize($file['tmp_name']);
|
||||
$size_max = 10500600;
|
||||
if ($size_file > $size_max) {
|
||||
$ret = array('ret' => "ko", 'msg' => "Le PDF SAP a une taille trop importante : " . $size_file . " pour un max de " . $size_max);
|
||||
eLog("problème de taille de fichier : " . $size_file . " pour un max de " . $size_max);
|
||||
} else {
|
||||
//! on remplace les espaces par des _ dans le nom du fichier sur le serveur
|
||||
$filename = str_normalize(basename($file["name"]), true);
|
||||
if (strrpos($filename, ".") === false) {
|
||||
//! pas d'extension
|
||||
$typ = "";
|
||||
} else {
|
||||
$typ = substr($filename, strrpos($filename, ".") + 1);
|
||||
}
|
||||
eLog("le fichier sera uploadé : " . $upDir . $filename);
|
||||
if (move_uploaded_file($file['tmp_name'], $upDir . $filename)) {
|
||||
// On enregistre les infos de ce fichier dans la table medias
|
||||
$upDirMedia = "pub/files/upload/devis/";
|
||||
$sql = 'INSERT INTO medias SET dir0="' . $upDirMedia . '", support="devis_pdf_sap", support_rowid=' . $cid . ', fichier="' . $filename . '", type_fichier="' . $typ . '", date_creat="' . date("Y-m-d H:i:s") . '", fk_user_creat=' . $fkUser . ';';
|
||||
qSQL($sql, "gen");
|
||||
|
||||
// On change le statut du devis et on prévient le RR de la réception du PDF SAP par email
|
||||
$sql = 'UPDATE devis SET fk_statut_devis=6, date_modif="' . date("Y-m-d H:i:s") . '", fk_user_modif=' . $fkUser . ' WHERE rowid=' . $cid . ';';
|
||||
qSQL($sql, "gen");
|
||||
|
||||
// On ajoute un message automatique dans le chat
|
||||
$sql = 'INSERT INTO devis_histo SET fk_devis=' . $cid . ', fk_user=' . $fkUser . ', date_histo="' . date("Y-m-d H:i:s") . '", commentaire="Le PDF SAP du devis a été déposé pour vérification du RR";';
|
||||
qSQL($sql, "gen");
|
||||
|
||||
// On récupère les infos du devis pour envoyer un email au RR
|
||||
$sql = 'SELECT d.rowid, d.date_demande, d.fk_client, d.lib_new_client, d.cp_new_client, d.ville_new_client, u.prenom, u.libelle, u.email FROM devis d LEFT JOIN users u ON d.fk_user = u.rowid WHERE d.rowid=' . $cid . ';';
|
||||
$res = getinfos($sql, "gen");
|
||||
$data = $res[0];
|
||||
|
||||
$nom = $data["prenom"] . " " . $data["libelle"];
|
||||
$dest = $data["email"];
|
||||
$idClient = $data["fk_client"];
|
||||
$nomClient = "";
|
||||
|
||||
if ($idClient == 0) {
|
||||
$nomClient = $data["lib_new_client"] . ", (" . $data["cp_new_client"] . " - " . $data["ville_new_client"] . ")";
|
||||
} else {
|
||||
$sql = 'SELECT c.libelle, c.cp, c.ville FROM clients c WHERE c.rowid=' . $idClient . ';';
|
||||
$client = getinfos($sql, "gen");
|
||||
if (count($client) == 1) {
|
||||
$nomClient = $client[0]["libelle"] . " (" . $client[0]["cp"] . " - " . $client[0]["ville"] . ")";
|
||||
}
|
||||
}
|
||||
|
||||
$sujet = "DEVIS - Réception Devis SAP";
|
||||
$message = "Bonjour " . $nom . ",<br><br>Vous venez de recevoir la version SAP PDF d'un de vos devis. Merci de procéder à la vérification de celui-ci et de le valider sur CLEO.<br/>";
|
||||
$message .= "Devis : #" . $data["rowid"] . " du " . affiche_date($data["date_demande"]) . "<br/>";
|
||||
$message .= "Client : " . $nomClient . "<br/>";
|
||||
$message .= "<br/><br/>Cordialement,<br/>L'équipe ADV<br/>";
|
||||
$message .= "Email généré automatiquement par l'application CLEO de gestion des devis";
|
||||
|
||||
// Ajout de logs détaillés pour l'envoi d'email
|
||||
eLog("Tentative d'envoi d'email à " . $dest . " avec le sujet : " . $sujet);
|
||||
$email = envoieMail($dest, $sujet, $message);
|
||||
eLog("Résultat de l'envoi d'email : " . json_encode($email));
|
||||
|
||||
$ret = array('ret' => "ok", 'msg' => "L'importation est terminée et s'est bien déroulée");
|
||||
eLog("Le fichier PDF SAP a été correctement uploadé");
|
||||
} else {
|
||||
$ret = array('ret' => "ko", 'msg' => "Erreur lors de l'importation du PDF SAP");
|
||||
eLog("Le fichier PDF SAP n'a pas pu être uploadé");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$ret = array('ret' => "ko", 'msg' => "Pas d'id");
|
||||
}
|
||||
echo json_encode($ret);
|
||||
break;
|
||||
|
||||
case "get_files":
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid) && isset($data->sup)) {
|
||||
$idMedia = nettoie_input($data->cid);
|
||||
$support = nettoie_input($data->sup);
|
||||
$sql = 'SELECT * FROM medias WHERE support="' . $support . '" AND support_rowid=' . $idMedia . ';';
|
||||
$ret = getinfos($sql, "gen");
|
||||
} else {
|
||||
$ret = array('ret' => "ko", 'msg' => "Pas d'id");
|
||||
}
|
||||
echo json_encode($ret);
|
||||
break;
|
||||
|
||||
case "delete_file":
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$idMedia = nettoie_input($data->cid);
|
||||
$sql = 'DELETE FROM medias WHERE rowid=' . $idMedia . ';';
|
||||
qSQL($sql, "gen");
|
||||
$ret = array('ret' => "ok", 'msg' => "Le fichier a bien été supprimé");
|
||||
} else {
|
||||
$ret = array('ret' => "ko", 'msg' => "Pas d'id");
|
||||
}
|
||||
echo json_encode($ret);
|
||||
break;
|
||||
}
|
||||
exit();
|
||||
852
controllers/cjxpost.php
Normal file
@@ -0,0 +1,852 @@
|
||||
<?php
|
||||
global $Session;
|
||||
global $Conf;
|
||||
global $Route;
|
||||
|
||||
//! on va chercher la data de la Session au format tableau
|
||||
$session_data = $Session->get_data();
|
||||
//! où on récupère le fk_tiers sur lequel l'utilisateur travaille
|
||||
if (isset($session_data["tiers"])) {
|
||||
$fk_tiers = $session_data["tiers"];
|
||||
} else {
|
||||
$fk_tiers = 0;
|
||||
}
|
||||
|
||||
$fk_user = $Session->_user["rowid"];
|
||||
|
||||
eLog("jxpost action : " . $Route->_action);
|
||||
|
||||
function cleanData(&$str)
|
||||
{
|
||||
// Fonction de nettoyage des données pour l'export Excel
|
||||
if ($str == 't') $str = 'TRUE';
|
||||
if ($str == 'f') $str = 'FALSE';
|
||||
if (preg_match("/^0/", $str) || preg_match("/^\+?\d{8,}$/", $str) || preg_match("/^\d{4}.\d{1,2}.\d{1,2}/", $str)) {
|
||||
$str = "'$str";
|
||||
}
|
||||
if (strstr($str, '"')) $str = '"' . str_replace('"', '""', $str) . '"';
|
||||
$str = mb_convert_encoding($str, 'UTF-16LE', 'UTF-8');
|
||||
}
|
||||
|
||||
function filterData(&$str)
|
||||
{
|
||||
$str = preg_replace("/\t/", "\\t", $str);
|
||||
$str = preg_replace("/\r?\n/", "\\n", $str);
|
||||
if (strstr($str, '"')) $str = '"' . str_replace('"', '""', $str) . '"';
|
||||
}
|
||||
|
||||
switch ($Route->_action) {
|
||||
case "filter":
|
||||
$idfilter = $_POST["idfilter"];
|
||||
switch ($idfilter) {
|
||||
case "filtertiers":
|
||||
$Session->set_data($idfilter . "_search", $_POST["filter_search"]);
|
||||
$Session->set_data($idfilter . "_contact", $_POST["filter_contact"]);
|
||||
$Session->set_data($idfilter . "_ape", $_POST["filter_ape"]);
|
||||
if (isset($_POST["filter_recent"])) {
|
||||
$recent = 1;
|
||||
} else {
|
||||
$recent = 0;
|
||||
}
|
||||
$Session->set_data($idfilter . "_recent", $recent);
|
||||
if (isset($_POST["filter_agenda"])) {
|
||||
$agenda = 1;
|
||||
} else {
|
||||
$agenda = 0;
|
||||
}
|
||||
$Session->set_data($idfilter . "_agenda", $agenda);
|
||||
if (isset($_POST["filter_archive"])) {
|
||||
$archive = 1;
|
||||
} else {
|
||||
$archive = 0;
|
||||
}
|
||||
$Session->set_data($idfilter . "_archive", $archive);
|
||||
$Session->set_data($idfilter . "_type_tiers", $_POST["filter_type_tiers"]);
|
||||
$Session->set_data($idfilter . "_ville", $_POST["filter_villeA"]); //! filter_villeA parce que c'est un autocomplete : retourne le rowid qui est en fait la ville !
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case "upfind":
|
||||
$tab = $_POST["win"];
|
||||
$lid = $Route->_param1;
|
||||
|
||||
if ($Conf::erp) {
|
||||
$sql = 'SELECT * FROM medias WHERE dir2="' . $tab . '" AND support_rowid=' . $lid . ';';
|
||||
} else {
|
||||
$sql = 'SELECT * FROM medias WHERE support="' . $tab . '" AND support_rowid=' . $lid . ';';
|
||||
}
|
||||
$upls = array();
|
||||
$upls = getinfos($sql, "groupe");
|
||||
echo json_encode($upls);
|
||||
break;
|
||||
|
||||
case "updelete":
|
||||
updelete($_POST);
|
||||
break;
|
||||
|
||||
case "upload":
|
||||
if ($Conf::erp) {
|
||||
upload($_POST);
|
||||
} else {
|
||||
upload_old($_POST);
|
||||
}
|
||||
break;
|
||||
|
||||
case "medias":
|
||||
$user = $Session->_user["rowid"];
|
||||
$type = $Route->_param1; //! facture par exemple
|
||||
if (isset($_POST["rowid"])) {
|
||||
$rowid = $_POST["rowid"]; //! c'est le rowid de la facture par exemple
|
||||
if ($type == "facture" || $type == "devis") {
|
||||
//! on doit aller chercher le num_facture pour avoir le nom du fichier
|
||||
if ($type == "facture") {
|
||||
$num_facture = getdata("devis", $rowid, "num_facture");
|
||||
} else {
|
||||
$num_facture = getdata("devis", $rowid, "num_devis");
|
||||
}
|
||||
$fk_tiers = getdata("devis", $rowid, "fk_soc");
|
||||
$nom_tiers = str_normalize(getdata("clients", $fk_tiers, "libelle", "groupe"));
|
||||
if ($Conf->_entite["raz_num_devis"]) {
|
||||
$num_facture = substr('000' . $num_facture, -3);
|
||||
} else {
|
||||
$num_facture = substr('0000' . $num_facture, -4);
|
||||
}
|
||||
$support = $fk_tiers;
|
||||
$filename = $fk_tiers . "_" . $type . "_" . $num_facture . ".pdf";
|
||||
if (isset($_POST["relance"])) {
|
||||
$filename = $fk_tiers . "_" . $type . "_" . $num_facture . "_relance_" . date("Ymd") . ".pdf";
|
||||
}
|
||||
$typ = "pdf";
|
||||
$dir0 = "tiers";
|
||||
$dir1 = $nom_tiers;
|
||||
} else {
|
||||
$support = $type;
|
||||
$filename = $rowid . ".pdf";
|
||||
$typ = "pdf";
|
||||
$rep = $type . DS . $rowid . DS . "pdf";
|
||||
$dir0 = "tiers";
|
||||
$dir1 = "dir1";
|
||||
}
|
||||
$dir2 = $type;
|
||||
$des = "";
|
||||
$pos = "L";
|
||||
$hau = 0;
|
||||
$lar = 0;
|
||||
//! On vérifie d'abord qu'il n'y ait pas un média existant pour le même support, le même support_rowid et le même nom de fichier : doublon !
|
||||
$sql = 'SELECT * FROM medias WHERE dir0="' . $dir0 . '" AND dir1="' . $dir1 . '" AND dir2="' . $dir2 . '" AND fichier="' . $filename . '";';
|
||||
$doublon = getinfos($sql, "groupe");
|
||||
if (count($doublon) == 0) {
|
||||
//! il n'y a pas de doublon, on peut créer l'enregistrement
|
||||
$sql = 'INSERT INTO medias SET dir0="' . $dir0 . '", dir1="' . $dir1 . '", dir2="' . $dir2 . '", support_rowid=' . $rowid . ', fichier="' . $filename . '", fk_user_creat=' . $user . ', ';
|
||||
$sql .= 'type_fichier="' . $typ . '", date_creat="' . date("Y-m-d H:i:s") . '", description="' . $des . '", position="' . $pos . '", hauteur=' . $hau . ', largeur=' . $lar . ';';
|
||||
} else {
|
||||
//! l'enregistrement existe déjà : on met à jour date_modif et fk_user_modif
|
||||
$sql = 'UPDATE medias SET date_modif="' . date("Y-m-d H:i:s") . '", fk_user_modif=' . $user . ' WHERE dir0="' . $dir0 . '" AND dir1="' . $dir1 . '" AND dir2="' . $dir2 . '" AND fichier="' . $filename . '";';
|
||||
}
|
||||
eLog("jxpost medias : " . $sql);
|
||||
qSQL($sql, "groupe");
|
||||
}
|
||||
break;
|
||||
|
||||
case "refresh":
|
||||
$win = $Route->_param1;
|
||||
if ($win != "") {
|
||||
if (isset($_POST["input"])) {
|
||||
$input = $_POST["input"];
|
||||
switch ($win) {
|
||||
case "winaction":
|
||||
//! un refresh dans la fenêtre modale winaction
|
||||
if ($input == "fk_contact") {
|
||||
$sql = "SELECT rowid, CONCAT(firstname, ' ', name) AS libelle FROM contacts WHERE fk_soc=" . $fk_tiers . " ORDER BY libelle;";
|
||||
$res = qSQL($sql, "groupe");
|
||||
$arr = array();
|
||||
while ($rec = $res->fetch_assoc()) {
|
||||
$arr[] = $rec;
|
||||
}
|
||||
$jsonresult = json_encode($arr);
|
||||
$lignes = $jsonresult;
|
||||
echo $lignes;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "mediafind":
|
||||
$tab = $_POST["win"];
|
||||
$lid = $Route->_param1;
|
||||
|
||||
if ($Conf::erp) {
|
||||
$sql = 'SELECT * FROM medias WHERE dir2="' . $tab . '" AND support_rowid=' . $lid . ';';
|
||||
} else {
|
||||
$sql = 'SELECT * FROM medias WHERE support="' . $tab . '" AND support_rowid=' . $lid . ';';
|
||||
}
|
||||
$upls = array();
|
||||
$upls = getinfos($sql, "groupe");
|
||||
$upls = $upls[0];
|
||||
echo json_encode($upls);
|
||||
break;
|
||||
|
||||
case "getdata":
|
||||
$chp = $_POST["chp"];
|
||||
$typ = $Route->_param1;
|
||||
$sql = "";
|
||||
switch ($typ) {
|
||||
case "tiers":
|
||||
$sql = "SELECT $chp AS data FROM clients WHERE rowid=" . $fk_tiers . ";";
|
||||
$dbn = "groupe";
|
||||
break;
|
||||
}
|
||||
|
||||
$upls = array();
|
||||
if ($sql != "") {
|
||||
$upls = getinfos($sql, $dbn);
|
||||
$upls = $upls[0];
|
||||
}
|
||||
echo json_encode($upls);
|
||||
break;
|
||||
|
||||
case "setsession":
|
||||
if (isset($_POST["key"]) && isset($_POST["val"])) {
|
||||
$Session->set_data($_POST["key"], $_POST["val"]);
|
||||
}
|
||||
break;
|
||||
|
||||
case "autocomplete":
|
||||
if (isset($_POST["term"])) {
|
||||
$term = $_POST["term"];
|
||||
$tabl = $_POST["table"];
|
||||
$fiel = $_POST["field"];
|
||||
$fiel2 = isset($_POST["field2"]) ? $_POST["field2"] : "";
|
||||
$fiel3 = isset($_POST["field3"]) ? $_POST["field3"] : "";
|
||||
$fiel4 = isset($_POST["field4"]) ? $_POST["field4"] : "";
|
||||
$fiel5 = isset($_POST["field5"]) ? $_POST["field5"] : "";
|
||||
$fiel6 = isset($_POST["field6"]) ? $_POST["field6"] : "";
|
||||
$fiel7 = isset($_POST["field7"]) ? $_POST["field7"] : "";
|
||||
$fiel8 = isset($_POST["field8"]) ? $_POST["field8"] : "";
|
||||
$grou = isset($_POST["group"]) ? $_POST["group"] : "";
|
||||
|
||||
if (strtolower(substr($tabl, 0, 7)) == "select ") {
|
||||
//! C'est directement une requête
|
||||
$sql = $tabl;
|
||||
$minisql = strtolower($sql);
|
||||
$poswhere = strpos($minisql, " where ");
|
||||
if ($poswhere === FALSE) {
|
||||
//! il n'y a pas de clause WHERE dans la requête
|
||||
//! on regarde s'il y a une clause ORDER BY pour pouvoir insérer la clause WHERE juste avant
|
||||
$posorder = strpos($minisql, " order by ");
|
||||
if ($posorder === FALSE) {
|
||||
//! il n'y a pas non plus de clause ORDER BY dans la requête, on ajoute le WHERE à la fin
|
||||
$posgroup = strpos($minisql, " group by ");
|
||||
if ($posgroup === FALSE) {
|
||||
$sql = str_replace(';', ' WHERE ' . $fiel . ' LIKE "%' . $term . '%";', $sql);
|
||||
} else {
|
||||
//! il y a une clause GROUP BY
|
||||
$sql = str_replace(' GROUP BY ', ' WHERE ' . $fiel . ' LIKE "%' . $term . '%" GROUP BY ', $sql);
|
||||
}
|
||||
} else {
|
||||
//! il y a une clause ORDER BY
|
||||
$sql = str_replace(' ORDER BY ', ' WHERE ' . $fiel . ' LIKE "%' . $term . '%" ORDER BY ', $sql);
|
||||
}
|
||||
} else {
|
||||
//! il y a déjà une condition WHERE dans la requête définie
|
||||
$sql = str_replace(' WHERE ', ' WHERE ' . $fiel . ' LIKE "%' . $term . '%" AND ', $sql);
|
||||
}
|
||||
} else {
|
||||
if ($grou == "") {
|
||||
$sql = 'SELECT * FROM ' . $tabl . ' WHERE ' . $fiel . ' LIKE "%' . $term . '%" ORDER BY ' . $fiel . ';';
|
||||
} else {
|
||||
$sql = 'SELECT * FROM ' . $tabl . ' WHERE ' . $fiel . ' LIKE "%' . $term . '%" GROUP BY ' . $fiel . ' ORDER BY ' . $fiel . ';';
|
||||
}
|
||||
}
|
||||
eLog("autocomplete : " . $sql);
|
||||
$res = qSQL($sql);
|
||||
$rows = array();
|
||||
while ($r = mysqli_fetch_assoc($res)) {
|
||||
$rows[] = $r;
|
||||
}
|
||||
echo json_encode($rows);
|
||||
exit();
|
||||
}
|
||||
break;
|
||||
|
||||
case "get_context":
|
||||
//! Renvoie le contexte de l'utilisateur
|
||||
$ajson = array();
|
||||
$ajson["user"] = $Session->_user;
|
||||
$ajson["session"] = $Session->get_data();
|
||||
$ajson["devip"] = $Conf->_devIp ? "1" : "0";
|
||||
echo json_encode($ajson);
|
||||
break;
|
||||
|
||||
case "load_client":
|
||||
//! Charge les infos d'un client
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
$sql = 'SELECT c.* FROM clients c WHERE c.rowid=' . $cid . ';';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
} else {
|
||||
echo "Erreur : pas de client";
|
||||
}
|
||||
break;
|
||||
|
||||
case "search_clients":
|
||||
//! Cherche les clients correspondant à un libellé, une adresse, un code postal, une ville, un nom, un prénom, une fonction ou un email
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->search)) {
|
||||
$search = nettoie_input($data->search);
|
||||
$sql = 'SELECT c.rowid, c.libelle, c.type_client, c.adresse1, c.cp, c.ville FROM clients c ';
|
||||
$sql .= 'WHERE c.libelle LIKE "%' . $search . '%" OR c.adresse1 LIKE "%' . $search . '%" OR c.cp LIKE "%' . $search . '%" OR c.ville LIKE "%' . $search . '%" OR c.contact_nom LIKE "%' . $search . '%" OR c.contact_prenom LIKE "%' . $search . '%" OR c.contact_fonction LIKE "%' . $search . '%" OR c.email LIKE "%' . $search . '%" ';
|
||||
$sql .= 'ORDER BY c.libelle;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
} else {
|
||||
$ret = array('ret' => "ko");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "load_marches":
|
||||
//! charge des infos de tous les marchés
|
||||
$sql = 'SELECT m.rowid, m.libelle FROM marches m ORDER BY m.libelle DESC;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
break;
|
||||
|
||||
case "load_marche":
|
||||
//! Charge les infos d'un marché
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
$sql = 'SELECT m.* FROM marches m WHERE m.rowid=' . $cid . ';';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
} else {
|
||||
$ret = array('ret' => "ko");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "search_produits":
|
||||
//! Cherche les produits correspondant à un code ou à un libellé
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->search)) {
|
||||
$search = nettoie_input($data->search);
|
||||
$sql = 'SELECT p.*, pf.famille FROM produits p LEFT JOIN produits_familles pf ON p.fk_famille_produit=pf.rowid ';
|
||||
$sql .= 'WHERE (p.code LIKE "%' . $search . '%" OR p.libelle LIKE "%' . $search . '%" OR p.groupe LIKE "%' . $search . '%" OR pf.famille LIKE "%' . $search . '%") AND p.prix_vente>0 AND p.active=1 ';
|
||||
$sql .= 'ORDER BY p.libelle;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
} else {
|
||||
$ret = array('ret' => "ko");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "load_familles":
|
||||
//! Charge les familles de produits existantes
|
||||
$sql = 'SELECT xf.* FROM x_familles xf ORDER BY xf.ordre;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
break;
|
||||
|
||||
case "load_familles_groupes":
|
||||
//! Charge les familles par groupes de produits existantes
|
||||
$sql = 'SELECT pf.* FROM produits_familles pf ORDER BY pf.ordre, pf.groupe;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
break;
|
||||
|
||||
case "save_familles":
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
//! on récupère les familles existantes
|
||||
$sql = 'SELECT pf.rowid FROM produits_familles pf ORDER BY pf.rowid;';
|
||||
$pf = getinfos($sql, "gen");
|
||||
//! on les désactive toutes
|
||||
$sql = 'UPDATE produits_familles SET active=0;';
|
||||
qSQL($sql);
|
||||
|
||||
//! on boucle sur les familles reçues
|
||||
foreach ($data as $row) {
|
||||
$rowid = nettoie_input($row->id);
|
||||
$set = 'groupe="' . nettoie_input(trim($row->groupe)) . '", ';
|
||||
$set .= 'ordre="' . nettoie_input(trim($row->ordre)) . '", ';
|
||||
$set .= 'fk_famille="' . nettoie_input(trim($row->famille)) . '", ';
|
||||
$set .= 'code_maintenance="' . nettoie_input(trim($row->maintenance)) . '", ';
|
||||
$set .= 'marge_rr="' . nettoie_input(trim($row->margerr)) . '", ';
|
||||
$set .= 'marge_dv="' . nettoie_input(trim($row->margedv)) . '", ';
|
||||
$set .= 'active=1 ';
|
||||
//! on recherche si la famille existe déjà
|
||||
$found = false;
|
||||
foreach ($pf as $p) {
|
||||
if ($p["rowid"] == $rowid) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
//! Si la famille n'existe pas, on la crée
|
||||
$sql = 'INSERT INTO produits_familles SET ' . $set . ';';
|
||||
} else {
|
||||
//! Sinon on la met à jour
|
||||
$sql = 'UPDATE produits_familles SET ' . $set . ' ';
|
||||
$sql .= 'WHERE rowid=' . $rowid . ';';
|
||||
}
|
||||
eLog($sql);
|
||||
qSQL($sql, "gen");
|
||||
}
|
||||
//! enfin, on supprime toutes les familles qui n'ont pas été reçues, active=0
|
||||
$sql = 'DELETE FROM produits_familles WHERE active=0;';
|
||||
eLog($sql);
|
||||
qSQL($sql, "gen");
|
||||
|
||||
$ret = array('ret' => "ok", 'msg' => "Enregistrement des familles effectué");
|
||||
echo json_encode($ret);
|
||||
break;
|
||||
|
||||
case "save_marche":
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->rowid)) {
|
||||
$cid = nettoie_input($data->rowid);
|
||||
$act = nettoie_input($data->act);
|
||||
|
||||
$set = 'SET libelle="' . nettoie_input(trim($data->libelle)) . '"';
|
||||
$set .= ', numero="' . nettoie_input($data->numero) . '"';
|
||||
$set .= ', nom="' . nettoie_input($data->nom) . '"';
|
||||
$set .= isset($data->chk_remise_sur_tg) ? ', chk_remise_sur_tg=1' : ', chk_remise_sur_tg=0';
|
||||
$set .= isset($data->chk_prix_nets) ? ', chk_prix_nets=1' : ', chk_prix_nets=0';
|
||||
$set .= isset($data->chk_marche_public) ? ', chk_marche_public=1' : ', chk_marche_public=0';
|
||||
|
||||
if ($data->taux_remise_trimestrielle == "") {
|
||||
$set .= ', taux_remise_trimestrielle=0';
|
||||
} else {
|
||||
$set .= ', taux_remise_trimestrielle=' . $data->taux_remise_trimestrielle;
|
||||
}
|
||||
if ($data->taux_remise_semestrielle == "") {
|
||||
$set .= ', taux_remise_semestrielle=0';
|
||||
} else {
|
||||
$set .= ', taux_remise_semestrielle=' . $data->taux_remise_semestrielle;
|
||||
}
|
||||
if ($data->taux_remise_annuelle == "") {
|
||||
$set .= ', taux_remise_annuelle=0';
|
||||
} else {
|
||||
$set .= ', taux_remise_annuelle=' . $data->taux_remise_annuelle;
|
||||
}
|
||||
$set .= ', date_debut="' . d6GetDate($data->date_debut, "FM") . '"';
|
||||
$set .= ', date_fin="' . d6GetDate($data->date_fin, "FM") . '"';
|
||||
$set .= ', date_validite_prix="' . d6GetDate($data->date_validite_prix, "FM") . '"';
|
||||
$set .= ', franco_de_port="' . nettoie_input(trim($data->franco_de_port)) . '"';
|
||||
$set .= ', garantie="' . nettoie_input(trim($data->garantie)) . '"';
|
||||
$set .= ', delai_de_livraison="' . nettoie_input(trim($data->delai_de_livraison)) . '"';
|
||||
$set .= ', remises_commerciales="' . nettoie_input(trim($data->remises_commerciales)) . '"';
|
||||
if ($data->remise_palier_1 == "") {
|
||||
$set .= ', remise_palier_1=0';
|
||||
} else {
|
||||
$set .= ', remise_palier_1=' . $data->remise_palier_1;
|
||||
}
|
||||
if ($data->remise_taux_1 == "") {
|
||||
$set .= ', remise_taux_1=0';
|
||||
} else {
|
||||
$set .= ', remise_taux_1=' . $data->remise_taux_1;
|
||||
}
|
||||
if ($data->remise_palier_2 == "") {
|
||||
$set .= ', remise_palier_2=0';
|
||||
} else {
|
||||
$set .= ', remise_palier_2=' . $data->remise_palier_2;
|
||||
}
|
||||
if ($data->remise_taux_2 == "") {
|
||||
$set .= ', remise_taux_2=0';
|
||||
} else {
|
||||
$set .= ', remise_taux_2=' . $data->remise_taux_2;
|
||||
}
|
||||
if ($data->remise_palier_3 == "") {
|
||||
$set .= ', remise_palier_3=0';
|
||||
} else {
|
||||
$set .= ', remise_palier_3=' . $data->remise_palier_3;
|
||||
}
|
||||
if ($data->remise_taux_3 == "") {
|
||||
$set .= ', remise_taux_3=0';
|
||||
} else {
|
||||
$set .= ', remise_taux_3=' . $data->remise_taux_3;
|
||||
}
|
||||
if ($data->remise_palier_4 == "") {
|
||||
$set .= ', remise_palier_4=0';
|
||||
} else {
|
||||
$set .= ', remise_palier_4=' . $data->remise_palier_4;
|
||||
}
|
||||
if ($data->remise_taux_4 == "") {
|
||||
$set .= ', remise_taux_4=0';
|
||||
} else {
|
||||
$set .= ', remise_taux_4=' . $data->remise_taux_4;
|
||||
}
|
||||
|
||||
$set .= ', commentaire="' . nettoie_input(trim($data->commentaire)) . '"';
|
||||
$set .= isset($data->chk_cache_commerciaux) ? ', chk_cache_commerciaux=1' : ', chk_cache_commerciaux=0';
|
||||
$set .= isset($data->chk_marche_hybride) ? ', chk_marche_hybride=1' : ', chk_marche_hybride=0';
|
||||
$set .= isset($data->chk_regle_seuils_marge) ? ', chk_regle_seuils_marge=1' : ', chk_regle_seuils_marge=0';
|
||||
$set .= isset($data->active) ? ', active=1' : ', active=0';
|
||||
|
||||
if ($cid == 0) {
|
||||
$set .= ', date_creat = "' . date("Y-m-d H:i:s") . '", fk_user_creat=' . $fk_user;
|
||||
$sql = 'INSERT INTO marches ' . $set . ';';
|
||||
} else {
|
||||
$set .= ', date_modif = "' . date("Y-m-d H:i:s") . '", fk_user_modif=' . $fk_user;
|
||||
$sql = 'UPDATE marches ' . $set . ' WHERE rowid=' . $cid . ';';
|
||||
}
|
||||
eLog($sql);
|
||||
qSQL($sql, "gen");
|
||||
|
||||
$ret = array('ret' => "ok");
|
||||
echo json_encode($ret);
|
||||
} else {
|
||||
$ret = array('ret' => "ko");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "delete_marche":
|
||||
//! Supprime un marché
|
||||
//! Réception de l'id du marché à supprimer
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
$sql = 'DELETE FROM marches m WHERE m.rowid=' . $cid . ';';
|
||||
qSQL($sql, "gen");
|
||||
eLog($sql);
|
||||
//! on supprime aussi la ligne dans la table marches_listes
|
||||
$sql = 'DELETE FROM marches_listes ml WHERE ml.fk_marche=' . $cid . ';';
|
||||
qSQL($sql, "gen");
|
||||
eLog($sql);
|
||||
//! on supprime aussi les lignes produits de ce marché dans la table produits
|
||||
$sql = 'DELETE FROM produits p WHERE p.fk_marche=' . $cid . ';';
|
||||
qSQL($sql, "gen");
|
||||
eLog($sql);
|
||||
|
||||
$ret = array('ret' => "ok", 'msg' => 'Marché supprimé');
|
||||
echo json_encode($ret);
|
||||
} else {
|
||||
$ret = array('ret' => "ko", 'msg' => 'Marché non supprimé');
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "save_marches_listes":
|
||||
//! Enregistre les mises à jour du tableau des listes tarifaires par marché
|
||||
//! Réception du tableau des mots clés par marché en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->idListe)) {
|
||||
if ($data->idListe == "1") {
|
||||
foreach ($data as $key => $val) {
|
||||
if (substr($key, 0, 7) == "motcle_") {
|
||||
$fk_marche = trim(substr($key, 7));
|
||||
$motCle = trim($val);
|
||||
$motCleAchat = "";
|
||||
$motCleVente = "";
|
||||
foreach ($data as $keyAV => $valAV) {
|
||||
if ($keyAV == "motcleachat_" . $fk_marche) {
|
||||
$motCleAchat = trim($valAV);
|
||||
} else {
|
||||
if ($keyAV == "motclevente_" . $fk_marche) {
|
||||
$motCleVente = trim($valAV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'SELECT l.rowid FROM marches_listes l WHERE l.fk_marche=' . $fk_marche . ';';
|
||||
$retour = getinfos($sql, "gen");
|
||||
if (count($retour) > 0) {
|
||||
$sql = 'UPDATE marches_listes SET mot_cle="' . $motCle . '", terme_achat="' . $motCleAchat . '", terme_vente="' . $motCleVente . '" WHERE fk_marche=' . $fk_marche . ';';
|
||||
qSQL($sql, "gen");
|
||||
} else {
|
||||
$sql = 'INSERT INTO marches_listes SET fk_marche=' . $fk_marche . ', mot_cle="' . $motCle . '", terme_achat="' . $motCleAchat . '", terme_vente="' . $motCleVente . '";';
|
||||
qSQL($sql, "gen");
|
||||
}
|
||||
}
|
||||
}
|
||||
$ret = array('ret' => "ok", "msg" => "Enregistrement des données effectué");
|
||||
echo json_encode($ret);
|
||||
} else {
|
||||
$ret = array('ret' => "ko", "msg" => "Erreur lors de la réception des données à enregistrer");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
} else {
|
||||
$ret = array('ret' => "ko", "msg" => "Erreur lors de la réception des données à enregistrer");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "load_produits_marche":
|
||||
//! Charge les produits d'un marché
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
|
||||
// On récupère certaines infos du marché pour savoir si on doit appliquer des filtres
|
||||
$sql = 'SELECT m.chk_remise_sur_tg FROM marches m WHERE m.rowid=' . $cid . ';';
|
||||
$retour = getinfos($sql, "gen");
|
||||
$chk_remise_sur_tg = $retour[0]["chk_remise_sur_tg"];
|
||||
|
||||
if ($cid == 999 || $chk_remise_sur_tg == 1) {
|
||||
// c'est directement le TG, on ne fait rien de spécial
|
||||
$sql = 'SELECT p.rowid, p.code, p.libelle, concat(p.code, " # ", p.libelle) as rech, p.groupe, p.prix_achat_net, p.prix_vente, p.prc_discount_1 ';
|
||||
$sql .= 'FROM produits p WHERE p.fk_marche=' . $cid . ' AND p.active=1 ORDER BY p.code DESC;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
} else {
|
||||
// On regarde le terme_achat du marché
|
||||
$sql = 'SELECT l.terme_achat FROM marches_listes l WHERE l.fk_marche=' . $cid . ';';
|
||||
$retour = getinfos($sql, "gen");
|
||||
$terme_achat = $retour[0]["terme_achat"];
|
||||
|
||||
if ($terme_achat == "Purchasing") {
|
||||
// On doit alors récupérer leur prix d'achat dans le marché TG
|
||||
$sql = 'SELECT p.rowid, p.code, p.libelle, concat(p.code, " # ", p.libelle) as rech, p.groupe, p.prix_achat_net, p.prix_vente, p.prc_discount_1 ';
|
||||
$sql .= 'FROM produits p WHERE p.fk_marche=' . $cid . ' AND p.active=1 ORDER BY p.code DESC;';
|
||||
$retour = getinfos($sql, "gen");
|
||||
if (count($retour) > 0) {
|
||||
foreach ($retour as &$prod) {
|
||||
$sql = 'SELECT p.prix_achat_net, p.prc_discount_1 FROM produits p WHERE p.fk_marche=999 AND p.code="' . $prod["code"] . '";';
|
||||
$retour2 = getinfos($sql, "gen");
|
||||
if (count($retour2) == 1) {
|
||||
$prod["prix_achat_net"] = $retour2[0]["prix_achat_net"];
|
||||
$prod["prc_discount_1"] = $retour2[0]["prc_discount_1"];
|
||||
}
|
||||
}
|
||||
}
|
||||
echo json_encode($retour);
|
||||
} else {
|
||||
$sql = 'SELECT p.rowid, p.code, p.libelle, concat(p.code, " # ", p.libelle) as rech, p.groupe, p.prix_achat_net, p.prix_vente, p.prc_discount_1 ';
|
||||
$sql .= 'FROM produits p WHERE p.fk_marche=' . $cid . ' AND p.active=1 ORDER BY p.code DESC;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
$ret = array("ret" => "ko", "msg" => "Erreur lors de l'envoi du marché à charger");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "load_marches_listes":
|
||||
//! Charge la liste des mots clés des marchés à retrouver dans les fichiers d'importation
|
||||
$sql = 'SELECT l.* FROM marches_listes l;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
break;
|
||||
|
||||
case "load_user":
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
$sql = 'SELECT u.* FROM users u WHERE u.rowid=' . $cid . ';';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
} else {
|
||||
$ret = array("ret" => "ko", "msg" => "Erreur lors de la récupération des infos de l'utilisateur");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "save_user":
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->rowid)) {
|
||||
$cid = nettoie_input($data->rowid);
|
||||
$act = nettoie_input($data->act);
|
||||
|
||||
$set = 'SET libelle="' . nettoie_input(trim($data->libelle)) . '"';
|
||||
$set .= ', prenom="' . nettoie_input(trim($data->prenom)) . '"';
|
||||
$set .= ', mobile="' . nettoie_input(trim($data->mobile)) . '"';
|
||||
$set .= ', email="' . nettoie_input(trim($data->email)) . '"';
|
||||
$set .= ', username="' . nettoie_input(trim($data->username)) . '"';
|
||||
$set .= ', fk_role=' . nettoie_input(trim($data->fk_role));
|
||||
$set .= ', fk_region=' . nettoie_input(trim($data->fk_region));
|
||||
if (isset($data->fk_parent)) {
|
||||
$set .= ', fk_parent=' . nettoie_input(trim($data->fk_parent));
|
||||
} else {
|
||||
$set .= ', fk_parent=0';
|
||||
}
|
||||
$set .= ', lst_depts="' . nettoie_input(trim($data->lst_depts)) . '"';
|
||||
$set .= isset($data->chk_grands_comptes) ? ', chk_grands_comptes=1' : ', chk_grands_comptes=0';
|
||||
$set .= isset($data->active) ? ', active=1' : ', active=0';
|
||||
|
||||
if ($cid == 0) {
|
||||
// on lui crée un mot de passe par défaut : initiale prénom en majuscule + initiale nom Maj + 3 caractères suivants du nom en minuscules + . + mois + année
|
||||
// On supprime les espaces dans le nom de l'utilisateur et on ne garde que les 3 premiers caractères
|
||||
$libUser = str_replace(" ", "", nettoie_input(trim($data->libelle)));
|
||||
$pwd = strtoupper(substr(nettoie_input(trim($data->prenom)), 0, 1)) . strtoupper(substr($libUser, 0, 1)) . strtolower(substr($libUser, 1, 3)) . "." . date("mY");
|
||||
eLog($pwd);
|
||||
$set .= ', userpswd="' . hashPsswd($pwd) . '"';
|
||||
|
||||
$set .= ', date_creat = "' . date("Y-m-d H:i:s") . '", fk_user_creat=' . $fk_user;
|
||||
$sql = 'INSERT INTO users ' . $set . ';';
|
||||
} else {
|
||||
$set .= ', date_modif = "' . date("Y-m-d H:i:s") . '", fk_user_modif=' . $fk_user;
|
||||
$sql = 'UPDATE users ' . $set . ' WHERE rowid=' . $cid . ';';
|
||||
}
|
||||
eLog($sql);
|
||||
qSQL($sql, "gen");
|
||||
|
||||
$ret = array('ret' => "ok");
|
||||
echo json_encode($ret);
|
||||
} else {
|
||||
$ret = array('ret' => "ko");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "delete_user":
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
// TODO : Supprimer les devis créés par cet utilisateur
|
||||
|
||||
$sql = 'DELETE FROM users WHERE rowid=' . $cid . ';';
|
||||
eLog($sql);
|
||||
qSQL($sql, "gen");
|
||||
$ret = array('ret' => "ok");
|
||||
echo json_encode($ret);
|
||||
} else {
|
||||
$ret = array('ret' => "ko");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "maintenance":
|
||||
if ($Conf->_devIp) {
|
||||
// Mise à jour des mots de passe Utilisateurs
|
||||
$sql = 'SELECT u.rowid, u.prenom, u.libelle FROM users u WHERE u.active=1 AND u.rowid=39;';
|
||||
$users = getinfos($sql, "gen");
|
||||
foreach ($users as $user) {
|
||||
$uId = $user["rowid"];
|
||||
$libUser = str_replace(" ", "", nettoie_input(trim($user["libelle"])));
|
||||
$pwd = strtoupper(substr(nettoie_input(trim($user["prenom"])), 0, 1)) . strtoupper(substr($libUser, 0, 1)) . strtolower(substr($libUser, 1, 3)) . "." . date("mY");
|
||||
eLog($pwd);
|
||||
$sql = 'UPDATE users SET userpswd="' . hashPsswd($pwd) . '" WHERE rowid=' . $uId . ';';
|
||||
eLog($sql);
|
||||
qSQL($sql, "gen");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "load_roles":
|
||||
$sql = 'SELECT xro.rowid, xro.libelle FROM x_roles xro WHERE xro.active=1 ORDER BY xro.rowid;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
break;
|
||||
|
||||
case "load_regions":
|
||||
$sql = 'SELECT xre.rowid, xre.libelle FROM x_regions xre WHERE xre.active=1 ORDER BY xre.rowid;';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
break;
|
||||
|
||||
case "export_sap_devis":
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
eLog("export_sap_devis: ");
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
$sql = 'SELECT d.* FROM devis d WHERE d.rowid=' . $cid . ';';
|
||||
$dev = getinfos($sql, "gen");
|
||||
$devis = $dev[0];
|
||||
eLog("export_sap_devis: " . $cid . " & " . $devis["fk_client"]);
|
||||
|
||||
$filename = "devis_" . $cid . "_" . date('Y_m_d_hi') . ".csv";
|
||||
$fields = array("Code", "Etablissement", "Adresse 1", "Adresse 2");
|
||||
|
||||
$sql = 'SELECT c.code, c.libelle, c.adresse1, c.adresse2 FROM clients c WHERE c.rowid=' . $devis["fk_client"] . ';';
|
||||
eLog($sql);
|
||||
$cli = getinfos($sql, "gen");
|
||||
$client = $cli[0];
|
||||
|
||||
$excelData = implode("\t", array_values($fields)) . "\n";
|
||||
|
||||
eLog($excelData);
|
||||
|
||||
array_walk($client, 'filterData');
|
||||
$excelData .= implode("\t", array_values($client)) . "\n";
|
||||
|
||||
$sql = 'SELECT p.code, p.libelle, p.prix_achat_net, p.prix_vente, dp.qte, dp.remise, dp.totalht FROM devis_produits dp LEFT JOIN produits p ON dp.fk_produit=p.rowid WHERE dp.fk_devis=' . $cid . ';';
|
||||
eLog($sql);
|
||||
$data = getinfos($sql, "gen");
|
||||
|
||||
// une ligne vierge de séparation
|
||||
$excelData .= "\n";
|
||||
$fields = array("Code", "Désignation", "Prix Achat", "Prix Vente", "Quantité", "Remise", "Total HT");
|
||||
$excelData .= implode("\t", array_values($fields)) . "\n";
|
||||
|
||||
foreach ($data as $row) {
|
||||
array_walk($row, 'filterData');
|
||||
$excelData .= implode("\t", $row) . "\n";
|
||||
}
|
||||
eLog($excelData);
|
||||
header("Content-Type: application/vnd.ms-excel; charset=UTF-16LE");
|
||||
header("Content-Disposition: attachment; filename=$filename");
|
||||
header("Content-Disposition: attachment; filename=\"$filename\"");
|
||||
echo $excelData;
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
|
||||
case "load_info":
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
$sql = 'SELECT i.* FROM infos i WHERE i.rowid=' . $cid . ';';
|
||||
echo getinfos($sql, "gen", "json");
|
||||
}
|
||||
break;
|
||||
|
||||
case "save_info":
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
|
||||
$set = 'SET date_infos="' . nettoie_input($data->cdate) . '" ';
|
||||
$set .= ', titre_infos="' . nettoie_input(trim($data->ctitre)) . '" ';
|
||||
$set .= ', text_infos="' . nettoie_input(trim($data->ctexte)) . '" ';
|
||||
if (isset($data->cpublie)) {
|
||||
if ($data->cpublie == false || $data->cpublie == 0) {
|
||||
$set .= ', chk_publie=0';
|
||||
} else {
|
||||
$set .= ', chk_publie=1';
|
||||
}
|
||||
} else {
|
||||
$set .= ', chk_publie=0';
|
||||
}
|
||||
|
||||
if ($cid == 0) {
|
||||
$set .= ', date_creat = "' . date("Y-m-d H:i:s") . '", fk_user_creat=' . $fk_user;
|
||||
$sql = 'INSERT INTO infos ' . $set . ';';
|
||||
} else {
|
||||
$set .= ', date_modif = "' . date("Y-m-d H:i:s") . '", fk_user_modif=' . $fk_user;
|
||||
$sql = 'UPDATE infos ' . $set . ' WHERE rowid=' . $cid . ';';
|
||||
}
|
||||
eLog($sql);
|
||||
qSQL($sql, "gen");
|
||||
$ret = array('ret' => "ok");
|
||||
echo json_encode($ret);
|
||||
} else {
|
||||
$ret = array('ret' => "ko");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
case "supp_info":
|
||||
//! Réception et lecture de la demande en json
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
if (isset($data->cid)) {
|
||||
$cid = nettoie_input($data->cid);
|
||||
$sql = 'DELETE FROM infos i WHERE i.rowid=' . $cid . ';';
|
||||
eLog($sql);
|
||||
qSQL($sql, "gen");
|
||||
|
||||
$ret = array('ret' => "ok");
|
||||
echo json_encode($ret);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
exit();
|
||||
41
controllers/clogin.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
session_regenerate_id();
|
||||
global $Route;
|
||||
|
||||
require_once $Route->_model;
|
||||
$msg = "";
|
||||
|
||||
if (isset($_POST['bcusername'])) {
|
||||
$a = nettoie_input($_POST['bcusername']);
|
||||
$r = nettoie_input($_POST['bcpassword']);
|
||||
|
||||
if ((strlen($a) != 0) && (strlen($r) != 0)) {
|
||||
$sql = 'SELECT * FROM users WHERE username="' . $a . '" AND active=1;';
|
||||
$res = getinfos($sql, "gen");
|
||||
if (count($res) == 1) {
|
||||
$userLine = $res[0];
|
||||
|
||||
if (checkPsswd($r, $userLine["userpswd"])) {
|
||||
openSession($userLine);
|
||||
eLog("Login Linet de " . $_SESSION['uname']);
|
||||
header("Location: /accueil");
|
||||
die();
|
||||
} else {
|
||||
echo "utilisateur non reconnu<br/>";
|
||||
$msg = "Identifiant et/ou mot de passe incorrect(s)";
|
||||
eLog("Login Admin KO 0 : " . $a . "/" . $r);
|
||||
}
|
||||
|
||||
} else {
|
||||
echo "utilisateur non reconnu<br/>";
|
||||
$msg = "Identifiant et/ou mot de passe incorrect(s)";
|
||||
eLog("Login Admin KO 1 : " . $a . "/" . $r);
|
||||
}
|
||||
|
||||
} else {
|
||||
eLog("Login Admin KO 2 : " . $a . "/" . $r);
|
||||
}
|
||||
}
|
||||
|
||||
$aView = $aModel;
|
||||
require_once $Route->_view;
|
||||
4
controllers/cmarches.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
$aModel=array();
|
||||
require_once $Route->_model;
|
||||
require_once $Route->_view;
|
||||
4
controllers/csap.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
$aModel=array();
|
||||
require_once $Route->_model;
|
||||
require_once $Route->_view;
|
||||
5
controllers/cusers.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
global $Route;
|
||||
$aModel = array();
|
||||
require_once $Route->_model;
|
||||
require_once $Route->_view;
|
||||
100
deploy-cleo-dev.sh
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script de déploiement de Cleo vers l'environnement de développement
|
||||
|
||||
cd /Users/pierre/dev/cleo
|
||||
|
||||
# Configuration du serveur hôte Debian 12
|
||||
HOST_SSH_HOST=195.154.80.116 # Adresse IP du serveur hôte
|
||||
HOST_SSH_USER=root # Utilisateur SSH sur le serveur hôte
|
||||
HOST_SSH_PORT=22 # Port SSH du serveur hôte
|
||||
HOST_SSH_KEY=/Users/pierre/.ssh/id_rsa_mbpi # Clé SSH privée pour accéder au serveur hôte
|
||||
|
||||
# Configuration du conteneur Incus hébergeant cette application
|
||||
CT_PROJECT_NAME=default # Nom du projet Incus où se trouve le conteneur
|
||||
CT_NAME=dva-front # Nom du conteneur Incus
|
||||
CT_IP=13.23.33.42 # IP interne du conteneur Incus
|
||||
CT_SSH_USER=root # Utilisateur SSH dans le conteneur
|
||||
CT_SSH_PORT=22 # Port SSH interne du conteneur
|
||||
CT_SSH_KEY=/root/.ssh/id_rsa_in3_pierre # Clé SSH privée pour accéder au conteneur
|
||||
|
||||
# Configuration de l'application
|
||||
DOMAIN_NAME=dcleo.unikoffice.com # Nom de domaine du site
|
||||
SERVER_PORT=3000 # Port du serveur Node.js
|
||||
ADMIN_PORT=3001 # Port du serveur d'administration
|
||||
DEPLOY_DIR=/var/www # Répertoire de déploiement sur le conteneur
|
||||
APP_NAME=cleo # Nom de l'application et du fichier de config nginx
|
||||
|
||||
# Propriétaire et groupe pour les fichiers et dossiers de destination
|
||||
OWNER=nginx
|
||||
GROUP=nginx
|
||||
|
||||
# Vérifier que les variables nécessaires sont définies
|
||||
if [ -z "$HOST_SSH_HOST" ] || [ -z "$HOST_SSH_USER" ] || [ -z "$CT_NAME" ] || [ -z "$CT_PROJECT_NAME" ]; then
|
||||
echo "Erreur: Variables HOST_SSH_HOST, HOST_SSH_USER, CT_NAME et CT_PROJECT_NAME requises dans $ENV_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Variables pour les alertes (optionnelles)
|
||||
ALERT_EMAIL=${ALERT_EMAIL:-""}
|
||||
DISCORD_WEBHOOK_URL=${DISCORD_WEBHOOK_URL:-""}
|
||||
|
||||
# Utiliser les valeurs par défaut si non définies
|
||||
HOST_SSH_PORT=${HOST_SSH_PORT:-22}
|
||||
SERVER_PORT=${SERVER_PORT:-3000}
|
||||
ADMIN_PORT=${ADMIN_PORT:-3001}
|
||||
DOMAIN_NAME=${DOMAIN_NAME:-$CT_IP}
|
||||
DEPLOY_DIR=${DEPLOY_DIR:-/var/www}
|
||||
APP_NAME=${APP_NAME:-d6soft}
|
||||
SUB_DIR=${SUB_DIR:-web}
|
||||
|
||||
# Afficher les paramètres
|
||||
echo "=== Paramètres de déploiement ==="
|
||||
echo "Serveur hôte: $HOST_SSH_USER@$HOST_SSH_HOST:$HOST_SSH_PORT"
|
||||
echo "Projet Incus: $CT_PROJECT_NAME"
|
||||
echo "Conteneur: $CT_NAME"
|
||||
echo "Domaine: $DOMAIN_NAME"
|
||||
echo "Répertoire de déploiement: $DEPLOY_DIR/$APP_NAME/$SUB_DIR"
|
||||
echo "Propriétaire: $OWNER"
|
||||
echo "Groupe: $GROUP"
|
||||
echo "=================================="
|
||||
|
||||
# Définir les options SSH
|
||||
SSH_OPTS="-p $HOST_SSH_PORT"
|
||||
SCP_OPTS="-P $HOST_SSH_PORT"
|
||||
if [ ! -z "$HOST_SSH_KEY" ]; then
|
||||
SSH_OPTS="$SSH_OPTS -i \"$HOST_SSH_KEY\""
|
||||
SCP_OPTS="$SCP_OPTS -i \"$HOST_SSH_KEY\""
|
||||
fi
|
||||
|
||||
# 1. Copier les fichiers vers le HOST incus
|
||||
echo "=== Copie des fichiers vers le HOST Incus ==="
|
||||
rsync -avz --progress --exclude='.log' --exclude='pub/files/upload' -e "ssh $SSH_OPTS" ./ $HOST_SSH_USER@$HOST_SSH_HOST:/tmp/$APP_NAME/
|
||||
|
||||
# 2. Créer le répertoire de destination dans le conteneur
|
||||
echo "=== Création du répertoire de destination dans le conteneur ==="
|
||||
eval "ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST \"incus project switch $CT_PROJECT_NAME\""
|
||||
eval "ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST \"incus exec $CT_NAME -- mkdir -p $DEPLOY_DIR/$APP_NAME\""
|
||||
|
||||
# 3. Transférer les fichiers vers le conteneur Incus
|
||||
echo "=== Transfert des fichiers vers le conteneur Incus ==="
|
||||
eval "ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST \"incus file push --recursive /tmp/$APP_NAME/. $CT_NAME/$DEPLOY_DIR/\""
|
||||
|
||||
# 4. Configurer les permissions dans le conteneur
|
||||
echo "=== Configuration des permissions dans le conteneur ==="
|
||||
eval "ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST \"incus exec $CT_NAME -- sh -c 'chown -R $OWNER:$GROUP $DEPLOY_DIR/$APP_NAME && \
|
||||
find $DEPLOY_DIR/$APP_NAME -type d -exec chmod 755 {} \\; && \
|
||||
find $DEPLOY_DIR/$APP_NAME -type f -exec chmod 644 {} \\; && \
|
||||
if [ -d $DEPLOY_DIR/$APP_NAME/server/logs ]; then chmod 775 $DEPLOY_DIR/$APP_NAME/server/logs; fi && \
|
||||
if [ -d $DEPLOY_DIR/$APP_NAME/mda/backend/logs ]; then chmod 775 $DEPLOY_DIR/$APP_NAME/mda/backend/logs; fi && \
|
||||
if [ -d $DEPLOY_DIR/$APP_NAME/mda/db ]; then chmod 775 $DEPLOY_DIR/$APP_NAME/mda/db; fi'\""
|
||||
|
||||
# 5. Nettoyer les fichiers temporaires sur l'hôte
|
||||
echo "=== Nettoyage des fichiers temporaires sur l'hôte ==="
|
||||
eval "ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST \"rm -rf /tmp/$APP_NAME\""
|
||||
|
||||
echo "==================================================="
|
||||
echo "Déploiement terminé avec succès !"
|
||||
echo "==================================================="
|
||||
echo "Votre site $APP_NAME est maintenant déployé dans le conteneur $CT_NAME."
|
||||
echo "Chemin de déploiement: $DEPLOY_DIR/$APP_NAME/$SUB_DIR"
|
||||
BIN
favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
favicon.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
12
index.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
require_once 'config/init.php';
|
||||
require_once FMKROOT . DS . 'router.php';
|
||||
$Route = new Router($_GET, $Conf);
|
||||
require_once FMKROOT . DS . 'session.php';
|
||||
$Session = new Session($Route, $Conf);
|
||||
|
||||
require_once $Route->_controller;
|
||||
5
models/maccueil.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
$aModel = array(
|
||||
"infos" => getinfos('SELECT i.* FROM infos i WHERE i.chk_publie=1 ORDER BY i.date_infos DESC;', "gen"),
|
||||
);
|
||||
16
models/mclients.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
$sch = "";
|
||||
if ($_POST) {
|
||||
if (isset($_POST["schClients"])) {
|
||||
$search = nettoie_input(trim($_POST["schClients"]));
|
||||
$sch = 'c.libelle LIKE "%' . $search . '%" OR c.adresse1 LIKE "%' . $search . '%" OR c.cp LIKE "%' . $search . '%" OR c.ville LIKE "%' . $search . '%" OR c.contact_nom LIKE "%' . $search . '%" OR c.contact_prenom LIKE "%' . $search . '%" OR c.contact_fonction LIKE "%' . $search . '%" ';
|
||||
}
|
||||
}
|
||||
|
||||
//! On récupère la liste des clients
|
||||
$sql = 'SELECT c.* FROM clients c ';
|
||||
if ($sch != "") {
|
||||
$sql .= 'WHERE ' . $sch;
|
||||
}
|
||||
$sql .= 'ORDER BY c.libelle';
|
||||
$aModel["clients"] = getinfos($sql, "gen");
|
||||
75
models/mdevis.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
global $Session;
|
||||
|
||||
$fkUser = $Session->_user["rowid"];
|
||||
$fkRole = $Session->_user["fk_role"];
|
||||
|
||||
switch ($fkRole) {
|
||||
case 1:
|
||||
// DIR-CO
|
||||
$where = 'd.fk_user=' . $fkUser . ' OR d.fk_statut_devis>=2';
|
||||
break;
|
||||
case 2:
|
||||
// DV : on récupère tous les RR de son périmètre
|
||||
$sql = 'SELECT rowid FROM users WHERE fk_parent =' . $fkUser . ';';
|
||||
$aRR = getinfos($sql, "gen");
|
||||
$lstRR = '';
|
||||
foreach ($aRR as $rr) {
|
||||
$lstRR .= $rr["rowid"] . ',';
|
||||
}
|
||||
$lstRR = substr($lstRR, 0, -1);
|
||||
$where = 'd.fk_user=' . $fkUser . ' OR (d.fk_statut_devis>=3 AND d.fk_user IN (' . $lstRR . '))';
|
||||
break;
|
||||
default:
|
||||
// RR
|
||||
$where = 'd.fk_user=' . $fkUser;
|
||||
break;
|
||||
}
|
||||
|
||||
$sql = 'SELECT d.rowid, d.dossier, d.date_demande, d.date_remise, d.num_opportunite, d.fk_client, d.montant_total_ht_remise, d.marge_totale, d.commentaire, d.chk_speciaux, c.libelle, c.ville, c.cp, d.fk_statut_devis, ';
|
||||
$sql .= 'd.lib_new_client, d.type_new_client, d.adresse1_new_client, d.adresse2_new_client, d.adresse3_new_client, d.cp_new_client, d.ville_new_client, d.comment_devis, d.comment_geste_comm, ';
|
||||
$sql .= 'd.contact_new_nom, d.contact_new_prenom, d.new_telephone, d.new_mobile, d.new_email, d.contact_new_fonction, LEFT(u.prenom,1) AS prenom, u.libelle as nom, ';
|
||||
$sql .= 'xs.libelle as lib_statut, d.chk_new_statut, m.libelle as lib_marche, d.chk_validat, d.fk_user_validat, d.date_validat ';
|
||||
$sql .= 'FROM devis d LEFT JOIN clients c ON c.rowid=d.fk_client LEFT JOIN x_statuts_devis xs ON xs.rowid=d.fk_statut_devis LEFT JOIN marches m ON m.rowid=d.fk_marche LEFT JOIN users u ON u.rowid=d.fk_user ';
|
||||
$sql .= 'WHERE ' . $where . ' ORDER BY d.dossier, d.date_remise DESC;';
|
||||
$aModel["devis"] = getinfos($sql, "gen");
|
||||
|
||||
//! on compte le nombre de devis par statut
|
||||
$aModel["nb_devis"] = array();
|
||||
foreach ($aModel["devis"] as $devis) {
|
||||
if (!isset($aModel["nb_devis"][$devis["fk_statut_devis"]])) {
|
||||
$aModel["nb_devis"][$devis["fk_statut_devis"]] = 1;
|
||||
} else {
|
||||
$aModel["nb_devis"][$devis["fk_statut_devis"]]++;
|
||||
}
|
||||
}
|
||||
|
||||
//! On récupère la liste des dossiers des devis
|
||||
$sql = 'SELECT DISTINCT d.dossier FROM devis d WHERE ' . $where . ' ORDER BY d.dossier;';
|
||||
$aModel["dossiers"] = getinfos($sql, "gen");
|
||||
|
||||
//! Tous les produits du catalogue
|
||||
$sql = 'SELECT rowid, code, libelle, prix_vente, prix_achat_net FROM produits WHERE active=1;';
|
||||
$aModel["produits"] = getinfos($sql, "gen");
|
||||
|
||||
//! toutes les familles de produits
|
||||
$sql = 'SELECT rowid, libelle, ordre FROM x_familles WHERE active=1 ORDER BY ordre;';
|
||||
$aModel["familles"] = getinfos($sql, "gen");
|
||||
|
||||
$sql = 'SELECT m.* FROM marches m WHERE m.active=1 AND m.chk_cache_commerciaux=0 ORDER BY m.libelle;';
|
||||
$aModel["marches"] = getinfos($sql, "gen");
|
||||
|
||||
//! les types de clients
|
||||
$sql = 'SELECT rowid, code, libelle FROM x_clients_types WHERE active=1 ORDER BY code;';
|
||||
$aModel["types_clients"] = getinfos($sql, "gen");
|
||||
|
||||
//! les statuts de devis sans le 20 - Archivés
|
||||
$sql = 'SELECT rowid, libelle FROM x_statuts_devis WHERE active=1 AND rowid<20 ORDER BY rowid;';
|
||||
$aModel["statuts_devis"] = getinfos($sql, "gen");
|
||||
|
||||
// On récupère le dernier numéro de devis mis à jour de la session
|
||||
if (isset($_SESSION["lastDevis"])) {
|
||||
$aModel["last_devis"] = $_SESSION["lastDevis"];
|
||||
} else {
|
||||
$aModel["last_devis"] = 0;
|
||||
}
|
||||
217
models/mexpxls.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
global $Route;
|
||||
|
||||
function cleanData(&$str)
|
||||
{
|
||||
// Fonction de nettoyage des données pour l'export Excel
|
||||
if ($str == 't') $str = 'TRUE';
|
||||
if ($str == 'f') $str = 'FALSE';
|
||||
if (preg_match("/^0/", $str) || preg_match("/^\+?\d{8,}$/", $str) || preg_match("/^\d{4}.\d{1,2}.\d{1,2}/", $str)) {
|
||||
$str = "'$str";
|
||||
}
|
||||
if (strstr($str, '"')) $str = '"' . str_replace('"', '""', $str) . '"';
|
||||
$str = mb_convert_encoding($str, 'UTF-16LE', 'UTF-8');
|
||||
}
|
||||
|
||||
function filterData(&$str)
|
||||
{
|
||||
$str = preg_replace("/\t/", "\\t", $str);
|
||||
$str = preg_replace("/\r?\n/", "\\n", $str);
|
||||
if ($str == 't') $str = 'TRUE';
|
||||
if ($str == 'f') $str = 'FALSE';
|
||||
if (preg_match("/^0/", $str) || preg_match("/^\+?\d{8,}$/", $str) || preg_match("/^\d{4}.\d{1,2}.\d{1,2}/", $str)) {
|
||||
$str = "'$str";
|
||||
}
|
||||
if (strstr($str, '"')) $str = '"' . str_replace('"', '""', $str) . '"';
|
||||
$str = mb_convert_encoding($str, 'UTF-16LE', 'UTF-8');
|
||||
// $str = iconv('UTF-8', 'UTF-8', $str);
|
||||
}
|
||||
|
||||
|
||||
switch ($Route->_action) {
|
||||
case "export_tpg":
|
||||
$sql = 'SELECT p.code, p.libelle, p.groupe, IF(p.prix_vente=0, "", FORMAT(p.prix_vente, 2, "fr_FR")) AS vente FROM produits p WHERE p.fk_marche=999 AND p.active=1 ORDER BY p.code;';
|
||||
$prods = getinfos($sql, "gen");
|
||||
|
||||
$fileName = "TPG_" . date('Y_m_d_hi') . ".xls";
|
||||
|
||||
$excelData = "TPG AU " . date('d/m/Y') . "\n";
|
||||
|
||||
$cols = array("code", "Libelle", "Groupe", "Prix Vente");
|
||||
$excelData .= implode("\t", array_values($cols)) . "\n";
|
||||
|
||||
foreach ($prods as $prod) {
|
||||
array_walk($prod, 'filterData');
|
||||
$excelData .= implode("\t", $prod) . "\n";
|
||||
}
|
||||
|
||||
// une ligne vierge de séparation
|
||||
$excelData .= "\n";
|
||||
header('Content-Type: application/vnd.ms-excel; charset=utf-16le');
|
||||
header("Content-type: application/x-msexcel; charset=utf-16le");
|
||||
header('Content-Disposition: attachment; filename="' . $fileName . '"');
|
||||
header('Cache-Control: max-age=0');
|
||||
echo $excelData;
|
||||
exit();
|
||||
break;
|
||||
|
||||
case "export_sap_devis":
|
||||
$cid = nettoie_input($Route->_param1);
|
||||
|
||||
$sql = 'SELECT d.* FROM devis d WHERE d.rowid=' . $cid . ';';
|
||||
eLog("Export Excel SAP Devis : " . $sql);
|
||||
$dev = getinfos($sql, "gen");
|
||||
$devis = $dev[0];
|
||||
|
||||
$fileName = "devis_" . $cid . "_" . date('Y_m_d_hi') . ".xls";
|
||||
|
||||
// On affiche les données client
|
||||
$fields = array("Code", "Etablissement", "Adresse 1", "Adresse 2", "Adresse 3", "Code Postal", "Ville");
|
||||
$excelData = implode("\t", array_values($fields)) . "\n";
|
||||
|
||||
$sql = 'SELECT c.code, c.libelle, c.adresse1, c.adresse2, c.adresse3, c.cp, c.ville FROM clients c WHERE c.rowid=' . $devis["fk_client"] . ';';
|
||||
$cli = getinfos($sql, "gen");
|
||||
if (count($cli) == 0) {
|
||||
// c'est un nouveau client, on affiche les données client enregistrées dans le devis
|
||||
$sql = 'SELECT "0" AS code, d.lib_new_client, d.adresse1_new_client, d.adresse2_new_client, d.adresse3_new_client, d.cp_new_client, d.ville_new_client FROM devis d WHERE d.rowid=' . $cid . ';';
|
||||
$cli = getinfos($sql, "gen");
|
||||
$client = $cli[0];
|
||||
|
||||
array_walk($client, 'filterData');
|
||||
$excelData .= implode("\t", array_values($client)) . "\n";
|
||||
|
||||
// une ligne vierge de séparation
|
||||
$excelData .= "\n";
|
||||
|
||||
// Les données du contact à prendre aussi dans le devis
|
||||
$sql = 'SELECT d.contact_new_nom, d.contact_new_prenom, d.contact_new_fonction, d.new_telephone, d.new_mobile, d.new_email FROM devis d WHERE d.rowid=' . $cid . ';';
|
||||
$cont = getinfos($sql, "gen");
|
||||
$contact = $cont[0];
|
||||
$fields = array("Contact Nom", "Prenom", "Fonction", "Fixe", "Mobile", "Email");
|
||||
$excelData .= implode("\t", array_values($fields)) . "\n";
|
||||
|
||||
array_walk($contact, 'filterData');
|
||||
$excelData .= implode("\t", array_values($contact)) . "\n";
|
||||
|
||||
} else {
|
||||
$client = $cli[0];
|
||||
|
||||
array_walk($client, 'filterData');
|
||||
$excelData .= implode("\t", array_values($client)) . "\n";
|
||||
|
||||
// une ligne vierge de séparation
|
||||
$excelData .= "\n";
|
||||
|
||||
// Les données du contact
|
||||
$sql = 'SELECT c.contact_nom, c.contact_prenom, c.contact_fonction, c.telephone, c.mobile, c.email FROM clients c WHERE c.rowid=' . $devis["fk_client"] . ';';
|
||||
$cont = getinfos($sql, "gen");
|
||||
$contact = $cont[0];
|
||||
$fields = array("Contact Nom", "Prenom", "Fonction", "Fixe", "Mobile", "Email");
|
||||
$excelData .= implode("\t", array_values($fields)) . "\n";
|
||||
|
||||
array_walk($contact, 'filterData');
|
||||
$excelData .= implode("\t", array_values($contact)) . "\n";
|
||||
}
|
||||
// une ligne vierge de séparation
|
||||
$excelData .= "\n";
|
||||
|
||||
// On affiche les données devis
|
||||
$sql = 'SELECT d.rowid, d.num_opportunite, IF(d.date_demande IS NULL OR d.date_demande="0000-00-00", "", DATE_FORMAT(d.date_demande, "%d/%m/%Y")) AS datedem, ';
|
||||
$sql .= 'IF(d.date_remise IS NULL OR d.date_remise="0000-00-00", "", DATE_FORMAT(d.date_remise, "%d/%m/%Y")) AS daterem, m.libelle AS lib_marche, m.numero AS num_marche, m.nom AS nom_marche, ';
|
||||
$sql .= 'IF(d.chk_devis_photos=1, "Oui", "Non") AS photos, d.commentaire, IF(d.chk_speciaux=1, "Oui", "Non") AS speciaux ';
|
||||
$sql .= 'FROM devis d LEFT JOIN marches m ON d.fk_marche=m.rowid WHERE d.rowid=' . $cid . ';';
|
||||
$dev = getinfos($sql, "gen");
|
||||
$devis = $dev[0];
|
||||
$chkSpeciaux = $devis["speciaux"];
|
||||
$fields = array("Devis", "Opportunite", "Date Demande", "Date remise client", "Marche", "Num Marche", "Nom Marche", "Avec photos", "Commentaire RR", "Speciaux");
|
||||
$excelData .= implode("\t", array_values($fields)) . "\n";
|
||||
|
||||
array_walk($devis, 'filterData');
|
||||
$excelData .= implode("\t", array_values($devis)) . "\n";
|
||||
|
||||
// une ligne vierge de séparation
|
||||
$excelData .= "\n";
|
||||
|
||||
// on affiche les totaux du devis
|
||||
$sql = 'SELECT d.montant_total_ht, d.montant_total_ht_remise, d.marge_totale FROM devis d WHERE d.rowid=' . $cid . ';';
|
||||
$dev = getinfos($sql, "gen");
|
||||
$totaux = $dev[0];
|
||||
$fields = array("Total HT", "Total HT Remise", "Marge Totale");
|
||||
$excelData .= implode("\t", array_values($fields)) . "\n";
|
||||
|
||||
array_walk($totaux, 'filterData');
|
||||
$excelData .= implode("\t", array_values($totaux)) . "\n";
|
||||
|
||||
// une ligne vierge de séparation
|
||||
$excelData .= "\n";
|
||||
|
||||
// on affiche les produits
|
||||
$sql = 'SELECT p.code, p.libelle, IF(p.prix_vente=0, "", FORMAT(p.prix_vente, 2, "fr_FR")) AS vente, ';
|
||||
$sql .= 'IF(dp.qte=0, "", dp.qte) AS qute, IF(dp.remise=0, "", FORMAT(dp.remise, 2, "fr_FR")) as remise, IF(dp.pu_vente_remise=0, "", FORMAT(dp.pu_vente_remise, 2, "fr_FR")) as puventeremise, ';
|
||||
$sql .= 'IF(dp.totalht=0, "", FORMAT(dp.totalht, 2, "fr_FR")) AS totht, IF(dp.marge=0, "", FORMAT(dp.marge, 2, "fr_FR")) AS marge, dp.commentaire ';
|
||||
$sql .= 'FROM devis_produits dp ';
|
||||
$sql .= 'LEFT JOIN produits p ON dp.fk_produit=p.rowid ';
|
||||
$sql .= 'LEFT JOIN produits_familles pf ON p.groupe=pf.groupe ';
|
||||
$sql .= 'LEFT JOIN x_familles xf ON pf.fk_famille=xf.rowid ';
|
||||
$sql .= 'WHERE dp.fk_devis=' . $cid . ' ORDER BY dp.ordre, xf.ordre, p.libelle;';
|
||||
$data = getinfos($sql, "gen");
|
||||
|
||||
$fields = array("Code", "Designation", "Prix Vente", "Quantite", "Remise", "PU vente avec remise", "Total HT", "Marge", "Commentaire");
|
||||
$excelData .= implode("\t", array_values($fields)) . "\n";
|
||||
|
||||
foreach ($data as $row) {
|
||||
array_walk($row, 'filterData');
|
||||
$excelData .= implode("\t", $row) . "\n";
|
||||
}
|
||||
|
||||
if ($chkSpeciaux == "Oui") {
|
||||
// une ligne vierge de séparation
|
||||
$excelData .= "\n";
|
||||
$excelData .= "----" . "\n";
|
||||
$excelData .= "PRODUITS SPECIAUX" . "\n";
|
||||
$excelData .= "----" . "\n";
|
||||
$sql = 'SELECT IF(ds.chk_livr_multi=1, "Oui", "Non") AS livr_multi, ds.nb_livr, DATE_FORMAT(ds.date_livr_1, "%d/%m/%Y") AS datelivr ';
|
||||
$sql .= 'FROM devis_speciaux ds WHERE ds.fk_devis=' . $cid . ';';
|
||||
$spec = getinfos($sql, "gen");
|
||||
$speciaux = $spec[0];
|
||||
|
||||
$fields = array("Livraisons multiples", "Nbre livraisons", "Date 1ere livraison");
|
||||
$excelData .= implode("\t", array_values($fields)) . "\n";
|
||||
|
||||
array_walk($speciaux, 'filterData');
|
||||
$excelData .= implode("\t", array_values($speciaux)) . "\n";
|
||||
|
||||
$excelData .= "\n";
|
||||
|
||||
$fields = array("#", "Code", "Designation", "Quantite", "Surcout", "Echantillon", "Date echantillon", "Concurrent", "Description");
|
||||
$excelData .= implode("\t", array_values($fields)) . "\n";
|
||||
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$sql = 'SELECT ds.fk_produit_' . $i . ', ds.code_produit_' . $i . ', ds.lib_produit_' . $i . ', ds.qte_' . $i . ', IF(ds.surcout_' . $i . '=0, "", FORMAT(ds.surcout_' . $i . ', 2, "fr_FR")), IF(ds.chk_echantillon_' . $i . '=1, "Oui", "Non") AS echantillon, ';
|
||||
$sql .= 'DATE_FORMAT(ds.date_echantillon_' . $i . ', "%d/%m/%Y") AS date_ech, ds.lib_concurrent_' . $i . ', ds.description_' . $i . ' ';
|
||||
$sql .= 'FROM devis_speciaux ds WHERE ds.fk_devis=' . $cid . ';';
|
||||
eLog($sql, "sql");
|
||||
$spec = getinfos($sql, "gen");
|
||||
$speciaux = $spec[0];
|
||||
|
||||
if ($speciaux["fk_produit_" . $i] > 0) {
|
||||
array_walk($speciaux, 'filterData');
|
||||
$excelData .= implode("\t", array_values($speciaux)) . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// une ligne vierge de séparation
|
||||
$excelData .= "\n";
|
||||
$excelData .= "----" . "\n";
|
||||
$excelData .= "FIN DU DEVIS" . "\n";
|
||||
$excelData .= "----" . "\n";
|
||||
$excelData .= "\n";
|
||||
header('Content-Type: application/vnd.ms-excel; charset=utf-16le');
|
||||
header("Content-type: application/x-msexcel; charset=utf-16le");
|
||||
header('Content-Disposition: attachment; filename="' . $fileName . '"');
|
||||
header('Cache-Control: max-age=0');
|
||||
echo $excelData;
|
||||
exit();
|
||||
|
||||
}
|
||||
3
models/minterface.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
$aModel["infos"] = getinfos('SELECT i.* FROM infos i ORDER BY i.date_infos DESC;');
|
||||
44
models/mlogin.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
$aModel = array();
|
||||
|
||||
function openSession($userdata)
|
||||
{
|
||||
$uid = $userdata['rowid'];
|
||||
$urole = $userdata['role'];
|
||||
|
||||
$sql = "DELETE FROM z_sessions WHERE fk_user=" . $uid . ";";
|
||||
qSQL($sql, "gen");
|
||||
|
||||
session_regenerate_id();
|
||||
|
||||
if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
|
||||
$uip = $_SERVER["HTTP_CLIENT_IP"];
|
||||
} elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
|
||||
$uip = $_SERVER["HTTP_X_FORWARDED_FOR"];
|
||||
} else {
|
||||
$uip = $_SERVER["REMOTE_ADDR"];
|
||||
}
|
||||
$utime = time();
|
||||
|
||||
//! en session on récupère son prénom et nom, son id, son ip et son rôle
|
||||
$_SESSION['uname'] = ucfirst($userdata["prenom"]) . " " . strtoupper($userdata["libelle"]);
|
||||
$_SESSION['uid'] = $uid;
|
||||
$_SESSION['urole'] = $urole;
|
||||
$_SESSION['umodified'] = $utime;
|
||||
$_SESSION['uip'] = $uip;
|
||||
session_write_close();
|
||||
|
||||
$sql = "INSERT INTO z_sessions (sid, fk_user, role, date_modified, ip, browser) VALUES ('" . session_id() . "', " . $uid . ", '" . $urole . "', '" . date("Y-m-d H:i:s") . "', '" . $uip . "', '" . $_SERVER['HTTP_USER_AGENT'] . "');";
|
||||
qSQL($sql, "gen");
|
||||
|
||||
// et on en profite pour purger les logs
|
||||
$sql = "DELETE FROM z_logs WHERE DATEDIFF(CURDATE(), z_logs.date)>5;";
|
||||
qSQL($sql);
|
||||
// ainsi que les sessions qui ont une date antérieure à 1 jour
|
||||
$sql = "DELETE FROM z_sessions WHERE DATEDIFF(CURDATE(), z_sessions.date_modified)>1;";
|
||||
qSQL($sql, "gen");
|
||||
|
||||
envoieMail("support@unikoffice.com", "Ouverture de session", "Ouverture de session pour " . $uid);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
11
models/mmarches.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
$sch = "";
|
||||
if ($_POST) {
|
||||
if (isset($_POST["chproduits"])) {
|
||||
$search = trim($_POST["chproduits"]);
|
||||
$sch = 'libelle LIKE "%' . $search . '%" OR libelle_court LIKE "%' . $search . '%"';
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'SELECT m.rowid, m.libelle, m.date_debut, m.date_fin, m.date_validite_prix, m.date_import, m.chk_remise_sur_tg, m.chk_marche_hybride, m.active FROM marches m ORDER BY m.libelle;';
|
||||
$aModel["lesmarches"] = getinfos($sql, "gen");
|
||||
42
models/msap.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
$aModel = array();
|
||||
|
||||
$sql = 'SELECT m.* FROM medias m WHERE m.support="devis_pdf_sap" ORDER BY m.support_rowid;';
|
||||
$aModel["medias"] = getinfos($sql, "gen");
|
||||
|
||||
$sql = 'SELECT d.*, c.libelle, c.adresse1, c.adresse2, c.adresse3, c.code, c.cp, c.ville, c.email, u.libelle as nom, u.prenom, s.libelle as lib_statut, m.libelle as lib_marche ';
|
||||
$sql .= 'FROM devis d ';
|
||||
$sql .= 'LEFT JOIN clients c on d.fk_client = c.rowid ';
|
||||
$sql .= 'LEFT JOIN users u ON d.fk_user = u.rowid ';
|
||||
$sql .= 'LEFT JOIN marches m ON d.fk_marche = m.rowid ';
|
||||
$sql .= 'LEFT JOIN x_statuts_devis s ON d.fk_statut_devis = s.rowid ';
|
||||
$sql .= 'WHERE (d.fk_statut_devis > 3 AND d.fk_statut_devis < 20) ORDER BY d.date_demande;';
|
||||
$aModel["devisEnCours"] = getinfos($sql, "gen");
|
||||
|
||||
//! on compte le nombre de devis par statut
|
||||
$aModel["nb_devis"] = array();
|
||||
foreach ($aModel["devisEnCours"] as $devis) {
|
||||
if (!isset($aModel["nb_devis"][$devis["fk_statut_devis"]])) {
|
||||
$aModel["nb_devis"][$devis["fk_statut_devis"]] = 1;
|
||||
} else {
|
||||
$aModel["nb_devis"][$devis["fk_statut_devis"]]++;
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'SELECT d.*, c.libelle, c.adresse1, c.adresse2, c.adresse3, c.code, c.cp, c.ville, u.libelle as nom, u.prenom, s.libelle as lib_statut, m.libelle as lib_marche ';
|
||||
$sql .= 'FROM devis d ';
|
||||
$sql .= 'LEFT JOIN clients c on d.fk_client = c.rowid ';
|
||||
$sql .= 'LEFT JOIN users u ON d.fk_user = u.rowid ';
|
||||
$sql .= 'LEFT JOIN marches m ON d.fk_marche = m.rowid ';
|
||||
$sql .= 'LEFT JOIN x_statuts_devis s ON d.fk_statut_devis = s.rowid ';
|
||||
$sql .= 'WHERE d.fk_statut_devis = 20 ORDER BY d.date_remise;';
|
||||
$aModel["devisArchives"] = getinfos($sql, "gen");
|
||||
|
||||
//! les statuts de devis sans le 20 - Archivés pour afficher les devis en cours à traiter au niveau de l'ADV
|
||||
$sql = 'SELECT rowid, libelle FROM x_statuts_devis WHERE active=1 AND rowid>3 AND rowid<20 ORDER BY rowid;';
|
||||
$aModel["statuts_devis"] = getinfos($sql, "gen");
|
||||
|
||||
//! On récupère la liste des dossiers des devis pour afficher les devis archivés par département
|
||||
$sql = 'SELECT DISTINCT d.dossier FROM devis d WHERE d.fk_statut_devis = 20 ORDER BY dossier;';
|
||||
$aModel["dossiers"] = getinfos($sql, "gen");
|
||||
12
models/musers.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
$sql = 'SELECT u.*, xrol.libelle AS lib_role, xreg.libelle AS lib_region FROM users u LEFT JOIN x_roles xrol on u.fk_role = xrol.rowid LEFT JOIN x_regions xreg on u.fk_region = xreg.rowid ORDER BY u.libelle, u.prenom;';
|
||||
$aModel["users"] = getinfos($sql, "gen");
|
||||
|
||||
$sql = 'SELECT rowid, libelle, abreviation FROM x_roles WHERE active=1 ORDER BY libelle;';
|
||||
$aModel["roles"] = getinfos($sql, "gen");
|
||||
|
||||
$sql = 'SELECT rowid, libelle FROM x_regions WHERE active=1 ORDER BY libelle;';
|
||||
$aModel["regions"] = getinfos($sql, "gen");
|
||||
|
||||
$sql = 'SELECT u.rowid, CONCAT(u.prenom, " ", u.libelle, " (", xrol.abreviation, ")") AS lib_parent FROM users u LEFT JOIN x_roles xrol on u.fk_role = xrol.rowid WHERE u.fk_role IN (1,2) ORDER BY lib_parent;';
|
||||
$aModel["parents"] = getinfos($sql, "gen");
|
||||
BIN
pub/.DS_Store
vendored
Normal file
BIN
pub/files/img/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
pub/files/img/loading_book.gif
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
pub/files/img/logos/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
pub/files/img/logos/linet.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
pub/files/img/logos/linet_logo_100.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
pub/files/img/logos/linet_logo_original.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
pub/files/img/logos/uof_linet_logo_250.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
pub/files/img/logos/uof_linet_logo_50.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
pub/files/upload/devis/202206968.pdf
Executable file
BIN
pub/files/upload/devis/212200278.pdf
Executable file
BIN
pub/files/upload/devis/26_21-24-sarace-2024_linet_annexe_1_a_bpu.pdf
Executable file
BIN
pub/files/upload/devis/292304361.pdf
Executable file
BIN
pub/files/upload/devis/292304730_bastide_20sentaccessxldemibarrieres_nov23.pdf
Executable file
BIN
pub/files/upload/devis/292304731_bastide_20sentaccessxlbarriere3-4_nov23.pdf
Executable file
BIN
pub/files/upload/devis/292500599.pdf
Normal file
BIN
pub/files/upload/devis/_21-24-sarace-2024_linet_offre_292402399_lot1_mco.pdf
Executable file
BIN
pub/files/upload/devis/_21-24-sarace-2024_linet_offre_292402400_lot2_ehpad.pdf
Executable file
BIN
pub/files/upload/devis/_linet_notre_offre_292404014_lot_1.pdf
Executable file
BIN
pub/files/upload/devis/_linet_notre_offre_uniha_292404017_lot_2.pdf
Executable file
BIN
pub/files/upload/devis/affiche_offre_speciale_parc_astérix_séjour_hotel.pdf
Executable file
BIN
pub/files/upload/devis/akd-736103511272.pdf
Executable file
BIN
pub/files/upload/devis/cv282305339_74_eam_st_jorioz_2rallonges_janv24.pdf
Executable file
BIN
pub/files/upload/devis/cv292302021_uniha_59_ch_valenciennes_5e2_juil23.pdf
Executable file
BIN
pub/files/upload/devis/cv292403566_uniha_45_chr_orleans_4tam_sept24.pdf
Executable file
BIN
pub/files/upload/devis/cv292405437_uniha_38_ch_bourgoin_jallieu_1mltx_dec24.pdf
Executable file
BIN
pub/files/upload/devis/cv292405438_uniha_38_ch_bourgoin_jallieu_1el5_dec24.pdf
Executable file
BIN
pub/files/upload/devis/d292403317_elsan_54_hop_prive_de_nancy_5el2_sept24.pdf
Executable file
BIN
pub/files/upload/devis/d292405060_uniha_60_ch_senlis_8tom2_dec24.pdf
Executable file
BIN
pub/files/upload/devis/de_resah_2020-011_29_ch_lamneur_18sca_nov23.pdf
Executable file
78
pub/files/upload/devis/dv1768_2024_09_23_13_14.xml
Executable file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cleodevis>
|
||||
<id>1768</id>
|
||||
<opportunite>xxxx</opportunite>
|
||||
<date_demande>2024-06-06</date_demande>
|
||||
<date_remise>2024-06-07</date_remise>
|
||||
<marche>HORS MARCHE (TG)</marche>
|
||||
<photos>0</photos>
|
||||
<commentaire_rr></commentaire_rr>
|
||||
<speciaux>0</speciaux>
|
||||
<total_devis_ht>97320.00</total_devis_ht>
|
||||
<total_devis_ht_remise>54499.20</total_devis_ht_remise>
|
||||
<marge_totale>24.10</marge_totale>
|
||||
<client>
|
||||
<code>570013</code>
|
||||
<etablissement>CLIN. AMBROISE PARE THIONVILLE</etablissement>
|
||||
<adresse1>21 ROUTE DE GUENTRANGE</adresse1>
|
||||
<adresse2></adresse2>
|
||||
<adresse3></adresse3>
|
||||
<codepostal>57100</codepostal>
|
||||
<ville>THIONVILLE</ville>
|
||||
<contact>
|
||||
<nom>BONDU</nom>
|
||||
<prenom>CHRISTINE</prenom>
|
||||
<fonction>DIRECTRICE</fonction>
|
||||
<fixe></fixe>
|
||||
<mobile>06 30 48 90 79</mobile>
|
||||
<email>c.bondu@a-pare.com</email>
|
||||
</contact>
|
||||
</client>
|
||||
<produits>
|
||||
<produit id="87996">
|
||||
<code>1EZ340-1</code>
|
||||
<designation>ESSENZA 340 DEMI-BARRIERES</designation>
|
||||
<prix_vente>3695.00</prix_vente>
|
||||
<quantite>24</quantite>
|
||||
<remise>44.00</remise>
|
||||
<pu_vente_avec_remise>2069.20</pu_vente_avec_remise>
|
||||
<total_ht>49660.80</total_ht>
|
||||
<marge>24.78</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="88176">
|
||||
<code>HC16_EZ</code>
|
||||
<designation>T?l?commande patient additionnelle avec plug and play + r?tro?lclairage du clavier</designation>
|
||||
<prix_vente>29.00</prix_vente>
|
||||
<quantite>24</quantite>
|
||||
<remise>44.00</remise>
|
||||
<pu_vente_avec_remise>16.24</pu_vente_avec_remise>
|
||||
<total_ht>389.76</total_ht>
|
||||
<marge>25.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="81057">
|
||||
<code>AU021</code>
|
||||
<designation>POTENCE D'ANGLE EPOXY AVEC POIGNEE REGLABLE ET ENROULEUR</designation>
|
||||
<prix_vente>161.00</prix_vente>
|
||||
<quantite>24</quantite>
|
||||
<remise>44.00</remise>
|
||||
<pu_vente_avec_remise>90.16</pu_vente_avec_remise>
|
||||
<total_ht>2163.84</total_ht>
|
||||
<marge>20.14</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="80242">
|
||||
<code>MA-215</code>
|
||||
<designation>MATELAS MAT HEB</designation>
|
||||
<prix_vente>170.00</prix_vente>
|
||||
<quantite>24</quantite>
|
||||
<remise>44.00</remise>
|
||||
<pu_vente_avec_remise>95.20</pu_vente_avec_remise>
|
||||
<total_ht>2284.80</total_ht>
|
||||
<marge>13.03</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
</produits>
|
||||
<produits_speciaux/>
|
||||
</cleodevis>
|
||||
144
pub/files/upload/devis/dv1770_2024_09_23_13_14.xml
Executable file
@@ -0,0 +1,144 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cleodevis>
|
||||
<id>1770</id>
|
||||
<opportunite>DISTRI2</opportunite>
|
||||
<date_demande>2024-06-06</date_demande>
|
||||
<date_remise>2024-06-07</date_remise>
|
||||
<marche>HORS MARCHE (TG)</marche>
|
||||
<photos>1</photos>
|
||||
<commentaire_rr></commentaire_rr>
|
||||
<speciaux>0</speciaux>
|
||||
<total_devis_ht>61215.00</total_devis_ht>
|
||||
<total_devis_ht_remise>26250.69</total_devis_ht_remise>
|
||||
<marge_totale>34.08</marge_totale>
|
||||
<client>
|
||||
<code>0</code>
|
||||
<etablissement>OXYPHARM</etablissement>
|
||||
<adresse1>272 RUE EDOUARD BRANLY</adresse1>
|
||||
<adresse2></adresse2>
|
||||
<adresse3></adresse3>
|
||||
<codepostal>14100</codepostal>
|
||||
<ville>LISIEUX</ville>
|
||||
<contact>
|
||||
<nom>DELAMARE</nom>
|
||||
<prenom>Laurent</prenom>
|
||||
<fonction>Responsable Commercial</fonction>
|
||||
<fixe>02 31 48 48 25</fixe>
|
||||
<mobile></mobile>
|
||||
<email>laurentdelamare@oxypharm.fr</email>
|
||||
</contact>
|
||||
</client>
|
||||
<produits>
|
||||
<produit id="79818">
|
||||
<code>10-1090A</code>
|
||||
<designation>LIT DE SOINS BAS SENTIDA SC ACCESSIBLE DF SFF0</designation>
|
||||
<prix_vente>3288.00</prix_vente>
|
||||
<quantite>15</quantite>
|
||||
<remise>49.80</remise>
|
||||
<pu_vente_avec_remise>1650.58</pu_vente_avec_remise>
|
||||
<total_ht>24758.64</total_ht>
|
||||
<marge>46.45</marge>
|
||||
<commentaire>LIT 1 - Sentida SC Accessible 90 (châssis dégagé, 4 demi-barrières, rallonge intégrée, butées murales)</commentaire>
|
||||
</produit>
|
||||
<produit id="90556">
|
||||
<code>OL-154_sC</code>
|
||||
<designation>Demi-barri?res SafeFree Fix4</designation>
|
||||
<prix_vente>608.00</prix_vente>
|
||||
<quantite>15</quantite>
|
||||
<remise>100.00</remise>
|
||||
<pu_vente_avec_remise>0.00</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="90587">
|
||||
<code>OL-178_sC</code>
|
||||
<designation>PROTECTION MURALE</designation>
|
||||
<prix_vente>76.00</prix_vente>
|
||||
<quantite>15</quantite>
|
||||
<remise>48.00</remise>
|
||||
<pu_vente_avec_remise>39.52</pu_vente_avec_remise>
|
||||
<total_ht>592.80</total_ht>
|
||||
<marge>35.43</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="81055">
|
||||
<code>10-0127</code>
|
||||
<designation>POTENCE D'ANGLE EPOXY AVEC POIGNEE REGLABLE ET ENROULEUR</designation>
|
||||
<prix_vente>109.00</prix_vente>
|
||||
<quantite>15</quantite>
|
||||
<remise>45.00</remise>
|
||||
<pu_vente_avec_remise>59.95</pu_vente_avec_remise>
|
||||
<total_ht>899.25</total_ht>
|
||||
<marge>34.68</marge>
|
||||
<commentaire>Accessoire sur le lit</commentaire>
|
||||
</produit>
|
||||
<produit id="82366">
|
||||
<code>50-0694</code>
|
||||
<designation>TIGE A PERFUSION 4 CROCHETS</designation>
|
||||
<prix_vente>112.00</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>42.00</remise>
|
||||
<pu_vente_avec_remise>64.96</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire>Autres options et accessoires disponibles</commentaire>
|
||||
</produit>
|
||||
<produit id="90524">
|
||||
<code>OL-135_sC</code>
|
||||
<designation>JEU DE PANNEAUX TS</designation>
|
||||
<prix_vente>52.00</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>42.00</remise>
|
||||
<pu_vente_avec_remise>30.16</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="90577">
|
||||
<code>OL-173_sC</code>
|
||||
<designation>VEILLEUSE DE NUIT</designation>
|
||||
<prix_vente>76.00</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>42.00</remise>
|
||||
<pu_vente_avec_remise>44.08</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="86741">
|
||||
<code>10-0944</code>
|
||||
<designation>PROTECTION MURALE TELESCOPIQUE (prix unitaire)</designation>
|
||||
<prix_vente>69.00</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>43.00</remise>
|
||||
<pu_vente_avec_remise>39.33</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="79083">
|
||||
<code>AC-021</code>
|
||||
<designation>FLEXIBLE SUPPORT TELECOMMANDE 2021</designation>
|
||||
<prix_vente>61.00</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>42.00</remise>
|
||||
<pu_vente_avec_remise>35.38</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="86742">
|
||||
<code>10-0954</code>
|
||||
<designation>POIGNEE D'AIDE A LA MOBILISATION MOBISTICK2 SENTIDA SC</designation>
|
||||
<prix_vente>198.00</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>42.00</remise>
|
||||
<pu_vente_avec_remise>114.84</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
</produits>
|
||||
<produits_speciaux/>
|
||||
</cleodevis>
|
||||
67
pub/files/upload/devis/dv2450_2024_09_23_13_08.xml
Executable file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cleodevis>
|
||||
<id>2450</id>
|
||||
<opportunite>LFR84022</opportunite>
|
||||
<date_demande>2024-09-23</date_demande>
|
||||
<date_remise>2024-09-24</date_remise>
|
||||
<marche>RESAH 2020-011_Hébergement LOT5 (prix Centrale)</marche>
|
||||
<photos>0</photos>
|
||||
<commentaire_rr></commentaire_rr>
|
||||
<speciaux>0</speciaux>
|
||||
<total_devis_ht>6333.44</total_devis_ht>
|
||||
<total_devis_ht_remise>6333.44</total_devis_ht_remise>
|
||||
<marge_totale>32.89</marge_totale>
|
||||
<client>
|
||||
<code>930022</code>
|
||||
<etablissement>EHPAD ARTHUR GROUSSIER</etablissement>
|
||||
<adresse1>CASVP - CENTRE FACTURIER</adresse1>
|
||||
<adresse2></adresse2>
|
||||
<adresse3></adresse3>
|
||||
<codepostal>75589</codepostal>
|
||||
<ville>PARIS CEDEX12</ville>
|
||||
<contact>
|
||||
<nom>ARTOIS</nom>
|
||||
<prenom>Adeline</prenom>
|
||||
<fonction>ACHETEUSE</fonction>
|
||||
<fixe>01 48 50 52 82</fixe>
|
||||
<mobile></mobile>
|
||||
<email>Adeline.Artois.vdp@paris.fr</email>
|
||||
</contact>
|
||||
</client>
|
||||
<produits>
|
||||
<produit id="89718">
|
||||
<code>10-1090A-2-RE</code>
|
||||
<designation>LIT DE SOINS BAS SENTIDA SC ACCESSIBLE G09</designation>
|
||||
<prix_vente>1426.35</prix_vente>
|
||||
<quantite>4</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>1426.35</pu_vente_avec_remise>
|
||||
<total_ht>5705.40</total_ht>
|
||||
<marge>34.08</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="83656">
|
||||
<code>AC-022</code>
|
||||
<designation>FLEXIBLE SUPPORT TELECOMMANDE 2021</designation>
|
||||
<prix_vente>36.12</prix_vente>
|
||||
<quantite>4</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>36.12</pu_vente_avec_remise>
|
||||
<total_ht>144.48</total_ht>
|
||||
<marge>35.44</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="83667">
|
||||
<code>MA-213</code>
|
||||
<designation>MATELAS NOVAFORM</designation>
|
||||
<prix_vente>120.89</prix_vente>
|
||||
<quantite>4</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>120.89</pu_vente_avec_remise>
|
||||
<total_ht>483.56</total_ht>
|
||||
<marge>18.11</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
</produits>
|
||||
<produits_speciaux/>
|
||||
</cleodevis>
|
||||
67
pub/files/upload/devis/dv2450_2024_09_23_13_11.xml
Executable file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cleodevis>
|
||||
<id>2450</id>
|
||||
<opportunite>LFR84022</opportunite>
|
||||
<date_demande>2024-09-23</date_demande>
|
||||
<date_remise>2024-09-24</date_remise>
|
||||
<marche>RESAH 2020-011_Hébergement LOT5 (prix Centrale)</marche>
|
||||
<photos>0</photos>
|
||||
<commentaire_rr></commentaire_rr>
|
||||
<speciaux>0</speciaux>
|
||||
<total_devis_ht>6333.44</total_devis_ht>
|
||||
<total_devis_ht_remise>6333.44</total_devis_ht_remise>
|
||||
<marge_totale>32.89</marge_totale>
|
||||
<client>
|
||||
<code>930022</code>
|
||||
<etablissement>EHPAD ARTHUR GROUSSIER</etablissement>
|
||||
<adresse1>CASVP - CENTRE FACTURIER</adresse1>
|
||||
<adresse2></adresse2>
|
||||
<adresse3></adresse3>
|
||||
<codepostal>75589</codepostal>
|
||||
<ville>PARIS CEDEX12</ville>
|
||||
<contact>
|
||||
<nom>ARTOIS</nom>
|
||||
<prenom>Adeline</prenom>
|
||||
<fonction>ACHETEUSE</fonction>
|
||||
<fixe>01 48 50 52 82</fixe>
|
||||
<mobile></mobile>
|
||||
<email>Adeline.Artois.vdp@paris.fr</email>
|
||||
</contact>
|
||||
</client>
|
||||
<produits>
|
||||
<produit id="89718">
|
||||
<code>10-1090A-2-RE</code>
|
||||
<designation>LIT DE SOINS BAS SENTIDA SC ACCESSIBLE G09</designation>
|
||||
<prix_vente>1426.35</prix_vente>
|
||||
<quantite>4</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>1426.35</pu_vente_avec_remise>
|
||||
<total_ht>5705.40</total_ht>
|
||||
<marge>34.08</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="83656">
|
||||
<code>AC-022</code>
|
||||
<designation>FLEXIBLE SUPPORT TELECOMMANDE 2021</designation>
|
||||
<prix_vente>36.12</prix_vente>
|
||||
<quantite>4</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>36.12</pu_vente_avec_remise>
|
||||
<total_ht>144.48</total_ht>
|
||||
<marge>35.44</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="83667">
|
||||
<code>MA-213</code>
|
||||
<designation>MATELAS NOVAFORM</designation>
|
||||
<prix_vente>120.89</prix_vente>
|
||||
<quantite>4</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>120.89</pu_vente_avec_remise>
|
||||
<total_ht>483.56</total_ht>
|
||||
<marge>18.11</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
</produits>
|
||||
<produits_speciaux/>
|
||||
</cleodevis>
|
||||
69
pub/files/upload/devis/dv2577_2024_10_28_16_56.xml
Executable file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cleodevis>
|
||||
<id>2577</id>
|
||||
<opportunite>LFR00000</opportunite>
|
||||
<date_demande>2024-10-07</date_demande>
|
||||
<date_remise>2024-10-09</date_remise>
|
||||
<marche>HORS MARCHE (TG)</marche>
|
||||
<num_marche></num_marche>
|
||||
<nom_marche>HORS MARCHE</nom_marche>
|
||||
<photos>0</photos>
|
||||
<commentaire_rr></commentaire_rr>
|
||||
<speciaux>0</speciaux>
|
||||
<total_devis_ht>14556.00</total_devis_ht>
|
||||
<total_devis_ht_remise>9461.40</total_devis_ht_remise>
|
||||
<marge_totale>58.25</marge_totale>
|
||||
<client>
|
||||
<code>330099</code>
|
||||
<etablissement>EHPAD LOUIS BRAILLE VAYRES</etablissement>
|
||||
<adresse1>2 AVENUE IZON</adresse1>
|
||||
<adresse2></adresse2>
|
||||
<adresse3></adresse3>
|
||||
<codepostal>33870</codepostal>
|
||||
<ville>VAYRES</ville>
|
||||
<contact>
|
||||
<nom>Monsieur PELLISSIER</nom>
|
||||
<prenom>Daniel</prenom>
|
||||
<fonction>Service technique</fonction>
|
||||
<fixe>05 57 74 81 44</fixe>
|
||||
<mobile></mobile>
|
||||
<email>servicetechnique@ehpad-louisbraille.fr</email>
|
||||
</contact>
|
||||
</client>
|
||||
<produits>
|
||||
<produit id="79818">
|
||||
<code>10-1090A</code>
|
||||
<designation>LIT DE SOINS BAS SENTIDA SC ACCESSIBLE DF SFF0</designation>
|
||||
<prix_vente>3520.00</prix_vente>
|
||||
<quantite>4</quantite>
|
||||
<remise>35.00</remise>
|
||||
<pu_vente_avec_remise>2288.00</pu_vente_avec_remise>
|
||||
<total_ht>9152.00</total_ht>
|
||||
<marge>58.59</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="79707">
|
||||
<code>OL-135_sc</code>
|
||||
<designation>JEU DE PANNEAUX TS</designation>
|
||||
<prix_vente>119.00</prix_vente>
|
||||
<quantite>4</quantite>
|
||||
<remise>35.00</remise>
|
||||
<pu_vente_avec_remise>77.35</pu_vente_avec_remise>
|
||||
<total_ht>309.40</total_ht>
|
||||
<marge>48.35</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="87929">
|
||||
<code>OL-159_sc</code>
|
||||
<designation>Barri?res pleine longueur 310</designation>
|
||||
<prix_vente>0.00</prix_vente>
|
||||
<quantite>4</quantite>
|
||||
<remise>35.00</remise>
|
||||
<pu_vente_avec_remise>0.00</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
</produits>
|
||||
<produits_speciaux/>
|
||||
</cleodevis>
|
||||
124
pub/files/upload/devis/dv2590_2024_10_17_11_58.xml
Executable file
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cleodevis>
|
||||
<id>2590</id>
|
||||
<opportunite>LFR84359</opportunite>
|
||||
<date_demande>2024-10-08</date_demande>
|
||||
<date_remise>2024-10-10</date_remise>
|
||||
<marche>RESAH 2020-011_Hébergement LOT5 (prix Centrale)</marche>
|
||||
<num_marche>2020-011_LOT5</num_marche>
|
||||
<nom_marche>RESAH</nom_marche>
|
||||
<photos>0</photos>
|
||||
<commentaire_rr></commentaire_rr>
|
||||
<speciaux>0</speciaux>
|
||||
<total_devis_ht>102989.53</total_devis_ht>
|
||||
<total_devis_ht_remise>102989.53</total_devis_ht_remise>
|
||||
<marge_totale>28.30</marge_totale>
|
||||
<client>
|
||||
<code>0</code>
|
||||
<etablissement>EHPAD RES. ST VINCENT DE PAUL GRAU DU ROI</etablissement>
|
||||
<adresse1>16 RUE DE L'EGALITE</adresse1>
|
||||
<adresse2></adresse2>
|
||||
<adresse3></adresse3>
|
||||
<codepostal>30240</codepostal>
|
||||
<ville>LE GRAU DU ROI</ville>
|
||||
<contact>
|
||||
<nom>BIANCARDINI</nom>
|
||||
<prenom>V.</prenom>
|
||||
<fonction>DIRECTRICE</fonction>
|
||||
<fixe>04.66.73.67.00</fixe>
|
||||
<mobile></mobile>
|
||||
<email>v.biancardini@ville-legrauduroi.fr</email>
|
||||
</contact>
|
||||
</client>
|
||||
<produits>
|
||||
<produit id="89717">
|
||||
<code>10-1090A-1-RE</code>
|
||||
<designation>LIT DE SOINS BAS SENTIDA SC ACCESSIBLE Ts10</designation>
|
||||
<prix_vente>1611.73</prix_vente>
|
||||
<quantite>57</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>1611.73</pu_vente_avec_remise>
|
||||
<total_ht>91868.61</total_ht>
|
||||
<marge>29.76</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="89719">
|
||||
<code>10-1090A-3-RE</code>
|
||||
<designation>LIT DE SOINS BAS SENTIDA SC ACCESSIBLE Ts3/4</designation>
|
||||
<prix_vente>1509.78</prix_vente>
|
||||
<quantite>1</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>1509.78</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>28.07</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="89784">
|
||||
<code>OL-173_sc</code>
|
||||
<designation>VEILLEUSE DE NUIT</designation>
|
||||
<prix_vente>46.51</prix_vente>
|
||||
<quantite>1</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>46.51</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>45.13</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="83591">
|
||||
<code>10-0944</code>
|
||||
<designation>PROTECTION MURALE TELESCOPIQUE (prix unitaire)</designation>
|
||||
<prix_vente>45.75</prix_vente>
|
||||
<quantite>57</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>45.75</pu_vente_avec_remise>
|
||||
<total_ht>2607.75</total_ht>
|
||||
<marge>46.14</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="83530">
|
||||
<code>10-0127</code>
|
||||
<designation>POTENCE D'ANGLE EPOXY AVEC POIGNEE REGLABLE ET ENROULEUR</designation>
|
||||
<prix_vente>46.44</prix_vente>
|
||||
<quantite>13</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>46.44</pu_vente_avec_remise>
|
||||
<total_ht>603.72</total_ht>
|
||||
<marge>15.68</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="83606">
|
||||
<code>50-0693</code>
|
||||
<designation>TIGE A PERFUSION 4 CROCHETS COUDEE 45?</designation>
|
||||
<prix_vente>107.62</prix_vente>
|
||||
<quantite>4</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>107.62</pu_vente_avec_remise>
|
||||
<total_ht>430.48</total_ht>
|
||||
<marge>45.21</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="83655">
|
||||
<code>AC-021</code>
|
||||
<designation>FLEXIBLE SUPPORT TELECOMMANDE 2021</designation>
|
||||
<prix_vente>33.17</prix_vente>
|
||||
<quantite>57</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>33.17</pu_vente_avec_remise>
|
||||
<total_ht>1890.69</total_ht>
|
||||
<marge>33.68</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="83668">
|
||||
<code>MA-214</code>
|
||||
<designation>MATELAS NOVAFORM TRONQUE</designation>
|
||||
<prix_vente>98.04</prix_vente>
|
||||
<quantite>57</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>98.04</pu_vente_avec_remise>
|
||||
<total_ht>5588.28</total_ht>
|
||||
<marge>-5.77</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
</produits>
|
||||
<produits_speciaux/>
|
||||
</cleodevis>
|
||||
190
pub/files/upload/devis/dv2716_2024_10_23_17_15.xml
Executable file
@@ -0,0 +1,190 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cleodevis>
|
||||
<id>2716</id>
|
||||
<opportunite>LFR55346</opportunite>
|
||||
<date_demande>2024-10-23</date_demande>
|
||||
<date_remise>2024-10-29</date_remise>
|
||||
<marche>UNIHA_hébergement</marche>
|
||||
<num_marche>219450_LOT1</num_marche>
|
||||
<nom_marche>UNIHA</nom_marche>
|
||||
<photos>0</photos>
|
||||
<commentaire_rr></commentaire_rr>
|
||||
<speciaux>0</speciaux>
|
||||
<total_devis_ht>26974.29</total_devis_ht>
|
||||
<total_devis_ht_remise>26974.29</total_devis_ht_remise>
|
||||
<marge_totale>38.67</marge_totale>
|
||||
<client>
|
||||
<code>620102</code>
|
||||
<etablissement>CH HESDIN</etablissement>
|
||||
<adresse1>3 RUE PREVOST</adresse1>
|
||||
<adresse2></adresse2>
|
||||
<adresse3></adresse3>
|
||||
<codepostal>62140</codepostal>
|
||||
<ville>HESDIN</ville>
|
||||
<contact>
|
||||
<nom>LION</nom>
|
||||
<prenom>Coline</prenom>
|
||||
<fonction>ACHATS</fonction>
|
||||
<fixe>03.21.86.71.80</fixe>
|
||||
<mobile></mobile>
|
||||
<email>c.lion@ch-hesdin.fr</email>
|
||||
</contact>
|
||||
</client>
|
||||
<produits>
|
||||
<produit id="89464">
|
||||
<code>10-1090A/2-UN</code>
|
||||
<designation>LIT DE SOINS BAS SENTIDA SC ACCESSIBLE Ts10</designation>
|
||||
<prix_vente>1553.36</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>1553.36</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="89551">
|
||||
<code>OL-171_sc</code>
|
||||
<designation>EXTENSION DU PLAN DE COUCHAGE PAR LOQUETS</designation>
|
||||
<prix_vente>55.58</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>55.58</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84420">
|
||||
<code>10-0127</code>
|
||||
<designation>POTENCE D'ANGLE EPOXY AVEC POIGNEE REGLABLE ET ENROULEUR</designation>
|
||||
<prix_vente>48.80</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>48.80</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84504">
|
||||
<code>50-0693</code>
|
||||
<designation>TIGE A PERFUSION 4 CROCHETS COUDEE 45?</designation>
|
||||
<prix_vente>116.05</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>116.05</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="89482">
|
||||
<code>10-0944</code>
|
||||
<designation>PROTECTION MURALE TELESCOPIQUE (prix unitaire)</designation>
|
||||
<prix_vente>45.65</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>45.65</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84689">
|
||||
<code>MA-214</code>
|
||||
<designation>MATELAS NOVAFORM TRONQUE</designation>
|
||||
<prix_vente>145.46</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>145.46</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84623">
|
||||
<code>74-0485M</code>
|
||||
<designation>BUREAU-COMMODE 120 CM OIKOS</designation>
|
||||
<prix_vente>214.26</prix_vente>
|
||||
<quantite>15</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>214.26</pu_vente_avec_remise>
|
||||
<total_ht>3213.90</total_ht>
|
||||
<marge>41.94</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84439">
|
||||
<code>10-0589</code>
|
||||
<designation>TABLE A MANGER AU LIT FIXE SV</designation>
|
||||
<prix_vente>335.78</prix_vente>
|
||||
<quantite>3</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>335.78</pu_vente_avec_remise>
|
||||
<total_ht>1007.34</total_ht>
|
||||
<marge>29.69</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84824">
|
||||
<code>TS-6</code>
|
||||
<designation>GALERIES SUR LE GRAND PLATEAU</designation>
|
||||
<prix_vente>27.85</prix_vente>
|
||||
<quantite>3</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>27.85</pu_vente_avec_remise>
|
||||
<total_ht>83.55</total_ht>
|
||||
<marge>34.65</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84597">
|
||||
<code>74-0182M-UN</code>
|
||||
<designation>CHEVET 1 PORTE + 1 TIROIR O?KOS</designation>
|
||||
<prix_vente>151.17</prix_vente>
|
||||
<quantite>40</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>151.17</pu_vente_avec_remise>
|
||||
<total_ht>6046.80</total_ht>
|
||||
<marge>30.85</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="87726">
|
||||
<code>60-0292C</code>
|
||||
<designation>BRIDGE IRIS CUROSET</designation>
|
||||
<prix_vente>194.19</prix_vente>
|
||||
<quantite>40</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>194.19</pu_vente_avec_remise>
|
||||
<total_ht>7767.60</total_ht>
|
||||
<marge>44.38</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84516">
|
||||
<code>60-0206C</code>
|
||||
<designation>Chaise iris</designation>
|
||||
<prix_vente>184.14</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>184.14</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="87725">
|
||||
<code>60-0230C</code>
|
||||
<designation>FAUTEUIL IRIS DOSSIER BAS FIXE CUROSET</designation>
|
||||
<prix_vente>451.07</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>451.07</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84583">
|
||||
<code>60-0517C</code>
|
||||
<designation>FAUTEUIL DE REPOS DI ACC TAPISSES FAMILLY CUROSET</designation>
|
||||
<prix_vente>590.34</prix_vente>
|
||||
<quantite>15</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>590.34</pu_vente_avec_remise>
|
||||
<total_ht>8855.10</total_ht>
|
||||
<marge>38.87</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
</produits>
|
||||
<produits_speciaux/>
|
||||
</cleodevis>
|
||||
69
pub/files/upload/devis/dv2724_2024_10_28_17_02.xml
Executable file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cleodevis>
|
||||
<id>2724</id>
|
||||
<opportunite>LFR75370</opportunite>
|
||||
<date_demande>2024-10-24</date_demande>
|
||||
<date_remise>2024-10-25</date_remise>
|
||||
<marche>LE CEDRE</marche>
|
||||
<num_marche></num_marche>
|
||||
<nom_marche>LE CEDRE</nom_marche>
|
||||
<photos>0</photos>
|
||||
<commentaire_rr></commentaire_rr>
|
||||
<speciaux>0</speciaux>
|
||||
<total_devis_ht>65595.00</total_devis_ht>
|
||||
<total_devis_ht_remise>64681.50</total_devis_ht_remise>
|
||||
<marge_totale>66.65</marge_totale>
|
||||
<client>
|
||||
<code>750160</code>
|
||||
<etablissement>EHPAD MARIE THERESE PARIS</etablissement>
|
||||
<adresse1>277 BOULEVARD RASPAIL</adresse1>
|
||||
<adresse2></adresse2>
|
||||
<adresse3></adresse3>
|
||||
<codepostal>75014</codepostal>
|
||||
<ville>PARIS</ville>
|
||||
<contact>
|
||||
<nom>FONTANIER</nom>
|
||||
<prenom>MARIE</prenom>
|
||||
<fonction>PSYCHOMOTRICIENNE</fonction>
|
||||
<fixe>06 76 07 15 06</fixe>
|
||||
<mobile>06 76 07 15 06</mobile>
|
||||
<email>psychomotricienne@mdrmarietherese.com</email>
|
||||
</contact>
|
||||
</client>
|
||||
<produits>
|
||||
<produit id="79818">
|
||||
<code>10-1090A</code>
|
||||
<designation>LIT DE SOINS BAS SENTIDA SC ACCESSIBLE DF SFF0</designation>
|
||||
<prix_vente>3520.00</prix_vente>
|
||||
<quantite>15</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>3520.00</pu_vente_avec_remise>
|
||||
<total_ht>52800.00</total_ht>
|
||||
<marge>73.08</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="87392">
|
||||
<code>OL-154_sc</code>
|
||||
<designation>Demi-barri?res SafeFree Fix4 360</designation>
|
||||
<prix_vente>650.00</prix_vente>
|
||||
<quantite>15</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>650.00</pu_vente_avec_remise>
|
||||
<total_ht>9750.00</total_ht>
|
||||
<marge>66.30</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="80265">
|
||||
<code>MA-213</code>
|
||||
<designation>Matelas Nova Form</designation>
|
||||
<prix_vente>203.00</prix_vente>
|
||||
<quantite>15</quantite>
|
||||
<remise>30.00</remise>
|
||||
<pu_vente_avec_remise>142.10</pu_vente_avec_remise>
|
||||
<total_ht>2131.50</total_ht>
|
||||
<marge>30.33</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
</produits>
|
||||
<produits_speciaux/>
|
||||
</cleodevis>
|
||||
223
pub/files/upload/devis/dv2792_2024_11_07_11_02.xml
Executable file
@@ -0,0 +1,223 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cleodevis>
|
||||
<id>2792</id>
|
||||
<opportunite>LFR66402</opportunite>
|
||||
<date_demande>2024-11-05</date_demande>
|
||||
<date_remise>2024-11-07</date_remise>
|
||||
<marche>UNIHA_hébergement</marche>
|
||||
<num_marche>219450_LOT1</num_marche>
|
||||
<nom_marche>UNIHA</nom_marche>
|
||||
<photos>0</photos>
|
||||
<commentaire_rr></commentaire_rr>
|
||||
<speciaux>0</speciaux>
|
||||
<total_devis_ht>371725.57</total_devis_ht>
|
||||
<total_devis_ht_remise>371725.57</total_devis_ht_remise>
|
||||
<marge_totale>30.07</marge_totale>
|
||||
<client>
|
||||
<code>340069</code>
|
||||
<etablissement>CH LUNEL</etablissement>
|
||||
<adresse1>141 PLACE DE LA REPUBLIQUE</adresse1>
|
||||
<adresse2></adresse2>
|
||||
<adresse3></adresse3>
|
||||
<codepostal>34403</codepostal>
|
||||
<ville>LUNEL</ville>
|
||||
<contact>
|
||||
<nom>DILLON</nom>
|
||||
<prenom>FRANCK</prenom>
|
||||
<fonction>RESPONSABLE ACHATS, LOGISTIQUE ET TECHNIQUE</fonction>
|
||||
<fixe>04 67 87 71 58</fixe>
|
||||
<mobile>06 14 10 73 91</mobile>
|
||||
<email>f.dillon@hopital-lunel.fr</email>
|
||||
</contact>
|
||||
</client>
|
||||
<produits>
|
||||
<produit id="84648">
|
||||
<code>M3-17</code>
|
||||
<designation>GALERIE ANTI-CHUTE EN ALU BROSSE</designation>
|
||||
<prix_vente>32.49</prix_vente>
|
||||
<quantite>158</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>32.49</pu_vente_avec_remise>
|
||||
<total_ht>5133.42</total_ht>
|
||||
<marge>31.79</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84485">
|
||||
<code>1TMS18</code>
|
||||
<designation>TABLE A MANGER AU LIT SOLIDO 3 - PLATEAU FIXE</designation>
|
||||
<prix_vente>281.63</prix_vente>
|
||||
<quantite>36</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>281.63</pu_vente_avec_remise>
|
||||
<total_ht>10138.68</total_ht>
|
||||
<marge>35.58</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84439">
|
||||
<code>10-0589</code>
|
||||
<designation>TABLE A MANGER AU LIT FIXE SV</designation>
|
||||
<prix_vente>335.78</prix_vente>
|
||||
<quantite>88</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>335.78</pu_vente_avec_remise>
|
||||
<total_ht>29548.64</total_ht>
|
||||
<marge>29.69</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84536">
|
||||
<code>60-0360</code>
|
||||
<designation>FAUTEUIL DE REPOS DI+RJ EDERA</designation>
|
||||
<prix_vente>663.06</prix_vente>
|
||||
<quantite>110</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>663.06</pu_vente_avec_remise>
|
||||
<total_ht>72936.60</total_ht>
|
||||
<marge>41.63</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84542">
|
||||
<code>60-0406</code>
|
||||
<designation>BRIDGE EDERA</designation>
|
||||
<prix_vente>204.26</prix_vente>
|
||||
<quantite>110</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>204.26</pu_vente_avec_remise>
|
||||
<total_ht>22468.60</total_ht>
|
||||
<marge>38.09</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84490">
|
||||
<code>50-0155</code>
|
||||
<designation>HOUSSE MATELASSEE DEMI-BARRIERES 10</designation>
|
||||
<prix_vente>68.86</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>0.00</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84640">
|
||||
<code>AC-021</code>
|
||||
<designation>FLEXIBLE SUPPORT TELECOMMANDE 2021</designation>
|
||||
<prix_vente>34.81</prix_vente>
|
||||
<quantite>0</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>0.00</pu_vente_avec_remise>
|
||||
<total_ht>0.00</total_ht>
|
||||
<marge>0.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="89464">
|
||||
<code>10-1090A/2-UN</code>
|
||||
<designation>LIT DE SOINS BAS SENTIDA SC ACCESSIBLE Ts10</designation>
|
||||
<prix_vente>1553.36</prix_vente>
|
||||
<quantite>84</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>1553.36</pu_vente_avec_remise>
|
||||
<total_ht>130482.24</total_ht>
|
||||
<marge>20.58</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84420">
|
||||
<code>10-0127</code>
|
||||
<designation>POTENCE D'ANGLE EPOXY AVEC POIGNEE REGLABLE ET ENROULEUR</designation>
|
||||
<prix_vente>48.80</prix_vente>
|
||||
<quantite>20</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>48.80</pu_vente_avec_remise>
|
||||
<total_ht>976.00</total_ht>
|
||||
<marge>19.75</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="89482">
|
||||
<code>10-0944</code>
|
||||
<designation>PROTECTION MURALE TELESCOPIQUE (prix unitaire)</designation>
|
||||
<prix_vente>45.65</prix_vente>
|
||||
<quantite>88</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>45.65</pu_vente_avec_remise>
|
||||
<total_ht>4017.20</total_ht>
|
||||
<marge>46.02</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84689">
|
||||
<code>MA-214</code>
|
||||
<designation>MATELAS NOVAFORM TRONQUE</designation>
|
||||
<prix_vente>145.46</prix_vente>
|
||||
<quantite>120</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>145.46</pu_vente_avec_remise>
|
||||
<total_ht>17455.20</total_ht>
|
||||
<marge>28.71</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="89487">
|
||||
<code>10-0947</code>
|
||||
<designation>POIGNEE DE MOBILISATION</designation>
|
||||
<prix_vente>145.56</prix_vente>
|
||||
<quantite>3</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>145.56</pu_vente_avec_remise>
|
||||
<total_ht>436.68</total_ht>
|
||||
<marge>45.89</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="89478">
|
||||
<code>MRALLO7</code>
|
||||
<designation>RALLONGE DE MATELAS 20x86x14 CM</designation>
|
||||
<prix_vente>59.15</prix_vente>
|
||||
<quantite>3</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>59.15</pu_vente_avec_remise>
|
||||
<total_ht>177.45</total_ht>
|
||||
<marge>28.28</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84499">
|
||||
<code>50-0549</code>
|
||||
<designation>HOUSSE MATELASSEE BARRIERES 3/4 EPOXY</designation>
|
||||
<prix_vente>119.93</prix_vente>
|
||||
<quantite>142</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>119.93</pu_vente_avec_remise>
|
||||
<total_ht>17030.06</total_ht>
|
||||
<marge>37.29</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84621">
|
||||
<code>74-0483M</code>
|
||||
<designation>BUREAU-COMMODE 120 CM OIKOS</designation>
|
||||
<prix_vente>225.92</prix_vente>
|
||||
<quantite>158</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>225.92</pu_vente_avec_remise>
|
||||
<total_ht>35695.36</total_ht>
|
||||
<marge>29.86</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84597">
|
||||
<code>74-0182M-UN</code>
|
||||
<designation>CHEVET 1 PORTE + 1 TIROIR O?KOS</designation>
|
||||
<prix_vente>151.17</prix_vente>
|
||||
<quantite>158</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>151.17</pu_vente_avec_remise>
|
||||
<total_ht>23884.86</total_ht>
|
||||
<marge>30.85</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
<produit id="84651">
|
||||
<code>M3-62</code>
|
||||
<designation>OPTION 4 ROULETTES DONT 2 AVEC FREIN</designation>
|
||||
<prix_vente>8.51</prix_vente>
|
||||
<quantite>158</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>8.51</pu_vente_avec_remise>
|
||||
<total_ht>1344.58</total_ht>
|
||||
<marge>24.32</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
</produits>
|
||||
<produits_speciaux/>
|
||||
</cleodevis>
|
||||
47
pub/files/upload/devis/dv2806_2024_11_07_11_07.xml
Executable file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cleodevis>
|
||||
<id>2806</id>
|
||||
<opportunite>pas d'op</opportunite>
|
||||
<date_demande>2024-11-06</date_demande>
|
||||
<date_remise>2024-11-07</date_remise>
|
||||
<marche>RESAH 2024-R025_Réa Latéralisation LOT1 (prix Cent</marche>
|
||||
<num_marche>2024-R025-001</num_marche>
|
||||
<nom_marche>RESAH</nom_marche>
|
||||
<photos>0</photos>
|
||||
<commentaire_rr></commentaire_rr>
|
||||
<speciaux>0</speciaux>
|
||||
<total_devis_ht>4532.47</total_devis_ht>
|
||||
<total_devis_ht_remise>4532.47</total_devis_ht_remise>
|
||||
<marge_totale>38.00</marge_totale>
|
||||
<client>
|
||||
<code>310001</code>
|
||||
<etablissement>CHU TOULOUSE</etablissement>
|
||||
<adresse1>2 RUE VIGUERIE</adresse1>
|
||||
<adresse2></adresse2>
|
||||
<adresse3></adresse3>
|
||||
<codepostal>31059</codepostal>
|
||||
<ville>TOULOUSE CEDEX 9</ville>
|
||||
<contact>
|
||||
<nom>SALGUES</nom>
|
||||
<prenom>Eric</prenom>
|
||||
<fonction>Ingénieur Biomédical</fonction>
|
||||
<fixe>06 29 22 16 40</fixe>
|
||||
<mobile></mobile>
|
||||
<email>salgues.e@chu-toulouse.fr</email>
|
||||
</contact>
|
||||
</client>
|
||||
<produits>
|
||||
<produit id="93465">
|
||||
<code>MV305</code>
|
||||
<designation>MATELAS A AIR VIRTUOSO 300 DV/DT</designation>
|
||||
<prix_vente>4532.47</prix_vente>
|
||||
<quantite>1</quantite>
|
||||
<remise>0.00</remise>
|
||||
<pu_vente_avec_remise>4532.47</pu_vente_avec_remise>
|
||||
<total_ht>4532.47</total_ht>
|
||||
<marge>38.00</marge>
|
||||
<commentaire></commentaire>
|
||||
</produit>
|
||||
</produits>
|
||||
<produits_speciaux/>
|
||||
</cleodevis>
|
||||