diff --git a/CHANGELOG.md b/CHANGELOG.md index 02aaaa15..d03ed4e1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,23 @@ # CHANGELOG +### Version 11.3 +- Minor UI updates +- Fix merge ingredient returning incorrect value when nothing found +- Fix placeholder for ingredient selection in formula +- Added force delete for ingredients in use +- A new updated IFRA document added +- IFRA document will show the maximum limit per all categories for 100% concentration +- IFRA document will be generated regardless if final product is off ifra limits +- Fixed currency symbol for finished product +- Ingredient UI update +- Formula UI and backend update +- Structure update +- UI update for IFRA Library +- Refactor JSON import for IFRALibrary +- Increase filter box size +- Extended search to the product name for Batches +- Added delete option for Batches even if the pdf is missing +- Batches backend rewrite + ### Version 11.2 - Refactor of backend - Fix author name not display properly in the contact form in Marketplace diff --git a/VERSION.md b/VERSION.md index 26d6dad9..8bb42223 100755 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -11.2 +11.3 diff --git a/core/finished_formula_data.php b/core/finished_formula_data.php index 01352ad7..e233e107 100644 --- a/core/finished_formula_data.php +++ b/core/finished_formula_data.php @@ -190,7 +190,7 @@ $m['cat_class'] = (string)$defCatClass; -$m['currency'] = (string)utf8_encode($settings['currency']); +$m['currency'] = (string)$settings['currency']; $m['product_concentration'] = (int)$concentration; $m['formula_name'] = (string)$meta['name']; $m['product_name'] = (string)$meta['product_name'] ?: '-'; diff --git a/core/full_formula_data.php b/core/full_formula_data.php index 7aed4a79..54fb8670 100644 --- a/core/full_formula_data.php +++ b/core/full_formula_data.php @@ -274,6 +274,8 @@ if(empty($r)){ $response['data'] = []; + echo json_encode($response); + return; } $m['total_ingredients'] = (int)countElement("formulas WHERE fid = '".$meta['fid']."'",$conn); diff --git a/core/getIngInfo.php b/core/getIngInfo.php new file mode 100644 index 00000000..1f905f36 --- /dev/null +++ b/core/getIngInfo.php @@ -0,0 +1,41 @@ + \ No newline at end of file diff --git a/core/list_batch_data.php b/core/list_batch_data.php index ad88e1c8..02825100 100644 --- a/core/list_batch_data.php +++ b/core/list_batch_data.php @@ -5,55 +5,81 @@ require_once(__ROOT__.'/inc/opendb.php'); require_once(__ROOT__.'/inc/settings.php'); -$row = $_POST['start']?:0; -$limit = $_POST['length']?:10; +$row = isset($_POST['start']) ? (int)$_POST['start'] : 0; +$limit = isset($_POST['length']) ? (int)$_POST['length'] : 10; -$order_by = $_POST['order_by']?:'created'; -$order = $_POST['order_as']?:'ASC'; -$extra = "ORDER BY ".$order_by." ".$order; +$order_by = isset($_POST['order_by']) ? $_POST['order_by'] : 'created'; +$order = isset($_POST['order_as']) && in_array(strtoupper($_POST['order_as']), ['ASC', 'DESC']) ? strtoupper($_POST['order_as']) : 'ASC'; +$extra = "ORDER BY $order_by $order"; -$s = trim($_POST['search']['value']); +$search_value = isset($_POST['search']['value']) ? trim($_POST['search']['value']) : ''; +$f = ''; -if($s != ''){ - $f = "WHERE 1 AND (id LIKE '%".$s."%')"; +if (!empty($search_value)) { + $f = "WHERE id LIKE ? OR product_name LIKE ?"; } -$q = mysqli_query($conn, "SELECT * FROM batchIDHistory $f $extra LIMIT $row, $limit"); -while($res = mysqli_fetch_array($q)){ +$query = "SELECT * FROM batchIDHistory $f $extra LIMIT ?, ?"; +$stmt = $conn->prepare($query); + +if (!empty($search_value)) { + $search_param = "%$search_value%"; + $stmt->bind_param('ssii', $search_param, $search_param, $row, $limit); +} else { + $stmt->bind_param('ii', $row, $limit); +} + +$stmt->execute(); +$result = $stmt->get_result(); + +$rs = []; +while ($res = $result->fetch_assoc()) { $rs[] = $res; } -foreach ($rs as $rq) { - - if(file_exists(__ROOT__.'/'.$rq['pdf']) === TRUE){ - $s = 1; - } - - $r['id'] = (string)$rq['id']; - $r['fid'] = (string)$rq['fid']; - $r['product_name'] = (string)$rq['product_name']?:'N/A'; - $r['pdf'] = (string)$rq['pdf']; - $r['state'] = (int)$s?:0; - $r['created'] = (string)$rq['created']; - - $rx[]=$r; +$rx = []; +foreach ($rs as $rq) { + $state = 0; + if (file_exists(__ROOT__.'/'.$rq['pdf'])) { + $state = 1; + } + + $r = [ + 'id' => (string)$rq['id'], + 'fid' => (string)$rq['fid'], + 'product_name' => (string)$rq['product_name'] ?: 'N/A', + 'pdf' => (string)$rq['pdf'], + 'state' => (int)$state, + 'created' => (string)$rq['created'] + ]; + + $rx[] = $r; } -$total = mysqli_fetch_assoc(mysqli_query($conn,"SELECT COUNT(id) AS entries FROM batchIDHistory")); -$filtered = mysqli_fetch_assoc(mysqli_query($conn,"SELECT COUNT(id) AS entries FROM batchIDHistory ".$f)); - -$response = array( - "draw" => (int)$_POST['draw'], - "recordsTotal" => (int)$total['entries'], - "recordsFiltered" => (int)$filtered['entries'], - "data" => $rx -); - -if(empty($r)){ - $response['data'] = []; + +$total_query = "SELECT COUNT(id) AS entries FROM batchIDHistory"; +$total_result = mysqli_fetch_assoc(mysqli_query($conn, $total_query)); + +$filtered_query = "SELECT COUNT(id) AS entries FROM batchIDHistory $f"; +$filtered_stmt = $conn->prepare($filtered_query); + +if (!empty($search_value)) { + $filtered_stmt->bind_param('ss', $search_param, $search_param); } +$filtered_stmt->execute(); +$filtered_result = $filtered_stmt->get_result(); +$filtered = $filtered_result->fetch_assoc(); + +$response = [ + "draw" => isset($_POST['draw']) ? (int)$_POST['draw'] : 1, + "recordsTotal" => (int)$total_result['entries'], + "recordsFiltered" => (int)$filtered['entries'], + "data" => !empty($rx) ? $rx : [] +]; header('Content-Type: application/json; charset=utf-8'); echo json_encode($response); + return; + ?> diff --git a/css/vault.css b/css/vault.css index 3bec2a72..9ba2e069 100755 --- a/css/vault.css +++ b/css/vault.css @@ -1410,8 +1410,17 @@ hr.hr-text::before { } .dataTables_filter input { - width: 400px; - display: inline-block; + width: 400px !important; + height: 40px; + padding: 10px; + font-size: 16px; +} + +.pvCustomSearch { + width: 400px; + height: 40px; + padding: 10px; + font-size: 16px; } input[type="search"]::-webkit-search-cancel-button { diff --git a/db/pvault.sql b/db/pvault.sql index 568c3141..2def9f5b 100755 --- a/db/pvault.sql +++ b/db/pvault.sql @@ -669,7 +669,97 @@ CREATE TABLE `templates` ( UNIQUE KEY `id` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; -INSERT INTO `templates` (`id`, `name`, `content`, `created`, `updated`, `description`) VALUES (NULL, 'IFRA Certification Template', '\r\n\r\n\r\n \r\n \r\n \r\n \r\n\r\n\r\n\r\n
\r\n

\r\n
\r\n

CERTIFICATE OF CONFORMITY OF FRAGRANCE MIXTURES WITH IFRA STANDARDS
\r\n

\r\n

This Certificate assesses the conformity of a fragrance mixture with IFRA Standards and provides restrictions for use as necessary. It is based only on those materials subject to IFRA Standards for the toxicity endpoint(s) described in each Standard. \r\n

\r\n


\r\n

\r\n
\r\n

\r\n

CERTIFYING PARTY:

\r\n

%BRAND_NAME%

\r\n

%BRAND_ADDRESS%

\r\n

%BRAND_EMAIL%

\r\n

%BRAND_PHONE%

\r\n\r\n\r\n

\r\n

CERTIFICATE DELIVERED TO: \r\n

\r\n

Customer:

\r\n

%CUSTOMER_NAME%

\r\n

%CUSTOMER_ADDRESS%

\r\n

%CUSTOMER_EMAIL%

\r\n

%CUSTOMER_WEB%

\r\n\r\n\r\n


\r\n

\r\n

SCOPE OF THE CERTIFICATE:

\r\n

Product: %PRODUCT_NAME%

\r\n

Size: %PRODUCT_SIZE%ml

\r\n

Concentration: %PRODUCT_CONCENTRATION%%

\r\n

\r\n COMPULSORY INFORMATION:\r\n

\r\n We certify that the above mixture is in compliance with the Standards of the INTERNATIONAL FRAGRANCE ASSOCIATION (IFRA), up to and including the %IFRA_AMENDMENT% Amendment to the IFRA Standards (published %IFRA_AMENDMENT_DATE%),\r\n provided it is used in the following category(ies)\r\n at a maximum concentration level of:

\r\n

 

\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
IFRA Category(ies)Level of use (%)*
%PRODUCT_CAT_CLASS%%PRODUCT_TYPE%
\r\n

*Actual use level or maximum use level

\r\n

\r\n For other kinds of, application or use at higher concentration levels, a new evaluation may be needed; please contact %BRAND_NAME%.\r\n

\r\n

Information about presence and concentration of fragrance ingredients subject to IFRA Standards in the fragrance mixture %PRODUCT_NAME% is as follows:

\r\n

 

\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n %IFRA_MATERIALS_LIST%\r\n
Material(s) under the scope of IFRA Standards:CAS number(s):Recommendation (%) from IFRA Standard:Concentration (%) in finished product:Risk
\r\n

 

\r\n

Signature (If generated electronically, no signature)

\r\n

Date: %CURRENT_DATE%

\r\n

\r\n
\r\n

\r\n Disclaimer:\r\n

\r\n

This Certificate provides restrictions for use of the specified product based only on those materials restricted by IFRA Standards for the toxicity endpoint(s) described in each Standard.

\r\n

This Certificate does not provide certification of a comprehensive safety assessment of all product constituents.

\r\n

This certificate is the responsibility of the fragrance supplier issuing it. It has not been prepared or endorsed by IFRA in anyway. \r\n

\r\n
\r\n\r\n', current_timestamp(), current_timestamp(), 'The default IFRA certification template'); +INSERT INTO `templates` (`id`, `name`, `content`, `created`, `updated`, `description`) VALUES (NULL, 'IFRA Document Template', ' + + + + + + + + + +
+

+
+

CERTIFICATE OF CONFORMITY OF FRAGRANCE MIXTURES WITH IFRA STANDARDS
+

+

This Certificate assesses the conformity of a fragrance mixture with IFRA Standards and provides restrictions for use as necessary. It is based only on those materials subject to IFRA Standards for the toxicity endpoint(s) described in each Standard. +

+


+

+
+

+

CERTIFYING PARTY:

+

%BRAND_NAME%

+

%BRAND_ADDRESS%

+

%BRAND_EMAIL%

+

%BRAND_PHONE%

+ + +

+

CERTIFICATE DELIVERED TO: +

+

Customer:

+

%CUSTOMER_NAME%

+

%CUSTOMER_ADDRESS%

+

%CUSTOMER_EMAIL%

+

%CUSTOMER_WEB%

+ +


+

+

SCOPE OF THE CERTIFICATE:

+

Product: %PRODUCT_NAME%

+

Size: %PRODUCT_SIZE%ml

+

Concentration: %PRODUCT_CONCENTRATION%%

+

+ COMPULSORY INFORMATION: +

+ We certify that the above mixture is in compliance with the Standards of the INTERNATIONAL FRAGRANCE ASSOCIATION (IFRA), up to and including the %IFRA_AMENDMENT% Amendment to the IFRA Standards (published %IFRA_AMENDMENT_DATE%), + provided it is used in the following category(ies) + at a maximum concentration level of:

+

 

+ + + + + + + + + +
IFRA CategoryDescriptionLevel of use (%)*
%IFRA_CAT_LIST%
+

*Actual use level or maximum use level at 100% concentration

+

+ For other kinds of, application or use at higher concentration levels, a new evaluation may be needed; please contact %BRAND_NAME%. +

+

Information about presence and concentration of fragrance ingredients subject to IFRA Standards in the fragrance mixture %PRODUCT_NAME% is as follows:

+

 

+ + + + + + + + + %IFRA_MATERIALS_LIST% +
Material(s) under the scope of IFRA Standards:CAS number(s):Recommendation (%) from IFRA Standard:Concentration (%) in finished product:Risk
+

 

+

Signature (If generated electronically, no signature)

+

Date: %CURRENT_DATE%

+

+
+

+ Disclaimer: +

+

This Certificate provides restrictions for use of the specified product based only on those materials restricted by IFRA Standards for the toxicity endpoint(s) described in each Standard.

+

This Certificate does not provide certification of a comprehensive safety assessment of all product constituents.

+

This certificate is the responsibility of the fragrance supplier issuing it. It has not been prepared or endorsed by IFRA in anyway. +

+
+ +', current_timestamp(), current_timestamp(), 'The default IFRA document template'); INSERT INTO `templates` (`id`, `name`, `content`, `created`, `updated`, `description`) VALUES (9, 'SDS Example template', '\n\n\n\n \n \n \n \n\n\n\n
\n
\n
\n \n
\n
\n
%SDS_PRODUCT_NAME%
\n Language %SDS_LANGUAGE%\n
%CURRENT_DATE%
\n
According to Regulation (EC) No. 1907/2006 (amended by Regulation (EU) No. 2020/878)
\n
\n
\n
\n
\n

1. Identification of the substance/mixture and of the company/undertaking

\n
\n
\n
\n
\n
1.1 Product identifier
\n
\n
Trade name/designation
\n
%SDS_PRODUCT_NAME%
\n
\n
\n
\n
1.2 Relevant identified uses of the substance or mixture and uses advised against
\n
\n
Relevant identified uses
\n
%SDS_PRODUCT_USE%
\n
\n
\n
Uses advised against
\n
%SDS_PRODUCT_ADA%
\n
\n
\n\n
\n
1.3 Details of the supplier of the safety data sheet
\n
\n
Supplier
\n
%SDS_SUPPLIER_NAME%
\n
%SDS_SUPPLIER_ADDRESS%, %SDS_SUPPLIER_COUNTRY%, %SDS_SUPPLIER_PO%
\n
%SDS_SUPPLIER_EMAIL%
\n
%SDS_SUPPLIER_PHONE%
\n
%SDS_SUPPLIER_WEB%
\n
\n
\n
\n
\n
\n
\n
\n

2. Hazards identification

\n
\n
\n
\n 2.2 Labeling\n

\n Label elements according to the regulation (EC) n°1272/2008 (CLP) and its amendments\n

\n
\n
\n %GHS_LABEL_LIST%\n
\n
\n
\n
\n
\n

3. Composition/information on ingredients

\n
\n
\n
\n In accordance with the product knowledge, no nanomaterials have been identified. The mixture does not contain any substances classified as Substances of Very High Concern (SVHC) by the European Chemicals Agency (ECHA) under article 57 of REACH: http://echa.europa.eu/en/candidate-list-table
\n
\n
\n \n \n \n \n \n \n \n \n %CMP_MATERIALS_LIST%\n \n
NameCASEINESMin percentageMax percentageGHS
\n
\n
\n
\n
\n
\n
\n

4. First aid measures

\n
\n
\n
\n Description of first aid measures\n
\n
\n
\n
General information
\n
%FIRST_AID_GENERAL%
\n
\n
\n
Following inhalation
\n
%FIRST_AID_INHALATION%
\n
\n
\n
Following skin contact
\n
%FIRST_AID_SKIN%
\n
\n
\n
Following eye contact
\n
%FIRST_AID_EYE%
\n
\n
\n
Following ingestion
\n
%FIRST_AID_INGESTION%
\n
\n
\n
Self-protection of the first aider
\n
%FIRST_AID_SELF_PROTECTION%
\n
\n
\n
Most important symptoms and effects, both acute and delayed
\n
%FIRST_AID_SYMPTOMS%
\n
\n
\n
Notes for the doctor
\n
%FIRST_AID_DR_NOTES%
\n
\n
\n
\n
\n
\n
\n

5. Firefighting measures

\n
\n
\n
\n
\n
Suitable extinguishing media
\n
%FIRE_SUIT_MEDIA%
\n
\n
\n
Unsuitable extinguishing media
\n
%FIRE_NONSUIT_MEDIA%
\n
\n
\n
Special hazards arising from the substance or mixture
\n
%FIRE_SPECIAL_HAZARDS%
\n
\n
\n
Advice for firefighters
\n
%FIRE_ADVICE%
\n
\n
\n
Additional information
\n
%FIRE_OTHER_INFO%
\n
\n
\n
\n
\n
\n
\n

6. Accidental release measures

\n
\n
\n
\n
\n
Personal precautions, protective equipment and emergency procedures
\n
%ACC_REL_PERSONAL_CAUTIONS%
\n
\n
\n
Environmental precautions
\n
%ACC_REL_ENV_CAUTIONS%
\n
\n
\n
Methods and material for containment and cleaning up
\n
%ACC_REL_CLEANING%
\n
\n
\n
Reference to other sections
\n
%ACC_REL_REFERENCES%
\n
\n
\n
Additional information
\n
%ACC_REL_OTHER_INFO%
\n
\n
\n
\n
\n
\n
\n

7. Handling and Storage

\n
\n
\n
\n
\n
Precautions for safe handling
\n
%HS_PROTECTION%
\n
\n
\n
Advices on general occupational hygiene
\n
%HS_HYGIENE%
\n
\n
\n
Conditions for safe storage, including any incompatibilities
\n
%HS_SAFE_STORE%
\n
\n
\n
Advice on joint storage
\n
%HS_JOINT_STORE%
\n
\n
\n
Specific end uses
\n
%HS_SPECIFIC_USES%
\n
\n
\n
\n
\n
\n
\n

8. Exposure controls/personal protection

\n
\n
\n
\n
\n
Occupational exposure limits
\n
%EXPOSURE_OCC_LIMIT%
\n
\n
\n
Biological limit values
\n
%EXPOSURE_BIO_LIMIT%
\n
\n
\n
Exposure limits at intended use
\n
%EXPOSURE_USE_LIMIT%
\n
\n
\n
Remarks
\n
%EXPOSURE_OTHER_REM%
\n
\n
\n
Eye/face protection
\n
%EXPOSURE_FACE_PROTECTION%
\n
\n
\n
Skin protection
\n
%EXPOSURE_SKIN_PROTECTION%
\n
\n
\n
Respiratory protection
\n
%EXPOSURE_RESP_PROTECTION%
\n
\n
\n
Environmental exposure controls
\n
%EXPOSURE_ENV_EXPOSURE%
\n
\n
\n
Consumer exposure controls
\n
%EXPOSURE_CONS_EXPOSURE%
\n
\n
\n
Additional information
\n
%EXPOSURE_OTHER_INFO%
\n
\n
\n
\n
\n
\n
\n

9. Physical and chemical Properties

\n
\n
\n
\n
\n
Physical state
\n
%PHYSICAL_STATE%
\n
\n
\n
Color
\n
%COLOR%
\n
\n
\n
Odor
\n
%ODOR%
\n
\n
\n
Odor threshold
\n
%ODOR_THRESHOLD%
\n
\n
\n
pH
\n
%PH%
\n
\n
\n
Melting/Freezing point
\n
%MELTING_POINT%
\n
\n
\n
Boiling point
\n
%BOILING_POINT%
\n
\n
\n
Flash point
\n
%FLASH_POINT%
\n
\n
\n
Evaporation rate
\n
%EVAPORATION_RATE%
\n
\n
\n
Water solubility
\n
%SOLUBILITY%
\n
\n
\n
Partition coefficient, n-octanol/water (log Pow)
\n
%LOGP%
\n
\n
\n
Auto-inflammability temperature
\n
%AUTO_INFL_TEMP%
\n
\n
\n
Decomposition temperature
\n
%DECOMP_TEMP%
\n
\n
\n
Viscosity
\n
%VISCOSITY%
\n
\n
\n
Explosive properties
\n
%EXPLOSIVE_PROPERTIES%
\n
\n
\n
Oxidising properties
\n
%OXIDISING_PROPERTIES%
\n
\n
\n
Solubility in other Solvents
\n
%SOLVENTS%
\n
\n
\n
Particle characteristics
\n
%PARTICLE_CHARACTERISTICS%
\n
\n
\n
\n
\n
\n
\n

10. Stability and Reactivity

\n
\n
\n
\n
\n
Reactivity
\n
%STABILLITY_REACTIVITY%
\n
\n
\n
Chemical stability
\n
%STABILLITY_CHEMICAL%
\n
\n
\n
Possibility of hazardous reactions
\n
%STABILLITY_REACTIONS%
\n
\n
\n
Conditions to avoid
\n
%STABILLITY_AVOID%
\n
\n
\n
Incompatible materials
\n
%STABILLITY_INCOMPATIBILITY%
\n
\n
\n
\n
\n
\n
\n

11. Toxicological information

\n
\n
\n
\n
\n
Acute oral toxicity
\n
%TOXICOLOGICAL_ACUTE_ORAL%
\n
\n
\n
Acute dermal toxicity
\n
%TOXICOLOGICAL_ACUTE_DERMAL%
\n
\n
\n
Acute inhalation toxicity
\n
%TOXICOLOGICAL_ACUTE_INHALATION%
\n
\n
\n
Skin corrosion/irritation
\n
%TOXICOLOGICAL_SKIN%
\n
\n
\n
Serious eye damage/irritation
\n
%TOXICOLOGICAL_EYE%
\n
\n\n
\n
Skin sensitisation
\n
%TOXICOLOGICAL_SENSITISATION%
\n
\n
\n
Specific target organ toxicity (repeated exposure)
\n
%TOXICOLOGICAL_ORGAN_REPEATED%
\n
\n
\n
Specific target organ toxicity (single exposure)
\n
%TOXICOLOGICAL_ORGAN_SINGLE%
\n
\n
\n
Carcinogenicity
\n
%TOXICOLOGICAL_CARCINOGENCITY%
\n
\n
\n
Reproductive toxicity
\n
%TOXICOLOGICAL_REPRODUCTIVE%
\n
\n
\n
Germ cell mutagenicity
\n
%TOXICOLOGICAL_CELL_MUTATION%
\n
\n
\n
Sensitisation to the respiratory tract
\n
%TOXICOLOGICAL_RESP_TRACT%
\n
\n
\n
Additional information
\n
%TOXICOLOGICAL_OTHER_INFO%
\n
\n
\n
Information on other hazards
\n
%TOXICOLOGICAL_OTHER_HAZARDS%
\n
\n
\n
\n\n
\n
\n
\n

12. Ecological information

\n
\n
\n
\n
\n
Toxicity
\n
%ECOLOGICAL_TOXICITY%
\n
\n
\n
Persistence and degradability
\n
%ECOLOGICAL_PERSISTENCE%
\n
\n
\n
Bioaccumulative potential
\n
%ECOLOGICAL_BIOACCUMULATIVE%
\n
\n
\n
Mobility in soil
\n
%ECOLOGICAL_SOIL_MOBILITY%
\n
\n
\n
Results of PBT and vPvB assessment
\n
%ECOLOGICAL_PBT_VPVB%
\n
\n
\n
Endocrine disrupting properties
\n
%ECOLOGICAL_ENDOCRINE_PROPERTIES%
\n
\n
\n
Other adverse effects
\n
%ECOLOGICAL_OTHER_ADV_EFFECTS%
\n
\n
\n
Additional ecotoxicological information
\n
%ECOLOGICAL_ADDITIONAL_ECOTOXICOLOGICAL_INFO%
\n
\n
\n
\n
\n
\n
\n

13. Disposal considerations

\n
\n
\n
\n
\n
Product/Packaging disposal
\n
%DISPOSAL_PRODUCT%
\n
\n
\n
Remark
\n
%DISPOSAL_REMARKS%
\n
\n
\n
\n\n
\n
\n
\n

14. Transport information

\n
\n
\n
\n
\n
UN number
\n
%TRANSPORT_UN_NUMBER%
\n
\n
\n
UN proper shipping name
\n
%TRANSPORT_SHIPPING_NAME%
\n
\n
\n
Transport hazard class(es)
\n
%TRANSPORT_HAZARD_CLASS%
\n
\n
\n
Packing group
\n
%TRANSPORT_PACKING_GROUP%
\n
\n
\n
Environmental hazards
\n
%TRANSPORT_ENV_HAZARDS%
\n
\n
\n
Special precautions for user
\n
%TRANSPORT_PRECAUTIONS%
\n
\n
\n
Bulk shipping according to IMO instruments
\n
%TRANSPORT_BULK_SHIPPING%
\n
\n
\n
\n
\n
\n
\n

15. Regulatory information

\n
\n
\n
\n
\n
Safety, health and environmental regulations/legislation specific for the substance or mixture
\n
%LEGISLATION_SAFETY%
\n
\n
\n
EU legislation
\n
%LEGISLATION_EU%
\n
\n
\n
Chemical Safety Assessment
\n
%LEGISLATION_CHEMICAL_SAFETY_ASSESSMENT%
\n
\n
\n
Additional information
\n
%LEGISLATION_OTHER_INFO%
\n
\n
\n
\n\n\n
\n
\n
\n

16. Other information

\n
\n
\n
\n
\n
Indication of changes
\n
%ADD_INFO_CHANGES%
\n
\n
\n
Abbreviations and acronyms
\n
%ADD_INFO_ACRONYMS%
\n
\n
\n
Key literature references and sources for data
\n
%ADD_INFO_REFERENCES%
\n
\n
\n
The classification of the mixture is in accordance with the evaluation method described in HazCom 2012
\n
%ADD_INFO_HAZCOM%
\n
\n
\n
The classification of the mixture is in accordance with the evaluation method described in the GHS
\n
%ADD_INFO_GHS%
\n
\n
\n
Training advice
\n
%ADD_INFO_TRAINING%
\n
\n
\n
Additional information
\n
%ADD_INFO_OTHER%
\n
\n
\n
\n\n
%BRAND_NAME%
\n Creation date: %CURRENT_DATE%\n
\n
\n %SDS_DISCLAIMER%\n
\n
\n

\n %BRAND_NAME%\n

\n

\n www.perfumersvault.com\n

\n
\n
\n
\n\n\n', '2024-06-22 10:23:11', '2024-06-27 08:47:38', 'This is an example template'); diff --git a/db/schema.ver b/db/schema.ver index 26d6dad9..8bb42223 100644 --- a/db/schema.ver +++ b/db/schema.ver @@ -1 +1 @@ -11.2 +11.3 diff --git a/db/updates/update_11.2-11.3.sql b/db/updates/update_11.2-11.3.sql new file mode 100644 index 00000000..fd239a6c --- /dev/null +++ b/db/updates/update_11.2-11.3.sql @@ -0,0 +1,91 @@ +INSERT INTO `templates` (`id`, `name`, `content`, `created`, `updated`, `description`) VALUES (NULL, 'IFRA Document Template', ' + + + + + + + + + +
+

+
+

CERTIFICATE OF CONFORMITY OF FRAGRANCE MIXTURES WITH IFRA STANDARDS
+

+

This Certificate assesses the conformity of a fragrance mixture with IFRA Standards and provides restrictions for use as necessary. It is based only on those materials subject to IFRA Standards for the toxicity endpoint(s) described in each Standard. +

+


+

+
+

+

CERTIFYING PARTY:

+

%BRAND_NAME%

+

%BRAND_ADDRESS%

+

%BRAND_EMAIL%

+

%BRAND_PHONE%

+ + +

+

CERTIFICATE DELIVERED TO: +

+

Customer:

+

%CUSTOMER_NAME%

+

%CUSTOMER_ADDRESS%

+

%CUSTOMER_EMAIL%

+

%CUSTOMER_WEB%

+ +


+

+

SCOPE OF THE CERTIFICATE:

+

Product: %PRODUCT_NAME%

+

Size: %PRODUCT_SIZE%ml

+

Concentration: %PRODUCT_CONCENTRATION%%

+

+ COMPULSORY INFORMATION: +

+ We certify that the above mixture is in compliance with the Standards of the INTERNATIONAL FRAGRANCE ASSOCIATION (IFRA), up to and including the %IFRA_AMENDMENT% Amendment to the IFRA Standards (published %IFRA_AMENDMENT_DATE%), + provided it is used in the following category(ies) + at a maximum concentration level of:

+

 

+ + + + + + + + + +
IFRA CategoryDescriptionLevel of use (%)*
%IFRA_CAT_LIST%
+

*Actual use level or maximum use level at 100% concentration

+

+ For other kinds of, application or use at higher concentration levels, a new evaluation may be needed; please contact %BRAND_NAME%. +

+

Information about presence and concentration of fragrance ingredients subject to IFRA Standards in the fragrance mixture %PRODUCT_NAME% is as follows:

+

 

+ + + + + + + + + %IFRA_MATERIALS_LIST% +
Material(s) under the scope of IFRA Standards:CAS number(s):Recommendation (%) from IFRA Standard:Concentration (%) in finished product:Risk
+

 

+

Signature (If generated electronically, no signature)

+

Date: %CURRENT_DATE%

+

+
+

+ Disclaimer: +

+

This Certificate provides restrictions for use of the specified product based only on those materials restricted by IFRA Standards for the toxicity endpoint(s) described in each Standard.

+

This Certificate does not provide certification of a comprehensive safety assessment of all product constituents.

+

This certificate is the responsibility of the fragrance supplier issuing it. It has not been prepared or endorsed by IFRA in anyway. +

+
+ +', current_timestamp(), current_timestamp(), 'The default IFRA document template'); \ No newline at end of file diff --git a/func/convert_to_decimal_point.php b/func/convert_to_decimal_point.php new file mode 100644 index 00000000..bba29b0f --- /dev/null +++ b/func/convert_to_decimal_point.php @@ -0,0 +1,14 @@ + 0) { + return '.' . str_repeat('0', $decimalPlaces); + } else { + return '0'; // If 0 or negative, return plain 0 + } +} + +?> \ No newline at end of file diff --git a/js/fullformula.view.js b/js/fullformula.view.js index 36f932cd..ed8d6c0d 100644 --- a/js/fullformula.view.js +++ b/js/fullformula.view.js @@ -258,7 +258,7 @@ $("#formula").on("click", ".open-replace-dialog", function () { $("#repIngNameDest").select2({ width: '100%', - placeholder: 'Search for ingredient (name, cas)', + placeholder: '', allowClear: true, dropdownAutoWidth: true, containerCssClass: "repIngNameDest", @@ -309,12 +309,15 @@ $("#formula").on("click", ".open-replace-dialog", function () { function formatIngredients (ingredientData) { + //console.log(ingredientData); + if (ingredientData.name === '') { + return 'Search for ingredient (name, cas)'; + } + if (ingredientData.loading) { return ingredientData.name; } - - //extrasShow(); - + if (!ingredientData.name){ return 'No ingredient found...'; } diff --git a/js/select2-v3-ingredient.js b/js/select2-v3-ingredient.js index 4054498e..4a3b08dd 100644 --- a/js/select2-v3-ingredient.js +++ b/js/select2-v3-ingredient.js @@ -38,7 +38,7 @@ $(document).ready(function(){ minimumInputLength: 2, dropdownAutoWidth: true, allowClear: true, - placeholder: 'Choose ingredient (name, cas)', + placeholder: '', templateResult: formatIngredients, templateSelection: formatIngredientsSelection, ajax: { @@ -174,61 +174,58 @@ $(document).ready(function(){ function formatIngredientsSelection (ingredientData) { + if (ingredientData.id === '') { + return 'Search ingredients (name, cas)'; + } + return ingredientData.name; } //UPDATE PURITY - $('#ingredient').on('select2:selecting', function(e){ + $('#ingredient').on('select2:selecting', function(e) { var ingType = $(e.currentTarget).attr('ing-type'); var ingID = $(e.currentTarget).attr('ing-id'); - //console.log(e); - $.ajax({ - url: '/pages/getIngInfo.php', - type: 'GET', - data: { - filter: "purity", - id: ingID - }, - dataType: 'json', - success: function (data) { - if(ingType == 'Solvent'){ - $("#concentration").prop("disabled", true); - $("#dilutant").prop("disabled", true); - $("#concentration").val(100); - $("#dilutant").val('None'); - }else{ - $("#concentration").prop("disabled", false); - $("#concentration").val(data.purity).trigger("input");; - } - $("#quantity").prop("disabled", false); - $("#quantity").val(); - } - }); - $.ajax({ - url: '/pages/getIngInfo.php', + $.ajax({ + url: '/core/getIngInfo.php', type: 'GET', data: { - filter: "solvent", + filter: "purity,solvent", id: ingID - }, + }, dataType: 'json', - success: function (data) { - $('#dilutant').val(data.solvent); + success: function(data) { + if (ingType === 'Solvent') { + $("#concentration").prop("disabled", true).val(100); + $("#dilutant").prop("disabled", true).val('none').selectpicker("refresh"); + } else { + $("#concentration").prop("disabled", false).val(data.purity).trigger("input"); + $("#dilutant").prop("disabled", false).val(data.solvent).selectpicker("refresh"); + } + + $("#quantity").prop("disabled", false); + }, + error: function(xhr, status, error) { + console.error('Error fetching ingredient info:', error); } }); - }); - $('#concentration').bind('input', function() { - var concentration = $('#concentration').val(); - if(concentration >= 100){ - $("#dilutant").prop("disabled", true); - $("#dilutant").val('').selectpicker("refresh"); - }else{ - $("#dilutant").prop("disabled", false).selectpicker('refresh'); + $('#concentration').on('input', function() { + const MAX_CONCENTRATION = 100; + var concentration = parseFloat($(this).val()); + + if (!isNaN(concentration)) { + if (concentration >= MAX_CONCENTRATION) { + $("#dilutant").prop("disabled", true).val('none').selectpicker("refresh"); + } else { + $("#dilutant").prop("disabled", false).selectpicker("refresh"); + } + } else { + console.warn('Invalid concentration input'); } - //$('.selectpicker').selectpicker('refresh'); }); + + }); diff --git a/login.php b/login.php index 579b392c..e2c72e27 100755 --- a/login.php +++ b/login.php @@ -228,7 +228,7 @@ }else if( data.auth.error){ msg = '
'+data.auth.msg+'
'; $("#login_btn").prop("disabled", false); - $("span").remove(); + $("#login_btn span").remove(); $("#login_email").prop("disabled", false); $("#login_pass").prop("disabled", false); } @@ -237,7 +237,7 @@ }, error: function (request, status, error) { $('#msg').html('
Unable to handle request, server returned an error: '+request.status+'
'); - $("span").remove(); + $("#login_btn span").remove(); $("#login_email").prop("disabled", false); $("#login_pass").prop("disabled", false); $("#login_btn").prop("disabled", false); diff --git a/pages/IFRA.php b/pages/IFRA.php index 995c9bd9..01e03b27 100755 --- a/pages/IFRA.php +++ b/pages/IFRA.php @@ -221,8 +221,8 @@ loadingRecords: ' ', processing: '
Please Wait...', zeroRecords: '
Nothing found, have you imported the IFRA library?
', - search: 'Quick Search:', - searchPlaceholder: 'Name, CAS, synonyms...', + search: '', + searchPlaceholder: 'Search by name, CAS, synonyms...', }, ajax: { url: '/core/list_IFRA_data.php', @@ -349,11 +349,18 @@ function reload_data() { $('#tdDataIFRA').DataTable().ajax.reload(null, true); }; - function actions(data, type, row){ - data = ''; - - return data; + function actions(data, type, row) { + data = ''; + + return data; }; + $('#exportCSV').click(() => { diff --git a/pages/bottles.php b/pages/bottles.php index efc2058d..757180bc 100644 --- a/pages/bottles.php +++ b/pages/bottles.php @@ -161,8 +161,8 @@ loadingRecords: ' ', processing: 'Please Wait...', zeroRecords: 'Nothing found', - search: 'Quick Search:', - searchPlaceholder: 'Name..', + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/list_bottle_data.php', diff --git a/pages/compounds.php b/pages/compounds.php index 9126a2da..3ba8a12e 100644 --- a/pages/compounds.php +++ b/pages/compounds.php @@ -193,8 +193,8 @@ loadingRecords: ' ', processing: 'Please Wait...', zeroRecords: 'Nothing found', - search: 'Quick Search:', - searchPlaceholder: 'Name..', + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/list_inventory_compounds_data.php', diff --git a/pages/customers.php b/pages/customers.php index 9879ea29..05a10ca6 100644 --- a/pages/customers.php +++ b/pages/customers.php @@ -118,8 +118,8 @@ loadingRecords: ' ', processing: 'Please Wait...', zeroRecords: 'Nothing found', - search: 'Quick Search:', - searchPlaceholder: 'Name..', + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/list_customer_data.php', diff --git a/pages/editHtmlTmpl.php b/pages/editHtmlTmpl.php index 70569bf8..ecee718d 100644 --- a/pages/editHtmlTmpl.php +++ b/pages/editHtmlTmpl.php @@ -30,9 +30,7 @@
  • Add product concentration
  • Add the IFRA amendment number you use
  • Add the IFRA amendment release date
  • -
  • Add the category class of your product
  • -
  • Add the product type
  • -
  • Add the category class of your product
  • +
  • Add IFRA categories and limits
  • Add materials in formula found under the IFRA scope
  • diff --git a/pages/formula.php b/pages/formula.php index 791b36a4..3eabb5b3 100755 --- a/pages/formula.php +++ b/pages/formula.php @@ -29,7 +29,7 @@ if($form[0]['ingredient']){ $legend = 1; } - +require_once(__ROOT__.'/func/convert_to_decimal_point.php'); ?> @@ -123,46 +123,70 @@
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - +
    + +
    + +
    + + +
    +
    + + %
    -
    - +
    + + +
    + +
    + + +
    +
    + +
    -
    - - + +
    + +
    +
    + + +
    + + -
    -
    -
    - -
    -
    - -
    +
    + + +
    +
    + + +
    +
    + + + +
    +
    diff --git a/pages/getIngInfo.php b/pages/getIngInfo.php deleted file mode 100755 index b56d4b5d..00000000 --- a/pages/getIngInfo.php +++ /dev/null @@ -1,33 +0,0 @@ - \ No newline at end of file diff --git a/pages/lids.php b/pages/lids.php index 0f2f74ba..5b3e62b0 100644 --- a/pages/lids.php +++ b/pages/lids.php @@ -141,8 +141,8 @@ loadingRecords: ' ', processing: 'Please Wait...', zeroRecords: 'Nothing found', - search: 'Quick Search:', - searchPlaceholder: 'Name..', + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/list_lid_data.php', @@ -222,11 +222,11 @@ function style(data, type, row){ function actions(data, type, row){ data = ''; return data; }; diff --git a/pages/listFormulas.php b/pages/listFormulas.php index e4ff875c..25ee3b34 100644 --- a/pages/listFormulas.php +++ b/pages/listFormulas.php @@ -171,8 +171,8 @@ function initTable(tableId, src) { loadingRecords: ' ', processing: '', emptyTable: '
    No formulas yet, click here to add or use the Marketplace to import a demo one
    ', - searchPlaceholder: 'Formula name, or product name...', - search: "Search" + searchPlaceholder: 'Search by formula, or product name...', + search: '' }, order: [0,'asc'], columnDefs: [ @@ -368,7 +368,7 @@ function fActions(data, type, row, meta){ bootbox.dialog({ title: "Confirm formula deletion", - message : '
    WARNING, this action cannot be reverted unless you have a backup.

    Permantly delete '+ $(this).attr('data-name') +' formula?

    ' + + message : '
    WARNING, this action cannot be reverted unless you have a backup.

    Permantly delete '+ $(this).attr('data-name') +' formula?

    ' + '
    ' + ''+ ''+ diff --git a/pages/listIngredients.php b/pages/listIngredients.php index 7c9f4dab..c9c0adf5 100644 --- a/pages/listIngredients.php +++ b/pages/listIngredients.php @@ -386,7 +386,11 @@ className : "btn-secondary", bootbox.dialog({ title: "Confirm ingredient deletion", - message : 'Permanently delete '+ ing.Name +' and its data?', + message : '
    WARNING, this action cannot be reverted.

    Permantly delete '+ ing.Name +' and its data?

    ' + + '
    ' + + ''+ + ''+ + '
    ', buttons :{ main: { label : "Delete", @@ -399,6 +403,7 @@ className : "btn-danger", data: { ingredient: "delete", ing_id: ing.ID, + forceDelIng: $("#forceDelIng").is(':checked') }, dataType: 'json', success: function (data) { diff --git a/pages/makeFormula.php b/pages/makeFormula.php index 30023fb9..9748f356 100644 --- a/pages/makeFormula.php +++ b/pages/makeFormula.php @@ -172,8 +172,8 @@ loadingRecords: ' ', processing: 'Please Wait...', zeroRecords: 'No pending ingredients found', - search: 'Quick Search:', - searchPlaceholder: 'Ingredient..', + search: '', + searchPlaceholder: 'Search by ingredient...', }, ajax: { url: '/core/pending_formulas_data.php?meta=0&fid=' + fid, diff --git a/pages/operations.php b/pages/operations.php index 465b07cd..72d65dd2 100644 --- a/pages/operations.php +++ b/pages/operations.php @@ -557,10 +557,68 @@ mysqli_query($conn, "TRUNCATE IFRALibrary"); foreach ($data['IFRALibrary'] as $d ){ - - $s = mysqli_query($conn, "INSERT INTO `IFRALibrary` (`ifra_key`,`image`,`amendment`,`prev_pub`,`last_pub`,`deadline_existing`,`deadline_new`,`name`,`cas`,`cas_comment`,`synonyms`,`formula`,`flavor_use`,`prohibited_notes`,`restricted_photo_notes`,`restricted_notes`,`specified_notes`,`type`,`risk`,`contrib_others`,`contrib_others_notes`,`cat1`,`cat2`,`cat3`,`cat4`,`cat5A`,`cat5B`,`cat5C`,`cat5D`,`cat6`,`cat7A`,`cat7B`,`cat8`,`cat9`,`cat10A`,`cat10B`,`cat11A`,`cat11B`,`cat12`) VALUES ('".$d['ifra_key']."','".$d['image']."','".$d['amendment']."','".$d['prev_pub']."','".$d['last_pub']."','".$d['deadline_existing']."','".$d['deadline_new']."','".$d['name']."','".$d['cas']."','".$d['cas_comment']."','".$d['synonyms']."','".$d['formula']."','".$d['flavor_use']."','".$d['prohibited_notes']."','".$d['restricted_photo_notes']."','".$d['restricted_notes']."','".$d['specified_notes']."','".$d['type']."','".$d['risk']."','".$d['contrib_others']."','".$d['contrib_others_notes']."','".$d['cat1']."','".$d['cat2']."','".$d['cat3']."','".$d['cat4']."','".$d['cat5A']."','".$d['cat5B']."','".$d['cat5C']."','".$d['cat5D']."','".$d['cat6']."','".$d['cat7A']."','".$d['cat7B']."','".$d['cat8']."','".$d['cat9']."','".$d['cat10A']."','".$d['cat10B']."','".$d['cat11A']."','".$d['cat11B']."','".$d['cat12']."') "); - - } + $ifra_key = mysqli_real_escape_string($conn, $d['ifra_key']); + $image = mysqli_real_escape_string($conn, $d['image']); + $amendment = mysqli_real_escape_string($conn, $d['amendment']); + $prev_pub = mysqli_real_escape_string($conn, $d['prev_pub']); + $last_pub = mysqli_real_escape_string($conn, $d['last_pub']); + $deadline_existing = mysqli_real_escape_string($conn, $d['deadline_existing']); + $deadline_new = mysqli_real_escape_string($conn, $d['deadline_new']); + $name = mysqli_real_escape_string($conn, $d['name']); + $cas = mysqli_real_escape_string($conn, $d['cas']); + $cas_comment = mysqli_real_escape_string($conn, $d['cas_comment']); + $synonyms = mysqli_real_escape_string($conn, $d['synonyms']); + $formula = mysqli_real_escape_string($conn, $d['formula']); + $flavor_use = mysqli_real_escape_string($conn, $d['flavor_use']); + $prohibited_notes = mysqli_real_escape_string($conn, $d['prohibited_notes']); + $restricted_photo_notes = mysqli_real_escape_string($conn, $d['restricted_photo_notes']); + $restricted_notes = mysqli_real_escape_string($conn, $d['restricted_notes']); + $specified_notes = mysqli_real_escape_string($conn, $d['specified_notes']); + $type = mysqli_real_escape_string($conn, $d['type']); + $risk = mysqli_real_escape_string($conn, $d['risk']); + $contrib_others = mysqli_real_escape_string($conn, $d['contrib_others']); + $contrib_others_notes = mysqli_real_escape_string($conn, $d['contrib_others_notes']); + + $cat1 = isset($d['cat1']) && $d['cat1'] !== '' ? floatval($d['cat1']) : 100; + $cat2 = isset($d['cat2']) && $d['cat2'] !== '' ? floatval($d['cat2']) : 100; + $cat3 = isset($d['cat3']) && $d['cat3'] !== '' ? floatval($d['cat3']) : 100; + $cat4 = isset($d['cat4']) && $d['cat4'] !== '' ? floatval($d['cat4']) : 100; + $cat5A = isset($d['cat5A']) && $d['cat5A'] !== '' ? floatval($d['cat5A']) : 100; + $cat5B = isset($d['cat5B']) && $d['cat5B'] !== '' ? floatval($d['cat5B']) : 100; + $cat5C = isset($d['cat5C']) && $d['cat5C'] !== '' ? floatval($d['cat5C']) : 100; + $cat5D = isset($d['cat5D']) && $d['cat5D'] !== '' ? floatval($d['cat5D']) : 100; + $cat6 = isset($d['cat6']) && $d['cat6'] !== '' ? floatval($d['cat6']) : 100; + $cat7A = isset($d['cat7A']) && $d['cat7A'] !== '' ? floatval($d['cat7A']) : 100; + $cat7B = isset($d['cat7B']) && $d['cat7B'] !== '' ? floatval($d['cat7B']) : 100; + $cat8 = isset($d['cat8']) && $d['cat8'] !== '' ? floatval($d['cat8']) : 100; + $cat9 = isset($d['cat9']) && $d['cat9'] !== '' ? floatval($d['cat9']) : 100; + $cat10A = isset($d['cat10A']) && $d['cat10A'] !== '' ? floatval($d['cat10A']) : 100; + $cat10B = isset($d['cat10B']) && $d['cat10B'] !== '' ? floatval($d['cat10B']) : 100; + $cat11A = isset($d['cat11A']) && $d['cat11A'] !== '' ? floatval($d['cat11A']) : 100; + $cat11B = isset($d['cat11B']) && $d['cat11B'] !== '' ? floatval($d['cat11B']) : 100; + $cat12 = isset($d['cat12']) && $d['cat12'] !== '' ? floatval($d['cat12']) : 100; + + $s = mysqli_query($conn, " + INSERT INTO `IFRALibrary` ( + `ifra_key`, `image`, `amendment`, `prev_pub`, `last_pub`, + `deadline_existing`, `deadline_new`, `name`, `cas`, `cas_comment`, + `synonyms`, `formula`, `flavor_use`, `prohibited_notes`, `restricted_photo_notes`, + `restricted_notes`, `specified_notes`, `type`, `risk`, `contrib_others`, + `contrib_others_notes`, `cat1`, `cat2`, `cat3`, `cat4`, `cat5A`, + `cat5B`, `cat5C`, `cat5D`, `cat6`, `cat7A`, `cat7B`, `cat8`, `cat9`, + `cat10A`, `cat10B`, `cat11A`, `cat11B`, `cat12` + ) VALUES ( + '$ifra_key', '$image', '$amendment', '$prev_pub', '$last_pub', + '$deadline_existing', '$deadline_new', '$name', '$cas', '$cas_comment', + '$synonyms', '$formula', '$flavor_use', '$prohibited_notes', '$restricted_photo_notes', + '$restricted_notes', '$specified_notes', '$type', '$risk', '$contrib_others', + '$contrib_others_notes', $cat1, $cat2, $cat3, $cat4, $cat5A, + $cat5B, $cat5C, $cat5D, $cat6, $cat7A, $cat7B, $cat8, $cat9, + $cat10A, $cat10B, $cat11A, $cat11B, $cat12 + ) + "); + } + if($s){ $result['success'] = "Import complete"; diff --git a/pages/scheduledFormulas.php b/pages/scheduledFormulas.php index 0445b3b2..a47d4d36 100644 --- a/pages/scheduledFormulas.php +++ b/pages/scheduledFormulas.php @@ -68,8 +68,8 @@ function extrasShow() { loadingRecords: ' ', processing: 'Please Wait...', zeroRecords: 'No scheduled to make formulas found', - search: 'Quick Search:', - searchPlaceholder: 'Name..', + search: '', + searchPlaceholder: 'Search by formula name...', }, ajax: { url: '/core/pending_formulas_data.php?meta=1', diff --git a/pages/update_data.php b/pages/update_data.php index a20dfe6e..8a019f32 100755 --- a/pages/update_data.php +++ b/pages/update_data.php @@ -1466,9 +1466,15 @@ function formatVal($num){ $id = mysqli_real_escape_string($conn, $_POST['ing_id']); $ing = mysqli_fetch_array(mysqli_query($conn, "SELECT name FROM ingredients WHERE id = '$id'")); - if(mysqli_num_rows(mysqli_query($conn, "SELECT ingredient FROM formulas WHERE ingredient = '".$ing['name']."'"))){ - $response["error"] = ''.$ing['name'].' is in use by at least one formula and cannot be removed!
    '; - }elseif(mysqli_query($conn, "DELETE FROM ingredients WHERE id = '$id'")){ + if($_POST['forceDelIng'] == "false"){ + + if(mysqli_num_rows(mysqli_query($conn, "SELECT ingredient FROM formulas WHERE ingredient = '".$ing['name']."'"))){ + $response["error"] = ''.$ing['name'].' is in use by at least one formula and cannot be removed!
    '; + echo json_encode($response); + return; + } + } + if(mysqli_query($conn, "DELETE FROM ingredients WHERE id = '$id'")){ mysqli_query($conn,"DELETE FROM ingredient_compounds WHERE ing = '".$ing['name']."'"); $response["success"] = 'Ingredient '.$ing['name'].' removed from the database!'; } diff --git a/pages/views/IFRA/genIFRAdoc.php b/pages/views/IFRA/genIFRAdoc.php index 73e49e50..9ad81bfd 100644 --- a/pages/views/IFRA/genIFRAdoc.php +++ b/pages/views/IFRA/genIFRAdoc.php @@ -21,12 +21,12 @@ $defPercentage = $settings['defPercentage']; if(!mysqli_num_rows(mysqli_query($conn, "SELECT id FROM IFRALibrary"))){ - echo '
    You need to import the IFRA xls first
    '; + echo '
    You need to import the IFRA xls first
    '; return; } if(!mysqli_num_rows(mysqli_query($conn, "SELECT id FROM templates"))){ - echo '
    You need to add an IFRA template first
    '; + echo '
    You need to add an IFRA template first
    '; return; } @@ -46,12 +46,12 @@ $mg = mysqli_fetch_assoc(mysqli_query($conn, "SELECT SUM(quantity) AS total_mg FROM formulas WHERE fid = '$fid'")); $new_conc = $bottle/100*$type; - +/* if(validateFormula($fid, $bottle, $new_conc, $mg['total_mg'], $defCatClass, $settings['qStep']) !== 0){ - echo '
    Your formula contains materials not compatible with IFRA standards
    '; + echo '
    Your formula contains materials not compatible with IFRA standards
    '; return; } - +*/ if ( empty($settings['brandLogo']) ){ $logo = "/img/logo.png"; @@ -59,65 +59,128 @@ $logo = $settings['brandLogo']; } if ( empty($settings['brandName']) || empty($settings['brandAddress']) || empty($settings['brandEmail']) || empty($settings['brandPhone']) ){ - echo '
    Missing brand info, please update your brand details in settings page first
    '; + echo '
    Missing brand info, please update your brand details in settings page first
    '; return; } if ( empty($customers['name']) || empty($customers['address']) || empty($customers['email']) ){ - echo '
    Missing customers info, please update your customers details in customers page first
    '; + echo '
    Missing customers info, please update your customers details in customers page first
    '; return; } -$tmpl = mysqli_fetch_array(mysqli_query($conn,"SELECT name,content FROM templates WHERE id = '".$_POST['template']."'")); - -$search = array('%LOGO%','%BRAND_NAME%','%BRAND_ADDRESS%','%BRAND_EMAIL%','%BRAND_PHONE%','%CUSTOMER_NAME%','%CUSTOMER_ADDRESS%','%CUSTOMER_EMAIL%','%CUSTOMER_WEB%','%PRODUCT_NAME%','%PRODUCT_SIZE%','%PRODUCT_CONCENTRATION%','%IFRA_AMENDMENT%','%IFRA_AMENDMENT_DATE%','%PRODUCT_CAT_CLASS%','%PRODUCT_TYPE%','%CURRENT_DATE%'); +$template_id = mysqli_real_escape_string($conn, $_POST['template']); +$template_query = "SELECT name, content FROM templates WHERE id = '$template_id'"; +$template_result = mysqli_query($conn, $template_query); +$tmpl = mysqli_fetch_array($template_result); + +$search = array( + '%LOGO%', '%BRAND_NAME%', '%BRAND_ADDRESS%', '%BRAND_EMAIL%', '%BRAND_PHONE%', + '%CUSTOMER_NAME%', '%CUSTOMER_ADDRESS%', '%CUSTOMER_EMAIL%', '%CUSTOMER_WEB%', + '%PRODUCT_NAME%', '%PRODUCT_SIZE%', '%PRODUCT_CONCENTRATION%', + '%IFRA_AMENDMENT%', '%IFRA_AMENDMENT_DATE%', '%CURRENT_DATE%' +); + +$replace = array( + $logo, + $settings['brandName'], $settings['brandAddress'], $settings['brandEmail'], + $settings['brandPhone'], $customers['name'], $customers['address'], + $customers['email'], $customers['web'], $meta['product_name'], $bottle, + $type, getIFRAMeta('MAX(amendment)', $conn), + getIFRAMeta('MAX(last_pub)', $conn), date('d/M/Y') +); + +$categories = []; +$cats_q = mysqli_query($conn, "SELECT name, description FROM IFRACategories"); +while ($category = mysqli_fetch_array($cats_q)) { + $categories[] = $category; +} -$replace = array($logo, $settings['brandName'], $settings['brandAddress'], $settings['brandEmail'], $settings['brandPhone'], $customers['name'],$customers['address'],$customers['email'],$customers['web'],$meta['product_name'],$bottle,$type,getIFRAMeta('MAX(amendment)',$conn),getIFRAMeta('MAX(last_pub)',$conn),strtoupper($defCatClass),$type,date('d/M/Y')); -$formula_q = mysqli_query($conn, "SELECT ingredient,quantity,concentration FROM formulas WHERE fid = '$fid'"); -while ($formula = mysqli_fetch_array($formula_q)){ - $form[] = $formula; +$formulas = []; +$formula_q = mysqli_query($conn, "SELECT ingredient, quantity, concentration,exclude_from_calculation FROM formulas WHERE fid = '" . mysqli_real_escape_string($conn, $fid) . "'"); +while ($formula = mysqli_fetch_array($formula_q)) { + $formulas[] = $formula; } - -foreach ($form as $formula){ - $cas = mysqli_fetch_array(mysqli_query($conn, "SELECT cas,$defCatClass FROM ingredients WHERE name = '".$formula['ingredient']."'")); - if ($cas['cas']){ - $q2 = mysqli_query($conn, "SELECT DISTINCT name,$defCatClass,risk,type,cas FROM IFRALibrary WHERE name LIKE '".$formula['ingredient']."' OR cas = '".$cas['cas']."' GROUP BY name"); - - while($ifra = mysqli_fetch_array($q2)){ - $new_quantity = $formula['quantity']/$mg['total_mg']*$new_conc; - $conc = $new_quantity/$bottle * 100; - $conc_p = number_format($formula['concentration'] / 100 * $conc, $settings['qStep']); - - if($settings['multi_dim_perc'] == '1'){ - $conc_p += multi_dim_perc($conn, $form, $cas['cas'], $settings['qStep'], $settings['defPercentage'])[$cas['cas']]; - } - - $x .=' - '.$ifra['name'].' - '.$ifra['cas'].' - '.$ifra[$defCatClass].' - '.number_format($conc_p, 4).' - '.$ifra['risk'].' - '; - - } + +$x = ''; + +foreach ($formulas as $formula) { + if ( $formulas['exclude_from_calculation'] != 1 ){ + $mg['total_mg'] += $formulas['quantity']; } + $ingredient = mysqli_real_escape_string($conn, $formula['ingredient']); + $cas = mysqli_fetch_array(mysqli_query($conn, "SELECT cas, $defCatClass FROM ingredients WHERE name = '$ingredient'")); + + if ($cas['cas']) { + $q2 = mysqli_query($conn, "SELECT DISTINCT name, $defCatClass, risk, type, cas FROM IFRALibrary WHERE name LIKE '$ingredient' OR cas = '" . $cas['cas'] . "' GROUP BY name"); + + while ($ifra = mysqli_fetch_array($q2)) { + $new_quantity = $formula['quantity'] / $mg['total_mg'] * $new_conc; + $conc = $new_quantity / $bottle * 100; + $conc_p = number_format($formula['concentration'] / 100 * $conc, $settings['qStep']); + + if ($settings['multi_dim_perc'] == '1') { + $multi_dim = multi_dim_perc($conn, $formulas, $cas['cas'], $settings['qStep'], $settings['defPercentage']); + $conc_p += $multi_dim[$cas['cas']]; + } + + $x .= ' + ' . htmlspecialchars($ifra['name']) . ' + ' . htmlspecialchars($ifra['cas']) . ' + ' . htmlspecialchars($ifra[$defCatClass]) . ' + ' . number_format($conc_p, $settings['qStep']) . ' + ' . htmlspecialchars($ifra['risk']) . ' + '; + } + } + + $qCMP = mysqli_query($conn, "SELECT ingredient_compounds.ing, ingredient_compounds.name, ingredient_compounds.cas, ingredient_compounds.$defPercentage, IFRALibrary.risk, IFRALibrary.$defCatClass + FROM ingredient_compounds, IFRALibrary + WHERE ingredient_compounds.ing = '$ingredient' + AND toDeclare = '1' + AND IFRALibrary.name = ingredient_compounds.name + GROUP BY name"); + + while ($cmp = mysqli_fetch_array($qCMP)) { + $x .= ' + ' . htmlspecialchars($cmp['name']) . ' + ' . htmlspecialchars($cmp['cas']) . ' + ' . htmlspecialchars($cmp[$defCatClass]) . ' + ' . number_format($cmp['percentage'] / 100 * $formula['quantity'] / $mg['total_mg'] * $new_conc / 100 * $bottle, $settings['qStep']) . ' + ' . htmlspecialchars($cmp['risk']) . ' + '; + } +} - if($qCMP = mysqli_query($conn, "SELECT ingredient_compounds.ing, ingredient_compounds.name, ingredient_compounds.cas, ingredient_compounds.$defPercentage, IFRALibrary.risk, IFRALibrary.$defCatClass FROM ingredient_compounds, IFRALibrary WHERE ingredient_compounds.ing = '".$formula['ingredient']."' AND toDeclare = '1' AND IFRALibrary.name = ingredient_compounds.name GROUP BY name ")){ - while($cmp = mysqli_fetch_array($qCMP)){ - $x .=' - '.$cmp['name'].' - '.$cmp['cas'].' - '.$cmp[$defCatClass].' - '.number_format($cmp['percentage']/100*$formula['quantity']/$mg['total_mg']*$new_conc/100*$bottle, 4).' - '.$cmp['risk'].' - '; - } - - } +$f = ''; +foreach ($categories as $cat) { + $lastValAccepted = null; + $catname = 'cat'.$cat['name']; + for ($c = 1; $c <= 100; $c++) { + + $result = validateFormula($fid, 100, $c, $mg['total_mg'], $catname, $settings['qStep'], 1); + + if ($result === 0) { + $lastValAccepted = $c; + } else { + break; + } + } + + if ($lastValAccepted !== null) { + $m[$catname] = $lastValAccepted; + } else { + $m[$catname] = '-'; + } + $f .= ' + Cat' . htmlspecialchars($cat['name']) . ' + ' . htmlspecialchars($cat['description']) . ' + '.$m[$catname].' + '; } -$contents = str_replace( $search, $replace, preg_replace('#(%IFRA_MATERIALS_LIST%)#ms', $x, $tmpl['content']) ); + +$contents = str_replace($search, $replace, preg_replace('#(%IFRA_MATERIALS_LIST%)#ms', $x, preg_replace('#(%IFRA_CAT_LIST%)#ms', $f, $tmpl['content']))); + echo $contents; ?> diff --git a/pages/views/formula/analysis.php b/pages/views/formula/analysis.php index 6f1e41ae..c7d813de 100644 --- a/pages/views/formula/analysis.php +++ b/pages/views/formula/analysis.php @@ -54,7 +54,8 @@ loadingRecords: ' ', processing: 'Loading...', emptyTable: 'No sub materials found', - search: 'Search:' + search: '', + searchPlaceholder: 'Search by ingredient...', }, ajax: { url: '/core/list_formula_analysis_data.php', diff --git a/pages/views/formula/attachments.php b/pages/views/formula/attachments.php index ada5d3ba..440818bd 100644 --- a/pages/views/formula/attachments.php +++ b/pages/views/formula/attachments.php @@ -48,7 +48,8 @@ loadingRecords: ' ', processing: 'Loading...', emptyTable: 'No attachments found.', - search: 'Search:' + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/list_formula_attachments_data.php', diff --git a/pages/views/formula/batches.php b/pages/views/formula/batches.php index 82458013..3420f333 100644 --- a/pages/views/formula/batches.php +++ b/pages/views/formula/batches.php @@ -46,8 +46,8 @@ loadingRecords: ' ', processing: '
    Please Wait...', zeroRecords: 'Nothing found', - search: 'Quick Search:', - searchPlaceholder: 'Batch ID..', + search: '', + searchPlaceholder: 'Search by Batch ID or Product name...', }, ajax: { url: '/core/list_batch_data.php', @@ -99,18 +99,18 @@ function batchID(data, type, row){ if(row.pdf){ var data = '' + row.id + '' }else{ - var data = ''; + var data = ''+ row.id +''; } return data; }; function actions(data, type, row){ - if(row.pdf){ - var data = '' - }else{ - var data = ''; - } - return data; + data = ''; + return data; }; @@ -122,7 +122,7 @@ function extrasShow() { }; function reload_data() { - $('#tdDataBatches').DataTable().ajax.reload(null, true); + $('#tdDataBatches').DataTable().ajax.reload(null, false); }; $('#tdDataBatches').on('click', '[id*=batch_del]', function () { diff --git a/pages/views/formula/finishedProduct.php b/pages/views/formula/finishedProduct.php index ace6d440..61e85514 100644 --- a/pages/views/formula/finishedProduct.php +++ b/pages/views/formula/finishedProduct.php @@ -145,7 +145,7 @@
    @@ -400,6 +400,10 @@ function createAlertBox(response) { $('#generateDoc').click(function() { $('.modal-IFRA-body-data').html('loading'); + $("#generateDoc").prop("disabled", true); + $('#generateDoc').append(''); + $("#template").prop("disabled", true); + $("#customer").prop("disabled", true); $.ajax({ type: 'POST', url: '/pages/views/IFRA/genIFRAdoc.php', @@ -414,11 +418,19 @@ function createAlertBox(response) { success: function(data) { $('.modal-IFRA-body-data').html(data); $('#ifraDocModal').modal('show'); + $("#generateDoc").prop("disabled", false); + $("#generateDoc span").remove(); + $("#template").prop("disabled", false); + $("#customer").prop("disabled", false); }, - error:function(err){ - data = '
    Unable to generate data
    '; + error:function(xhr, status, error){ + data = '
    Unable to generate data, ' + error + '
    '; $('.modal-IFRA-body-data').html(data); $('#ifraDocModal').modal('show'); + $("#generateDoc").prop("disabled", false); + $("#generateDoc span").remove(); + $("#template").prop("disabled", false); + $("#customer").prop("disabled", false); } }) }); diff --git a/pages/views/formula/replacements.php b/pages/views/formula/replacements.php index 785e7502..6d807362 100644 --- a/pages/views/formula/replacements.php +++ b/pages/views/formula/replacements.php @@ -35,15 +35,16 @@ loadingRecords: ' ', processing: 'Loading...', emptyTable: 'No replacements found.', - search: 'Search:' + search: '', + searchPlaceholder: 'Search by ingredient...', }, ajax: { url: '/core/list_ing_rep_data.php', type: 'POST', data: { - fid: '', - view: 'formula', - }, + fid: '', + view: 'formula', + }, }, columns: [ { data : 'ing_name', title: 'Main Ingredient', render: repName, name: 'main_ing' }, diff --git a/pages/views/formula/timeline.php b/pages/views/formula/timeline.php index e5cb72f8..c9de2cba 100644 --- a/pages/views/formula/timeline.php +++ b/pages/views/formula/timeline.php @@ -27,13 +27,14 @@ loadingRecords: ' ', processing: 'Loading...', emptyTable: 'No data found.', - search: 'Search:' + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/formula_timeline_data.php', type: 'GET', data: { - id: '', + id: '', }, }, columns: [ diff --git a/pages/views/formula/viewFormula.php b/pages/views/formula/viewFormula.php index 0daf62c7..3d88ae5b 100644 --- a/pages/views/formula/viewFormula.php +++ b/pages/views/formula/viewFormula.php @@ -64,8 +64,8 @@ @@ -141,8 +141,8 @@ loadingRecords: ' ', processing: 'Blending...', emptyTable: '
    Empty formula. Please add ingredients.
    ', - search: "Search in formula:", - searchPlaceholder: "CAS, Ingredient, etc.." + search: '', + searchPlaceholder: "Search by CAS, Ingredient..." }, ajax: { url: '/core/full_formula_data.php', @@ -967,8 +967,11 @@ function ingActions(data, type, row, meta){
    - - + +
    + + +
    @@ -979,8 +982,7 @@ function ingActions(data, type, row, meta){
    -
    - Auto adjust the total quantity by increasing or decreasing quantity from the selected solvent if enough is available.
    +
    Auto adjust the total quantity by increasing or decreasing quantity from the selected solvent if enough is available.
    For example, if you add 1 more to the current ingredient, the selected solvent's quantity will be deducted by 1 equally.
    @@ -1006,8 +1008,7 @@ function ingActions(data, type, row, meta){ -
    - You can merge 's quantity with another material in the formula. Use this method if the materials are similar. Please note, this action cannot be reverted, and the quantity will be added to the target ingredient's quantity. +
    You can merge 's quantity with another material in the formula. Use this method if the materials are similar. Please note, this action cannot be reverted, and the quantity will be added to the target ingredient's quantity.
    @@ -1036,7 +1037,7 @@ function ingActions(data, type, row, meta){
    -
    Replace
    with another ingredient, quantity and dilution values will not be affected.
    +
    Replace
    with another ingredient, quantity and dilution values will not be affected.
    Replace
    with:

    diff --git a/pages/views/ingredients/compos.php b/pages/views/ingredients/compos.php index 1d42824e..66003904 100644 --- a/pages/views/ingredients/compos.php +++ b/pages/views/ingredients/compos.php @@ -68,7 +68,8 @@ loadingRecords: ' ', processing: 'Loading...', emptyTable: 'No compositions added yet.', - search: 'Search:' + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/list_ing_compos_data.php?id=' @@ -169,7 +170,12 @@ function cmpDeclare(data, type, row){ }; function cmpActions(data, type, row){ - return ''; + data = '

    '; + return data; }; $('#tdCompositions').editable({ diff --git a/pages/views/ingredients/ingDocuments.php b/pages/views/ingredients/ingDocuments.php index 078a62fe..57b3c385 100644 --- a/pages/views/ingredients/ingDocuments.php +++ b/pages/views/ingredients/ingDocuments.php @@ -48,7 +48,8 @@ loadingRecords: ' ', processing: 'Loading...', emptyTable: 'No documents added yet.', - search: 'Search:' + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/list_ing_doc_data.php?id=' }, columns: [ @@ -77,7 +78,12 @@ function dNotes(data, type, row){ }; function dActions(data, type, row){ - return '' + data = ''; + return data; }; $('#tdIngDocs').editable({ diff --git a/pages/views/ingredients/ingSuppliers.php b/pages/views/ingredients/ingSuppliers.php index 5a1b6df2..ab735825 100644 --- a/pages/views/ingredients/ingSuppliers.php +++ b/pages/views/ingredients/ingSuppliers.php @@ -103,7 +103,8 @@ loadingRecords: ' ', processing: 'Loading...', emptyTable: 'No suppliers added yet.', - search: 'Search:' + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/list_ing_suppliers_data.php?id=' }, columns: [ diff --git a/pages/views/ingredients/mgmGeneral.php b/pages/views/ingredients/mgmGeneral.php index 1c616d00..edff7c5a 100644 --- a/pages/views/ingredients/mgmGeneral.php +++ b/pages/views/ingredients/mgmGeneral.php @@ -56,8 +56,11 @@
    - - + +
    + + % +
    diff --git a/pages/views/ingredients/repData.php b/pages/views/ingredients/repData.php index 5e40a207..e3e82898 100644 --- a/pages/views/ingredients/repData.php +++ b/pages/views/ingredients/repData.php @@ -53,7 +53,8 @@ loadingRecords: ' ', processing: 'Loading...', emptyTable: 'No replacements added yet.', - search: 'Search:' + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/list_ing_rep_data.php', @@ -138,7 +139,13 @@ function repNotes(data, type, row){ } function repActions(data, type, row){ - return ''; + //return ''; + data = ''; + return data; } diff --git a/pages/views/ingredients/synonyms.php b/pages/views/ingredients/synonyms.php index 6b7ae4d3..010fe7f4 100644 --- a/pages/views/ingredients/synonyms.php +++ b/pages/views/ingredients/synonyms.php @@ -53,7 +53,8 @@ loadingRecords: ' ', processing: 'Loading...', emptyTable: 'No synonyms added yet.', - search: 'Search:' + search: '', + searchPlaceholder: 'Search by name...', }, ajax: { url: '/core/list_ing_synonyms_data.php?id=' }, columns: [ @@ -78,7 +79,12 @@ function synSource(data, type, row){ function synActions(data, type, row){ - return ''; + data = ''; + return data; }; $('#tdSynonyms').editable({ diff --git a/pages/views/ingredients/usageData.php b/pages/views/ingredients/usageData.php index eca7adb6..bb4d8a78 100644 --- a/pages/views/ingredients/usageData.php +++ b/pages/views/ingredients/usageData.php @@ -96,16 +96,19 @@
    class="" > @@ -113,7 +116,7 @@
    - Cat %: + Cat +
    - + - + + % +

    -

    +