*/}}
Browse Source

elarp initial

YimingWu 10 months ago
commit
231f3b5546
5 changed files with 598 additions and 0 deletions
  1. 4 0
      .gitignore
  2. 42 0
      CMakeLists.txt
  3. 43 0
      elarp.c
  4. 114 0
      elarp.h
  5. 395 0
      elarp_operations.c

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+build/*
+.vscode/
+screenshots/
+*.udf

+ 42 - 0
CMakeLists.txt

@@ -0,0 +1,42 @@
+cmake_minimum_required(VERSION 3.1)
+project(elarp)
+
+IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+  SET(CMAKE_INSTALL_PREFIX $ENV{HOME}/elarp CACHE PATH "Where to install eLaRP" FORCE)
+ENDIF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+
+set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ".")
+include (InstallRequiredSystemLibraries)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR})
+
+set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-no-pie")
+set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} "-no-pie")
+
+find_package(lagui REQUIRED)
+find_package(PNG REQUIRED)
+
+add_compile_options("$<$<C_COMPILER_ID:MSVC>:/std:c11>")
+add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
+add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
+
+add_definitions(-w)
+
+include_directories(
+    ${LAGUI_INCLUDE_DIRS_ALL}
+)
+
+file(GLOB_RECURSE elarpFiles 
+    elarp_operations.c
+    elarp.c
+)
+
+
+add_executable(elarp ${elarpFiles})
+
+target_link_options(elarp PUBLIC "-Wl,--start-group")
+
+target_link_libraries(elarp
+    ${LAGUI_SHARED_LIBS}
+    ${PNG_LIBRARY}
+)

+ 43 - 0
elarp.c

@@ -0,0 +1,43 @@
+/*
+* eLaRP: A small ERP utility program
+* Copyright (C) 2024 Wu Yiming
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "elarp.h"
+
+int main(int argc, char *argv[]){
+    laGetReady();
+
+    erpRegisterEverything();
+    erpInit();
+
+    laRefreshUDFRegistries();
+
+    laEnsureUserPreferences();
+    
+    //laAddRootDBInst("basket");
+
+    if(!MAIN.Windows.pFirst){
+        laWindow* w = laDesignWindow(-1,-1,600,600);
+
+        laLayout* l = laDesignLayout(w, "Default Layout");
+        laBlock* b = l->FirstBlock;
+        laPanel* p=laCreatePanel(b, "erp_panel_orders");
+        laStartWindow(w);
+    }
+
+    laMainLoop();
+}

+ 114 - 0
elarp.h

@@ -0,0 +1,114 @@
+/*
+* eLaRP: A small ERP utility program
+* Copyright (C) 2024 Wu Yiming
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "la_5.h"
+extern LA MAIN;
+
+STRUCTURE(ERPCustomer){
+    laListItem Item;
+    laSafeString* Nickname;
+    laSafeString* Name;
+    laSafeString* Country;
+    laSafeString* State;
+    laSafeString* City;
+    laSafeString* Address;
+    laSafeString* PostalCode;
+    laSafeString* Tel;
+    laSafeString* EMail;
+};
+
+STRUCTURE(ERPProduct){
+    laListItem Item;
+    laSafeString* Name;
+    laListHandle AvailableOptions;
+    int Price;
+};
+
+STRUCTURE(ERPProductOption){
+    laListItem Item;
+    ERPProduct* Parent;
+    laSafeString* Name;
+    laListHandle Values;
+};
+
+STRUCTURE(ERPProductOptionValue){
+    laListItem Item;
+    ERPProductOption* Parent;
+    laSafeString* Name;
+    int PriceOffset;
+};
+
+STRUCTURE(ERPOrder){
+    laListItem Item;
+    ERPCustomer* Customer;
+    laListHandle ProductEntries;
+    laListHandle Packages;
+    laSafeString* Comments;
+    laSafeString* AdminComments;
+    int State;
+    int Paid;
+    int Discount;
+};
+
+STRUCTURE(ERPProductEntry){
+    laListItem Item;
+    ERPOrder* Parent;
+    ERPProduct* Product;
+    laListHandle Options;
+    int Amount;
+};
+
+STRUCTURE(ERPProductEntryOption){
+    laListItem Item;
+    ERPProductEntry* Parent;
+    ERPProductOption* Option;
+    ERPProductOptionValue* Value;
+};
+
+STRUCTURE(ERPPackage){
+    laListItem Item;
+    laSafeString* Tracking;
+    laListHandle IncludedProducts;
+};
+
+#define ERP_ORDER_STATE_PENDING_PACKAGING 0
+#define ERP_ORDER_STATE_PENDING_LABEL     1
+#define ERP_ORDER_STATE_PENDING_SHIPPING  2
+#define ERP_ORDER_STATE_SHIPPED           3
+#define ERP_ORDER_STATE_ARRIVED           4
+#define ERP_ORDER_STATE_IN_PERSON         5
+#define ERP_ORDER_STATE_NEED_INFO         127
+
+STRUCTURE(ERPMain){
+    void* _PAD;
+
+    ERPOrder* CurrentOrder;
+    ERPOrder* CurrentProduct;
+    ERPOrder* CurrentCustomer;
+
+    laListHandle Orders;
+    laListHandle Products;
+    laListHandle Customers;
+
+    laListHandle PinnedOrders;
+};
+
+extern ERPMain* erp;
+
+void erpRegisterEverything();
+void erpInit();

+ 395 - 0
elarp_operations.c

@@ -0,0 +1,395 @@
+/*
+* eLaRP: A small ERP utility program
+* Copyright (C) 2024 Wu Yiming
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "elarp.h"
+
+ERPMain* erp;
+
+void* erpget_erp(void* unused1, void* unused2){
+    return erp;
+}
+
+void erpui_CustomerListItem(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laUiItem* r=laBeginRow(uil,c,0,0);
+    laShowItemFull(uil,c,This,"nickname",LA_WIDGET_STRING_PLAIN,0,0,0);
+    laEndRow(uil,r);
+}
+void erpui_CustomerItem(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil),*cl,*cr; laSplitColumn(uil,c,0.5); cl=laLeftColumn(c,10);cr=laRightColumn(c,0);
+    
+    laShowItemFull(uil,c,This,"nickname",0,0,0,0);
+    laShowLabel(uil,c,"Details:",0,0);
+    laShowLabel(uil,cl,"Name:",0,0); laShowItemFull(uil,cr,This,"name",0,0,0,0);
+    laShowLabel(uil,cl,"Country:",0,0); laShowItemFull(uil,cr,This,"country",0,0,0,0);
+    laShowLabel(uil,cl,"State:",0,0); laShowItemFull(uil,cr,This,"state",0,0,0,0);
+    laShowLabel(uil,cl,"City:",0,0); laShowItemFull(uil,cr,This,"city",0,0,0,0);
+    laShowLabel(uil,cl,"Address:",0,0); laShowItemFull(uil,cr,This,"address",0,0,0,0);
+    laShowLabel(uil,cl,"Postal Code:",0,0); laShowItemFull(uil,cr,This,"postal_code",0,0,0,0);
+    laShowLabel(uil,cl,"Telephone:",0,0); laShowItemFull(uil,cr,This,"tel",0,0,0,0);
+    laShowLabel(uil,cl,"E-Mail:",0,0); laShowItemFull(uil,cr,This,"email",0,0,0,0);
+
+}
+void erpui_CustomersPanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil),*cl,*cr; laSplitColumn(uil,c,0.5); cl=laLeftColumn(c,20);cr=laRightColumn(c,0);
+
+    laUiItem* r=laBeginRow(uil,c,0,0);
+    laShowItem(uil,c,0,"ERP_new_customer");
+    laEndRow(uil,r);
+
+    laUiItem* gr=laMakeEmptyGroup(uil,cl,0,0); laUiList* gu=gr->Page; laColumn* gc=laFirstColumn(gu);
+    gu->HeightCoeff=-1; gr->Flags|=LA_UI_FLAGS_NO_DECAL;
+    laUiItem* ui=laShowItemFull(gu,gc,0,"erp.customers",0,0,erpui_CustomerListItem,0);
+
+    gr=laMakeEmptyGroup(uil,cr,0,0); gu=gr->Page; gc=laFirstColumn(gu);
+    gu->HeightCoeff=-1; gr->Flags|=LA_UI_FLAGS_NO_DECAL;
+    ui=laShowItemFull(gu,gc,0,"erp.current_customer",0,0,erpui_CustomerItem,0);
+    ui->Flags|=LA_UI_COLLECTION_NO_HIGHLIGHT;
+}
+
+void erpui_ProductOptionValueItem(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil), *cl,*cr; laSplitColumn(uil,c,0.2); cl=laLeftColumn(c,1);cr=laRightColumn(c,0);
+    laUiItem* r=laBeginRow(uil,cr,1,1);
+    laShowItemFull(uil,cr,This,"name",0,0,0,0);
+    laShowItem(uil,cr,This,"price_offset");
+    laEndRow(uil,r);
+    laShowItem(uil,cl,This,"__move");
+}
+void erpui_ProductOptionItem(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil), *cl,*cr; laSplitColumn(uil,c,0.2); cl=laLeftColumn(c,1);cr=laRightColumn(c,0);
+    laUiItem* r=laBeginRow(uil,cr,0,0);
+    laUiItem* b=laOnConditionToggle(uil,c,0,0,0,0,0); 
+    laShowItemFull(uil,cr,This,"name",0,0,0,0);
+    laShowSeparator(uil,cr)->Expand=1;
+    laShowItem(uil,cr,This,"new_value");
+    laEndRow(uil,r);
+    laShowItemFull(uil,cr,This,"values",0,0,erpui_ProductOptionValueItem,0);
+    laElse(uil,b);
+    laShowItemFull(uil,cr,This,"name",0,0,0,0);
+    laEndRow(uil,r);
+    laEndCondition(uil,b);
+    laShowItem(uil,cl,This,"__move");
+}
+void erpui_ProductListItem(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laUiItem* r=laBeginRow(uil,c,0,0);
+    laShowItemFull(uil,c,This,"price",LA_WIDGET_INT_PLAIN,0,0,0)->Flags|=LA_UI_FLAGS_NO_LABEL;
+    laShowItemFull(uil,c,This,"name",LA_WIDGET_STRING_PLAIN,0,0,0);
+    laEndRow(uil,r);
+}
+void erpui_ProductItem(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    
+    laShowItemFull(uil,c,This,"name",0,0,0,0);
+    laUiItem* r=laBeginRow(uil,c,0,0);
+    laShowItemFull(uil,c,This,"price",0,0,0,0);
+    laEndRow(uil,r);
+
+    r=laBeginRow(uil,c,0,0);
+    laUiItem* b=laOnConditionToggle(uil,c,0,0,0,0,0); strSafeSet(&b->ExtraInstructions,"text=Options"); b->Expand=1;
+    laShowItem(uil,c,This,"new_option");
+    laEndRow(uil,r);
+    laShowItemFull(uil,c,This,"available_options",0,0,erpui_ProductOptionItem,0);
+    laElse(uil,b);
+    laEndRow(uil,r);
+}
+void erpui_ProductsPanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil), *cl,*cr; laSplitColumn(uil,c,0.5); cl=laLeftColumn(c,20);cr=laRightColumn(c,0);
+
+    laUiItem* r=laBeginRow(uil,c,0,0);
+    laShowItem(uil,c,0,"ERP_new_product");
+    laEndRow(uil,r);
+
+    laUiItem* gr=laMakeEmptyGroup(uil,cl,0,0); laUiList* gu=gr->Page; laColumn* gc=laFirstColumn(gu);
+    gu->HeightCoeff=-1; gr->Flags|=LA_UI_FLAGS_NO_DECAL;
+    laUiItem* ui=laShowItemFull(gu,gc,0,"erp.products",0,0,erpui_ProductListItem,0);
+
+    gr=laMakeEmptyGroup(uil,cr,0,0); gu=gr->Page; gc=laFirstColumn(gu);
+    gu->HeightCoeff=-1; gr->Flags|=LA_UI_FLAGS_NO_DECAL;
+    ui=laShowItemFull(gu,gc,0,"erp.current_product",0,0,erpui_ProductItem,0);
+    ui->Flags|=LA_UI_COLLECTION_NO_HIGHLIGHT;
+}
+void erpui_ProductEntryOptionItem(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil), *cl,*cr; laSplitColumn(uil,c,0.2); cl=laLeftColumn(c,1);cr=laRightColumn(c,0);
+    laColumn* crl,*crr; laSplitColumn(uil,cr,0.8); crl=laLeftColumn(cr,0);crr=laRightColumn(cr,1);
+    laColumn* crll,*crlr; laSplitColumn(uil,crl,0.5); crll=laLeftColumn(crl,0);crlr=laRightColumn(crl,0);
+    
+    laShowItem(uil,cl,This,"__move");
+    laShowItemFull(uil,crll,This,"option",0,0,0,0);
+    laShowItemFull(uil,crlr,This,"value",0,0,0,0);
+}
+void erpui_ProductEntryItem(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil), *cl,*cr; laSplitColumn(uil,c,0.2); cl=laLeftColumn(c,1);cr=laRightColumn(c,0);
+    laColumn* crl,*crr; laSplitColumn(uil,cr,0.2); crl=laLeftColumn(cr,1);crr=laRightColumn(cr,0);
+    laColumn* crrl,*crrr; laSplitColumn(uil,crr,0.5); crrl=laLeftColumn(crr,0);crrr=laRightColumn(crr,7);
+    
+    laShowItem(uil,cl,This,"__move");
+    laUiItem* b=laOnConditionToggle(uil,crl,0,0,0,0,0); 
+    laShowItemFull(uil,crrl,This,"product",0,0,erpui_ProductListItem,0);
+    laShowItem(uil,crrr,This,"new_option");
+    laShowItemFull(uil,crr,This,"options",0,0,erpui_ProductEntryOptionItem,0);
+    laElse(uil,b);
+    laShowItemFull(uil,crr,This,"product",0,0,erpui_ProductListItem,0);
+    laEndCondition(uil,b);
+}
+void erpui_OrderListItem(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laShowItemFull(uil,c,This,"__uid",LA_WIDGET_STRING_PLAIN,0,0,0);
+}
+void erpui_OrderItem(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    
+    laUiItem* r=laBeginRow(uil,c,0,0);
+    laShowItemFull(uil,c,This,"state",0,0,0,0);
+    laShowSeparator(uil,c)->Expand=1;
+    laShowItemFull(uil,c,This,"paid",0,0,0,0);
+    laShowSeparator(uil,c);
+    laShowItemFull(uil,c,This,"discount",0,0,0,0);
+    laEndRow(uil,r);
+
+    laShowItemFull(uil,c,This,"customer",LA_WIDGET_COLLECTION_SELECTOR,0,0,0);
+
+    r=laBeginRow(uil,c,0,0);
+    laUiItem* b=laOnConditionToggle(uil,c,0,0,0,0,0); strSafeSet(&b->ExtraInstructions,"text=Product Entries"); b->Expand=1;
+    laShowItem(uil,c,This,"new_item");
+    laEndRow(uil,r);
+    laShowItemFull(uil,c,This,"product_entries",0,0,erpui_ProductEntryItem,0);
+    laElse(uil,b);
+    laEndRow(uil,r);
+    laEndCondition(uil,b);
+
+    laShowItemFull(uil,c,This,"packages",0,0,0,0);
+    laShowItemFull(uil,c,This,"comments",LA_WIDGET_STRING_MULTI,0,0,0)->Extra->HeightCoeff=2;
+    laShowSeparator(uil,c);
+    laShowItemFull(uil,c,This,"admin_comments",LA_WIDGET_STRING_MULTI,0,0,0)->Extra->HeightCoeff=2;
+}
+void erpui_OrdersPanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil), *cl,*cr; laSplitColumn(uil,c,0.5); cl=laLeftColumn(c,20);cr=laRightColumn(c,0);
+
+    laUiItem* r=laBeginRow(uil,c,0,0);
+    laShowItem(uil,c,0,"ERP_new_order");
+    laEndRow(uil,r);
+
+    laUiItem* gr=laMakeEmptyGroup(uil,cl,0,0); laUiList* gu=gr->Page; laColumn* gc=laFirstColumn(gu);
+    gu->HeightCoeff=-1; gr->Flags|=LA_UI_FLAGS_NO_DECAL;
+    laUiItem* ui=laShowItemFull(gu,gc,0,"erp.orders",0,0,erpui_OrderListItem,0);
+
+    gr=laMakeEmptyGroup(uil,cr,0,0); gu=gr->Page; gc=laFirstColumn(gu);
+    gu->HeightCoeff=-1; gr->Flags|=LA_UI_FLAGS_NO_DECAL;
+    ui=laShowItemFull(gu,gc,0,"erp.current_order",0,0,erpui_OrderItem,0);
+    ui->Flags|=LA_UI_COLLECTION_NO_HIGHLIGHT;
+}
+
+ERPOrder* erpNewOrder(){
+    ERPOrder* o=memAcquireHyper(sizeof(ERPOrder)); lstPushItem(&erp->Orders,o);
+    laNotifyUsers("erp.orders"); laNotifyUsers("erp.current_order"); memAssignRef(erp,&erp->CurrentOrder,o);
+    return o;
+}
+ERPCustomer* erpNewCustomer(){
+    ERPCustomer* c=memAcquireHyper(sizeof(ERPCustomer)); lstPushItem(&erp->Customers,c);
+    strSafeSet(&c->Nickname,"New Customer");
+    laNotifyUsers("erp.customers"); laNotifyUsers("erp.current_customer"); memAssignRef(erp,&erp->CurrentCustomer,c);
+    return c;
+}
+ERPProduct* erpNewProduct(){
+    ERPProduct* p=memAcquireHyper(sizeof(ERPProduct)); lstPushItem(&erp->Products,p);
+    strSafeSet(&p->Name,"New Product");
+    laNotifyUsers("erp.products"); laNotifyUsers("erp.current_product"); memAssignRef(erp,&erp->CurrentProduct,p);
+    return p;
+}
+ERPProductOption* erpNewProductOption(ERPProduct* p){ if(!p) return 0;
+    ERPProductOption* po=memAcquire(sizeof(ERPProductOption)); lstAppendItem(&p->AvailableOptions,po);
+    strSafeSet(&po->Name,"New Option"); laNotifyInstanceUsers(p); memAssignRef(po,&po->Parent,p);
+    return po;
+}
+ERPProductOptionValue* erpNewProductOptionValue(ERPProductOption* po){ if(!po) return 0;
+    ERPProductOptionValue* pov=memAcquire(sizeof(ERPProductOptionValue)); lstAppendItem(&po->Values,pov);
+    strSafeSet(&pov->Name,"New Value"); laNotifyInstanceUsers(po); memAssignRef(pov,&pov->Parent,po);
+    return pov;
+}
+ERPProductEntry* erpNewProductEntry(ERPOrder* o){ if(!o) return 0;
+    ERPProductEntry* pe=memAcquire(sizeof(ERPProductEntry)); lstAppendItem(&o->ProductEntries,pe);
+    laNotifyInstanceUsers(o); memAssignRef(pe,&pe->Parent,o);
+    return pe;
+}
+ERPProductEntry* erpNewProductEntryOption(ERPProductEntry* pe){ if(!pe) return 0;
+    ERPProductEntryOption* peo=memAcquire(sizeof(ERPProductEntryOption)); lstAppendItem(&pe->Options,peo);
+    laNotifyInstanceUsers(pe); memAssignRef(peo,&peo->Parent,pe);
+    return peo;
+}
+
+int erpinv_NewOrder(laOperator* a, laEvent* e){ erpNewOrder(); 
+    laRecordDifferences(0,"erp.orders");laRecordDifferences(0,"erp.current_order");laPushDifferences("New Order",0);
+    return LA_FINISHED;
+}
+int erpinv_NewCustomer(laOperator* a, laEvent* e){ erpNewCustomer(); 
+    laRecordDifferences(0,"erp.customer");laRecordDifferences(0,"erp.customer");laPushDifferences("New Customer",0);
+    return LA_FINISHED;
+}
+int erpinv_NewProduct(laOperator* a, laEvent* e){ erpNewProduct();
+    laRecordDifferences(0,"erp.product");laRecordDifferences(0,"erp.product");laPushDifferences("New Product",0);
+    return LA_FINISHED;
+}
+int erpinv_NewProductOption(laOperator* a, laEvent* e){
+    ERPProduct* p=a->This?a->This->EndInstance:0; if(!p){ return LA_FINISHED; }
+    ERPProductOption* po=erpNewProductOption(p); if(po){ laRecordInstanceDifferences(p,"erp_product");laPushDifferences("New Product Option",0); }
+    return LA_FINISHED;
+}
+int erpinv_NewProductOptionValue(laOperator* a, laEvent* e){
+    ERPProductOption* po=a->This?a->This->EndInstance:0; if(!po){ return LA_FINISHED; }
+    ERPProductOptionValue* pov=erpNewProductOptionValue(po); if(pov){ laRecordInstanceDifferences(po,"erp_product_option"); laPushDifferences("New Product Value",0); }
+    return LA_FINISHED;
+}
+int erpinv_NewProductEntry(laOperator* a, laEvent* e){
+    ERPOrder* o=a->This?a->This->EndInstance:0; if(!o){ return LA_FINISHED; }
+    ERPProductEntry* pe=erpNewProductEntry(o); if(pe){ laRecordInstanceDifferences(o,"erp_order");laPushDifferences("New Product Entry",0); }
+    return LA_FINISHED;
+}
+int erpinv_NewProductEntryOption(laOperator* a, laEvent* e){
+    ERPProductEntry* pe=a->This?a->This->EndInstance:0; if(!pe){ return LA_FINISHED; }
+    ERPProductEntry* peo=erpNewProductEntryOption(pe); if(peo){ laRecordInstanceDifferences(pe,"erp_product_entry");laPushDifferences("New Product Entry Option",0); }
+    return LA_FINISHED;
+}
+
+void erpset_ProductOptionMove(ERPProductOption* po, int move){
+    if(move<0 && po->Item.pPrev){ lstMoveUp(&po->Parent->AvailableOptions, po); laNotifyInstanceUsers(po->Parent); }
+    elif(move>0 && po->Item.pNext){ lstMoveDown(&po->Parent->AvailableOptions, po); laNotifyInstanceUsers(po->Parent); }
+}
+void erpset_ProductOptionValueMove(ERPProductOptionValue* pov, int move){
+    if(move<0 && pov->Item.pPrev){ lstMoveUp(&pov->Parent->Values, pov); laNotifyInstanceUsers(pov->Parent); }
+    elif(move>0 && pov->Item.pNext){ lstMoveDown(&pov->Parent->Values, pov); laNotifyInstanceUsers(pov->Parent); }
+}
+void erpset_ProductEntryMove(ERPProductEntry* pe, int move){
+    if(move<0 && pe->Item.pPrev){ lstMoveUp(&pe->Parent->ProductEntries, pe); laNotifyInstanceUsers(pe->Parent); }
+    elif(move>0 && pe->Item.pNext){ lstMoveDown(&pe->Parent->ProductEntries, pe); laNotifyInstanceUsers(pe->Parent); }
+}
+void erpset_ProductEntryOptionMove(ERPProductEntryOption* peo, int move){
+    if(move<0 && peo->Item.pPrev){ lstMoveUp(&peo->Parent->Options, peo); laNotifyInstanceUsers(peo->Parent); }
+    elif(move>0 && peo->Item.pNext){ lstMoveDown(&peo->Parent->Options, peo); laNotifyInstanceUsers(peo->Parent); }
+}
+void* erpget_FirstCustomer(void* unused1,void* unused2){ return erp->Customers.pFirst; }
+void* erpget_FirstProduct(void* unused1,void* unused2){ return erp->Products.pFirst; }
+void* erpget_FirstProductEntryOption(ERPProductEntryOption* peo,void* unused2){ return peo->Parent&&peo->Parent->Product?peo->Parent->Product->AvailableOptions.pFirst:0; }
+void* erpget_FirstProductEntryOptionValue(ERPProductEntryOption* peo,void* unused2){ return peo->Option?peo->Option->Values.pFirst:0; }
+
+void erpRegisterEverything(){
+    laPropContainer* pc; laKeyMapper* km; laProp* p; laSubProp* sp; laOperatorType* at;
+    
+    laCreateOperatorType("ERP_new_order","New Order","New order",0,0,0,erpinv_NewOrder,0,'+',0);
+    laCreateOperatorType("ERP_new_customer","New Customer","New customer",0,0,0,erpinv_NewCustomer,0,'+',0);
+    laCreateOperatorType("ERP_new_product","New Product","New product",0,0,0,erpinv_NewProduct,0,'+',0);
+    laCreateOperatorType("ERP_new_product_option","New Option","New option",0,0,0,erpinv_NewProductOption,0,'+',0);
+    laCreateOperatorType("ERP_new_product_option_value","New Value","New value",0,0,0,erpinv_NewProductOptionValue,0,'+',0);
+    laCreateOperatorType("ERP_new_product_entry","New Product Entry","New product entry",0,0,0,erpinv_NewProductEntry,0,'+',0);
+    laCreateOperatorType("ERP_new_product_entry_option","New Option","New option",0,0,0,erpinv_NewProductEntryOption,0,'+',0);
+
+    pc=laDefineRoot();
+
+    laAddSubGroup(pc,"erp","ERP","ERP main","erp_main",0,0,0,-1,erpget_erp,0,0,0,0,0,0,LA_UDF_SINGLE);
+
+    pc=laAddPropertyContainer("erp_main","ERP MAIN","ERP root structure",0,0,sizeof(ERPMain),0,0,1);
+    laAddSubGroup(pc,"current_order","Current Order","Current Order","erp_order",0,0,0,offsetof(ERPMain,CurrentOrder),0,0,0,0,0,0,0,LA_UDF_REFER);
+    laAddSubGroup(pc,"current_product","Current Product","Current Product","erp_product",0,0,0,offsetof(ERPMain,CurrentProduct),0,0,0,0,0,0,0,LA_UDF_REFER);
+    laAddSubGroup(pc,"current_customer","Current Customer","Current Customer","erp_customer",0,0,0,offsetof(ERPMain,CurrentCustomer),0,0,0,0,0,0,0,LA_UDF_REFER);
+    laAddSubGroup(pc,"orders","Orders","All Orders","erp_order",0,0,0,offsetof(ERPMain,CurrentOrder),0,0,0,0,0,0,offsetof(ERPMain,Orders),0);
+    laAddSubGroup(pc,"products","Products","All Products","erp_product",0,0,0,offsetof(ERPMain,CurrentProduct),0,0,0,0,0,0,offsetof(ERPMain,Products),0);
+    laAddSubGroup(pc,"customers","Customers","All Customers","erp_customer",0,0,0,offsetof(ERPMain,CurrentCustomer),0,0,0,0,0,0,offsetof(ERPMain,Customers),0);
+    
+    pc=laAddPropertyContainer("erp_customer","ERP Customer","ERP customer",0,0,sizeof(ERPCustomer),0,0,2);
+    laAddStringProperty(pc,"nickname","Nickname","Nickname",0,0,0,0,1,offsetof(ERPCustomer,Nickname),0,0,0,0,LA_AS_IDENTIFIER);
+    laAddStringProperty(pc,"name","Name","Name",0,0,0,0,1,offsetof(ERPCustomer,Name),0,0,0,0,0);
+    laAddStringProperty(pc,"country","Country","Country",0,0,0,0,1,offsetof(ERPCustomer,Country),0,0,0,0,0);
+    laAddStringProperty(pc,"state","State","State",0,0,0,0,1,offsetof(ERPCustomer,State),0,0,0,0,0);
+    laAddStringProperty(pc,"city","City","City",0,0,0,0,1,offsetof(ERPCustomer,City),0,0,0,0,0);
+    laAddStringProperty(pc,"address","Address","Address",0,0,0,0,1,offsetof(ERPCustomer,Address),0,0,0,0,0);
+    laAddStringProperty(pc,"postal_code","PostalCode","PostalCode",0,0,0,0,1,offsetof(ERPCustomer,PostalCode),0,0,0,0,0);
+    laAddStringProperty(pc,"tel","Tel","Tel",0,0,0,0,1,offsetof(ERPCustomer,Tel),0,0,0,0,0);
+    laAddStringProperty(pc,"email","EMail","EMail",0,0,0,0,1,offsetof(ERPCustomer,EMail),0,0,0,0,0);
+
+    pc=laAddPropertyContainer("erp_order","ERP Order","ERP order",0,0,sizeof(ERPOrder),0,0,2);
+    laAddSubGroup(pc,"customer","Customer","Customer","erp_customer",0,LA_WIDGET_ENUM_SELECTOR,erpui_CustomerListItem,offsetof(ERPOrder,Customer),erpget_FirstCustomer,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
+    laAddSubGroup(pc,"product_entries","Product Entries","Product entries","erp_product_entry",0,0,0,-1,0,0,0,0,0,0,offsetof(ERPOrder,ProductEntries),0);
+    laAddSubGroup(pc,"packages","Packages","Packages","erp_package",0,0,0,-1,0,0,0,0,0,0,offsetof(ERPOrder,Packages),0);
+    laAddStringProperty(pc,"comments","Comments","Comments about the order",0,0,0,0,1,offsetof(ERPOrder,Comments),0,0,0,0,0);
+    laAddStringProperty(pc,"admin_comments","Admin comments","Admin comments about the order",0,0,0,0,1,offsetof(ERPOrder,AdminComments),0,0,0,0,0);
+    p=laAddEnumProperty(pc,"state","State","State of the order",0,0,0,0,0,offsetof(ERPOrder,State),0,0,0,0,0,0,0,0,0,0);
+    laAddEnumItemAs(p,"PENDING_PACKAGING","Pending Packaging","Pending packaging",ERP_ORDER_STATE_PENDING_PACKAGING,0);
+    laAddEnumItemAs(p,"PENDING_LABEL","Pending Label","Pending label",ERP_ORDER_STATE_PENDING_LABEL,0);
+    laAddEnumItemAs(p,"PENDING_SHIPPING","Pending Shipping","Pending shipping",ERP_ORDER_STATE_PENDING_SHIPPING,0);
+    laAddEnumItemAs(p,"SHIPPED","Shipped","Shipped",ERP_ORDER_STATE_SHIPPED,0);
+    laAddEnumItemAs(p,"ARRIVED","Arrived","Arrived",ERP_ORDER_STATE_ARRIVED,0);
+    laAddEnumItemAs(p,"IN_PERSON","In Person","Pending packaging",ERP_ORDER_STATE_IN_PERSON,0);
+    laAddEnumItemAs(p,"NEEDS_INFO","Needs Information","Pending packaging",ERP_ORDER_STATE_NEED_INFO,0);
+    laAddIntProperty(pc,"paid","Paid","Paid",0,0,0,0,0,0,0,0,offsetof(ERPOrder,Paid),0,0,0,0,0,0,0,0,0,0,0);
+    laAddIntProperty(pc,"discount","Discount","Discount",0,0,0,0,0,0,0,0,offsetof(ERPOrder,Discount),0,0,0,0,0,0,0,0,0,0,0);
+    laAddOperatorProperty(pc,"new_item","New Item","New product entry","ERP_new_product_entry",0,0);
+
+    pc=laAddPropertyContainer("erp_package","ERP Package","ERP package",0,0,sizeof(ERPPackage),0,0,1);
+    laAddStringProperty(pc,"tracking","Tracking","Tracking number",0,0,0,0,1,offsetof(ERPPackage,Tracking),0,0,0,0,LA_AS_IDENTIFIER);
+    laAddSubGroup(pc,"included_products","Included Products","Included products","erp_included_product",0,0,0,-1,0,0,0,0,0,0,offsetof(ERPPackage,IncludedProducts),0);
+
+    pc=laAddPropertyContainer("erp_included_product","ERP Product Entry Option","ERP Product Entry Option",0,0,sizeof(laListItemPointer),0,0,1);
+    laAddSubGroup(pc,"product","Product","Prduct","erp_product_entry",0,0,0,offsetof(laListItemPointer,p),0,0,0,0,0,0,0,LA_UDF_REFER);
+
+    pc=laAddPropertyContainer("erp_product_entry","ERP Product Entry","ERP Product Entry",0,0,sizeof(ERPProductEntry),0,0,1);
+    laAddIntProperty(pc,"__move","Move","Move slider",LA_WIDGET_HEIGHT_ADJUSTER,0,0,0,0,0,0,0,0,0,erpset_ProductEntryMove,0,0,0,0,0,0,0,0,LA_UDF_IGNORE);
+    laAddSubGroup(pc,"parent","Parent","Parent order","erp_order",0,0,0,offsetof(ERPProductEntry,Parent),0,0,0,0,0,0,0,LA_UDF_REFER|LA_READ_ONLY);
+    laAddSubGroup(pc,"product","Product","Product","erp_product",0,LA_WIDGET_COLLECTION_SELECTOR,erpui_ProductListItem,offsetof(ERPProductEntry,Product),erpget_FirstProduct,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
+    laAddSubGroup(pc,"options","Options","Product Options","erp_product_entry_option",0,0,0,-1,0,0,0,0,0,0,offsetof(ERPProductEntry,Options),0);
+    laAddIntProperty(pc,"amount","Amount","Amount",0,0,0,0,0,0,0,0,offsetof(ERPProductEntry,Amount),0,0,0,0,0,0,0,0,0,0,0);
+    laAddOperatorProperty(pc,"new_option","New Option","New product option","ERP_new_product_entry_option",0,0);
+
+    pc=laAddPropertyContainer("erp_product_entry_option","ERP Product Entry Option","ERP Product Entry Option",0,0,sizeof(ERPProductEntryOption),0,0,1);
+    laAddIntProperty(pc,"__move","Move","Move slider",LA_WIDGET_HEIGHT_ADJUSTER,0,0,0,0,0,0,0,0,0,erpset_ProductEntryOptionMove,0,0,0,0,0,0,0,0,LA_UDF_IGNORE);
+    laAddSubGroup(pc,"parent","Parent","Parent entry","erp_product_entry",0,0,0,offsetof(ERPProductEntryOption,Parent),0,0,0,0,0,0,0,LA_UDF_REFER|LA_READ_ONLY);
+    laAddSubGroup(pc,"option","Option","Option","erp_product_option",0,LA_WIDGET_COLLECTION_SELECTOR,laui_IdentifierOnly,offsetof(ERPProductEntryOption,Option),erpget_FirstProductEntryOption,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
+    laAddSubGroup(pc,"value","Value","Value","erp_product_option_value",0,LA_WIDGET_COLLECTION_SELECTOR,laui_IdentifierOnly,offsetof(ERPProductEntryOption,Value),erpget_FirstProductEntryOptionValue,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
+    
+    pc=laAddPropertyContainer("erp_product","ERP Product","ERP product",0,0,sizeof(ERPOrder),0,0,2);
+    laAddStringProperty(pc,"name","Name","Name",0,0,0,0,1,offsetof(ERPProduct,Name),0,0,0,0,LA_AS_IDENTIFIER);
+    laAddSubGroup(pc,"available_options","Available Options","Available options","erp_product_option",0,0,0,-1,0,0,0,0,0,0,offsetof(ERPProduct,AvailableOptions),0);
+    laAddIntProperty(pc,"price","Price","Price",0,0,0,0,0,0,0,0,offsetof(ERPProduct,Price),0,0,0,0,0,0,0,0,0,0,0);
+    laAddOperatorProperty(pc,"new_option","New Option","New option for the product","ERP_new_product_option",0,0);
+
+    pc=laAddPropertyContainer("erp_product_option","ERP Product Option","ERP Product Option",0,0,sizeof(ERPProductOption),0,0,1);
+    laAddSubGroup(pc,"parent","Parent","Parent product","erp_product",0,0,0,offsetof(ERPProductOption,Parent),0,0,0,0,0,0,0,LA_UDF_REFER|LA_READ_ONLY);
+    laAddStringProperty(pc,"name","Name","Name",0,0,0,0,1,offsetof(ERPProductOption,Name),0,0,0,0,LA_AS_IDENTIFIER);
+    laAddSubGroup(pc,"values","Values","Values","erp_product_option_value",0,0,0,-1,0,0,0,0,0,0,offsetof(ERPProductOption,Values),0);
+    laAddIntProperty(pc,"__move","Move","Move slider",LA_WIDGET_HEIGHT_ADJUSTER,0,0,0,0,0,0,0,0,0,erpset_ProductOptionMove,0,0,0,0,0,0,0,0,LA_UDF_IGNORE);
+    laAddOperatorProperty(pc,"new_value","New Value","New value for the option","ERP_new_product_option_value",0,0);
+
+    pc=laAddPropertyContainer("erp_product_option_value","ERP Product Option","ERP Product Option Value",0,0,sizeof(ERPProductOptionValue),0,0,1);
+    laAddSubGroup(pc,"parent","Parent","Parent product option","erp_product_option",0,0,0,offsetof(ERPProductOptionValue,Parent),0,0,0,0,0,0,0,LA_UDF_REFER|LA_READ_ONLY);
+    laAddStringProperty(pc,"name","Name","Name",0,0,0,0,1,offsetof(ERPProductOptionValue,Name),0,0,0,0,LA_AS_IDENTIFIER);
+    laAddIntProperty(pc,"__move","Move","Move slider",LA_WIDGET_HEIGHT_ADJUSTER,0,0,0,0,0,0,0,0,0,erpset_ProductOptionValueMove,0,0,0,0,0,0,0,0,LA_UDF_IGNORE);
+    laAddIntProperty(pc,"price_offset","Price Offset","Price offset",0,0,0,0,0,0,0,0,offsetof(ERPProductOptionValue,PriceOffset),0,0,0,0,0,0,0,0,0,0,0);
+
+    laSaveProp("erp.products");
+    laSaveProp("erp.customers");
+    laSaveProp("erp.orders");
+
+    laRegisterUiTemplate("erp_panel_orders","Orders",erpui_OrdersPanel,0,0,0,0,20,20);
+    laRegisterUiTemplate("erp_panel_products","Products",erpui_ProductsPanel,0,0,0,0,20,20);
+    laRegisterUiTemplate("erp_panel_customers","Customers",erpui_CustomersPanel,0,0,0,0,20,20);
+}
+
+void erpInit(){
+    erp=memAcquire(sizeof(ERPMain));
+
+    laAddRootDBInst("erp");
+}