List of commits:
Subject Hash Author Date (UTC)
Added a difference check and refactoring + removal of old elements, Added a config path solution 914c5e14ba2c4051544b68d263726380e0ed4b29 Sebastian 2019-06-05 07:47:24
Fixed a bug that occured when there were 2 locations with the same sku and package and which caused the wrong writing the wrong numbers into the packagelist 75dde60e1034e5b3889b012d995fffbf4d9a296f Sebastian 2019-05-29 09:30:46
Added packlist fill script 8f3849dc0ad5fa4ad5ce9a6732429c6e647a5187 Sebastian 2019-05-10 15:42:12
Included an export to the storage list from Amazon, imporved useability by adding stops 810be612ed40559434e9ddacb904bc4a766b861d Sebastian 2019-05-09 10:24:36
Added a finish process and removed the order process fc33b421234ef320aa5a61f28cfb1e5d815477ca Sebastian 2019-05-08 13:02:21
Added a encoding sniffing to open the files in right encoding on Windows OS machines d0dbe5da1ca5810bfcc240a1e635ce053168b423 Sebastian 2019-05-06 14:54:04
Small usability improvements for the user to detect errors on different OS 2ec13ba3c85207c799eed1cf48ab1230d245a05f Sebastian 2019-05-06 08:25:54
Combined with the picklist creation project 4e5023c3c2bebc53794fe29edbcf9a7be93c826a Sebastian 2019-05-03 11:40:35
Created a building script for Linux based systems and made it possible to use the script from anywhere inside the filesystem e97b391d7b1cda885c99b6b483d414c75b288b75 Sebastian 2019-04-15 15:00:47
Added a working Linux executable Version, together with a different workspace creation process c84d49cf0f99fd3dcb7faabe82c125c8adc970cb Sebastian 2019-04-12 16:53:29
removed unnecessary files from the report and upload folder 94af82cbd9fd7f592fb6b421986c3d900f1bf532 Sebastian 2019-04-05 12:51:43
Added 2 options: a new order import to plenty and a fba shipment creation db5b9c168dabe3d6524b5674dd977044be286e0a Sebastian 2019-04-05 12:48:53
adjusted to another sync format and added a new export format that includes the sale price d33a1909c4f1ff88569509ad4510a5419e323136 Basti 2019-03-22 10:27:22
first Version of the Sync Upload de9ea87dff9ced84f635dd05d5ae039be870ae8a Basti 2019-03-19 16:32:44
First commit including Readme and folder structure 7e77aa7abd6013ce56d6878c7004973e32011a13 Basti 2019-03-19 10:44:36
Commit 914c5e14ba2c4051544b68d263726380e0ed4b29 - Added a difference check and refactoring + removal of old elements, Added a config path solution
Author: Sebastian
Author date (UTC): 2019-06-05 07:47
Committer name: Sebastian
Committer date (UTC): 2019-06-05 07:47
Parent(s): 75dde60e1034e5b3889b012d995fffbf4d9a296f
Signing key:
Tree: 414a172ce30f2e69760a157f6b88adf3aab663fb
File Lines added Lines deleted
inventory_synchronisation.py 60 114
packages/__pycache__/difference.cpython-37.pyc 0 0
packages/__pycache__/picklist_creation.cpython-37.pyc 0 0
packages/__pycache__/plenty_export.cpython-37.pyc 0 0
packages/__pycache__/shipment_finish.cpython-37.pyc 0 0
packages/__pycache__/syncfile.cpython-37.pyc 0 0
packages/difference.py 204 0
packages/picklist_creation.py 6 6
packages/plenty_export.py 42 14
packages/shipment_finish.py 14 10
packages/syncfile.py 22 222
File inventory_synchronisation.py changed (mode: 100644) (index 71babeb..98c7b85)
... ... import sys
4 4 import os import os
5 5 import re import re
6 6 import chardet import chardet
7 from packages.syncfile import importAmazonOrder, writeNewCsv
7 from packages.syncfile import importFBA_Shipment, writeNewCsv
8 8 from packages.shipment_finish import finishShipment from packages.shipment_finish import finishShipment
9 9 from packages.plenty_export import plentyExport from packages.plenty_export import plentyExport
10 10 #from packages.excelfile import createExcelSheet #from packages.excelfile import createExcelSheet
 
... ... def main():
25 25 "encoding":""} "encoding":""}
26 26 # Check if there is already a Report folder for the Amazon report files, if not create a new one. # Check if there is already a Report folder for the Amazon report files, if not create a new one.
27 27 reportpath = os.getcwd() + '/Report' reportpath = os.getcwd() + '/Report'
28 if not os.path.exists(reportpath):
29 while(not(re.match(r'^(y|j)', use_current_dir[0].lower()) or re.match(r'^n',use_current_dir[0].lower()))):
30 use_current_dir = input("Do you want to use the current directory, for the folders and config?")
31 if(use_current_dir[0].lower() == 'y' or use_current_dir[0].lower() == 'j'):
32 os.makedirs(reportpath)
33 if(use_current_dir[0].lower() == 'n'):
34 newpath = askdirectory(title="Choose the directory for the folders")
35 reportpath = newpath + '/Report'
36 try:
37 os.makedirs(reportpath)
38 except FileExistsError:
39 print("The Report folder exists!")
40
41 # Check if there is already a Upload folder if not create a new one.
42 if(newpath):
43 uploadpath = newpath + '/Upload'
28 uploadpath = os.getcwd() + '/Upload'
29 configpath = os.getcwd() + '/config.txt'
30 if( not( os.path.exists(reportpath) ) or not( os.path.exists(uploadpath) ) ):
31 if(not(os.path.isfile(configpath))):
32 while(not(re.match(r'^(y|j)', use_current_dir[0].lower()) or re.match(r'^n',use_current_dir[0].lower()))):
33 use_current_dir = input("Do you want to use the current directory, for the folders and config?")
34 if(use_current_dir[0].lower() == 'y' or use_current_dir[0].lower() == 'j'):
35 if( not os.path.exists(reportpath) ):
36 os.makedirs(reportpath)
37 os.makedirs(uploadpath)
38 else:
39 reportpath = os.getcwd() + '/Report'
40 uploadpath = os.getcwd() + '/Upload'
41 elif(use_current_dir[0].lower() == 'n'):
42 newpath = askdirectory(title="Choose the directory for the folders")
43 reportpath = newpath + '/Report'
44 uploadpath = newpath + '/Upload'
45 else:
46 print("y|n\n")
47
48 with open(configpath , mode = 'w') as config:
49 config.write( "#-Config file for the FBA Shipment Script;\n" )
50 config.write( "#= '#= marks the beginning of a comment', '= splits between name and value';\n" )
51 config.write( "#=location of upload and report folder;\n" )
52 config.write( "upload_folder = {0};\n".format(uploadpath) )
53 config.write( "report_folder = {0};\n".format(reportpath) )
54 else:
55 if(os.path.isfile(configpath)):
56 with open(configpath, mode = 'r') as config:
57 # Take every row into a list rows and strip them of spaces and new lines
58 rows = (row.strip(' ').strip('\n').split(';') for row in config)
59
60 for row in rows:
61 # combine the list to a string and split it at the '='
62 option = "".join(row[0]).split('=')
63
64 if(not(option[0].strip(' ') == '#')):
65 if(option[0].strip(' ') == 'upload_folder'):
66 uploadpath = option[1].strip(' ')
67 if(option[0].strip(' ') == 'report_folder'):
68 reportpath = option[1].strip(' ')
44 69 else: else:
45 uploadpath = os.getcwd() + '/Upload'
46 if not os.path.exists(uploadpath):
47 try:
48 os.makedirs(uploadpath)
49 except FileExistsError:
50 print("The Upload folder exists!")
51
52 # Get the producer list by following the config.txt to the location
53 # Get the plentymarkets id by following the config.txt
54 # If there is no config.txt create a new one
55
56 producer = ''
57 configid = ''
58 configpath = ''
70 if( not( os.path.exists(configpath) ) ):
71 with open(configpath , mode = 'w') as config:
72 config.write( "#-Config file for the FBA Shipment Script;\n" )
73 config.write( "#= '#= marks the beginning of a comment', '= splits between name and value';\n" )
74 config.write( "#=location of upload and report folder;\n" )
75 config.write( "upload_folder = {0};\n".format(uploadpath) )
76 config.write( "report_folder = {0};\n".format(reportpath) )
59 77
60 if(newpath):
61 configpath = newpath + '/config.txt'
62 else:
63 configpath = './config.txt'
64 if(os.path.isfile(configpath)):
65 with open(configpath, mode = 'r') as config:
66 # Take every row into a list rows and strip them of spaces and new lines
67 rows = (row.strip(' ').strip('\n').split(';') for row in config)
68
69 for row in rows:
70 # combine the list to a string and split it at the '='
71 option = "".join(row[0]).split('=')
72
73 if(not(option[0].strip(' ') == '#')):
74 if(option[0].strip(' ') == 'producer_list'):
75 producer = option[1].strip(' ')
76 #if(not( os.path.isfile(producer) )):
77 #print('No valid producer list located!\n')
78 #sys.exit()
79 if(option[0].strip(' ') == 'plenty_id'):
80 configid = int(option[1])
81 # if no producer_list option was set
82 if(not(producer)):
83 print("config reading failed or producer_list not set!\n")
84 print("The producer list needs the header: 'producer', 'adressid', 'customerid', 'firstname', 'lastname'\n")
85 os.system('pause')
86 sys.exit()
87 if(not(configid)):
88 print("config reading failed or plenty_id not valid!\n")
89 os.system('pause')
90 sys.exit()
91 # If there is no config.txt then create a new one
92 # and fill it with the base text and options
93 else:
94 with open(configpath , mode = 'a') as config:
95 config.write( "#-Config file for the order import;\n" )
96 config.write( "#= '#= marks the beginning of a comment', '= splits between name and value';\n" )
97 config.write( "#=provide the position of the producer list file in the format ./'location';\n" )
98 config.write( "producer_list = ;\n" )
99 config.write( "#=Enter your plentymarkets ID here;\n" )
100 config.write( "plenty_id = ;\n" )
101 config.write( "CountryCode = ;\n")
102
103 if(os.path.isfile('./config.txt')):
104 print("\nNew config file created, please fill out the options!\n")
105 os.system('pause')
106 sys.exit()
107 78
108 79 # Ask the user if the task is to import received items at amazon or to # Ask the user if the task is to import received items at amazon or to
109 # create a new order for production
110 80 line = "#" * 70 line = "#" * 70
111 81 altline = "-" * 70 altline = "-" * 70
112 82 print("{0}\n".format(line)) print("{0}\n".format(line))
 
... ... def main():
124 94
125 95 task = '' task = ''
126 96
127 while(not(task.lower() == 'ama' or\
128 task.lower() == 'fin' or\
97 while(not(task.lower() == 'fin' or\
129 98 task.lower() == 'exp' or\ task.lower() == 'exp' or\
130 99 task.lower() == 'fba')): task.lower() == 'fba')):
131 task = input('Do you want to create a new FBA Shipment?(fba)\nDo you want to finish a shipment?(fin)\nDo you want to export the plenty quantity to the storage report?(exp)\nOr do you want to import received items at Amazon (ama)?\n')
132 if(not(task.lower() == 'ama' or task.lower() == 'fin' or task.lower() == 'exp' or task.lower() == 'fba')):
133 print("Please enter either 'fba', 'fin', 'exp' or 'ama'.\n")
134
135 # Set to 0000 because the current state of the script doesnt need the orderid anymore
136 order ='0000'
100 task = input('Do you want to create a new FBA Shipment?(fba)\nDo you want to finish a shipment?(fin)\nOr do you want to export the plenty quantity to the storage report?(exp)\n')
101 if(not(task.lower() == 'fin' or task.lower() == 'exp' or task.lower() == 'fba')):
102 print("Please enter either 'fba', 'fin' or 'exp'.\n")
137 103
138 104 # A filedialog for the export and the report file # A filedialog for the export and the report file
139 105 root = Tk() root = Tk()
 
... ... def main():
146 112 , filetypes=[ ("csv files" ,"*.csv" ) ] ) , filetypes=[ ("csv files" ,"*.csv" ) ] )
147 113 # detect the encoding of the file # detect the encoding of the file
148 114 with open(export_file['filepath'], mode='rb') as item: with open(export_file['filepath'], mode='rb') as item:
149 raw_data = item.read()
115 raw_data = item.read(10000)
150 116 export_file['encoding'] = chardet.detect(raw_data)['encoding'] export_file['encoding'] = chardet.detect(raw_data)['encoding']
151 117
152 # AMAZON RECEIVED PART
153 if(task.lower() == 'ama'):
154 print("Open the report file of the recent delivered FBA packages,\nthis will contain the amount of items that amazon received.\n")
155 print("\n{0}\n".format(line))
156 shipment_file['filepath'] = askopenfilename(initialdir=reportpath
157 , title="FBA-Report"
158 , filetypes=[ ("csv files" ,"*.csv" ) ] )
159 # detect the encoding of the file
160 with open(shipment_file['filepath'], mode='rb') as item:
161 raw_data = item.read()
162 shipment_file['encoding'] = chardet.detect(raw_data)['encoding']
163
164 try:
165 importAmazonOrder(export=export_file, folder=uploadpath, orderid=order,
166 plentyid=configid, report=shipment_file
167 )
168 except Exception as Err:
169 print("Something went terribly wrong...")
170 print(Err)
171
172 118 # FINISH SHIPMENT PART # FINISH SHIPMENT PART
173 119 if(task.lower() == 'fin'): if(task.lower() == 'fin'):
174 120 print("{0}\n".format(line)) print("{0}\n".format(line))
 
... ... def main():
185 131
186 132 try: try:
187 133 plenty_Data = {} plenty_Data = {}
188 plenty_Data = finishShipment(picklist=shipment_file, folder=reportpath)
134 plenty_Data = finishShipment(picklist=shipment_file, folder=reportpath, uploadfolder=uploadpath)
189 135 writeNewCsv(dataset=plenty_Data['data'], writeNewCsv(dataset=plenty_Data['data'],
190 136 path=uploadpath, path=uploadpath,
191 137 header=plenty_Data['fields'], header=plenty_Data['fields'],
 
... ... def main():
209 155
210 156 # detect the encoding of the file # detect the encoding of the file
211 157 with open(shipment_file['filepath'], mode='rb') as item: with open(shipment_file['filepath'], mode='rb') as item:
212 raw_data = item.read()
158 raw_data = item.read(12000)
213 159 shipment_file['encoding'] = chardet.detect(raw_data)['encoding'] shipment_file['encoding'] = chardet.detect(raw_data)['encoding']
214 160
215 importAmazonOrder(export=export_file, reportfolder=reportpath, folder=uploadpath,
216 orderid=order, plentyid=configid, stockreport=shipment_file
161 importFBA_Shipment(export=export_file, reportfolder=reportpath, folder=uploadpath,
162 stockreport=shipment_file
217 163 ) )
218 164
219 165 print("\npress ENTER to continue...") print("\npress ENTER to continue...")
 
... ... def main():
238 184
239 185 # detect the encoding of the file # detect the encoding of the file
240 186 with open(locationexport['filepath'], mode='rb') as item: with open(locationexport['filepath'], mode='rb') as item:
241 raw_data = item.read()
187 raw_data = item.read(10000)
242 188 locationexport['encoding'] = chardet.detect(raw_data)['encoding'] locationexport['encoding'] = chardet.detect(raw_data)['encoding']
243 189
244 plentyExport(plentyexport=locationexport,folder=uploadpath)
190 plentyExport(folder=uploadpath, plentyexport=locationexport)
245 191
246 192 print("\npress ENTER to continue...") print("\npress ENTER to continue...")
247 193 input() input()
File packages/__pycache__/difference.cpython-37.pyc added (mode: 100644) (index 0000000..e4669ac)
File packages/__pycache__/picklist_creation.cpython-37.pyc changed (mode: 100644) (index 1869fd5..708a981)
File packages/__pycache__/plenty_export.cpython-37.pyc changed (mode: 100644) (index 3e934f5..0c48f28)
File packages/__pycache__/shipment_finish.cpython-37.pyc changed (mode: 100644) (index f2b2414..d5753af)
File packages/__pycache__/syncfile.cpython-37.pyc changed (mode: 100644) (index bf76bae..cf76ffb)
File packages/difference.py added (mode: 100644) (index 0000000..ba319d8)
1 import re
2 import csv
3 import sys
4 from packages import syncfile
5
6 # Define and initialize the line separators
7 line = "#"*70
8 altline = "-"*70
9
10 def close_script():
11 print("Press Enter to contine..")
12 input()
13 sys.exit()
14
15
16 def compare_locations(locationlist, folder, pickdata, export='', exportdata='', plenty_file='', plenty_data=''):
17 # Open all three files and save them into a dictionary
18 # compare the quantity of the export to the storage report
19 # and the quantity of the plenty sync with the stock of the
20 # locations before the shipment
21
22 Data = {}
23 error_flag = False
24
25 column_names = ['SKU', 'Barcode', 'Total', 'Total-NEW',
26 'Total-Total-NEW', 'Diff-Picklist', 'export-plenty-diff?',
27 'location-1', 'qty-1', 'qty-1-new',
28 'location-2', 'qty-2', 'qty-2-new',
29 'location-3', 'qty-3', 'qty-3-new',
30 'location-4', 'qty-4', 'qty-4-new',
31 'location-5', 'qty-5', 'qty-5-new',]
32
33 print("\n{0}\n".format(line))
34 # Open the locationlist
35 with open(locationlist['filepath'], mode = 'r', encoding = locationlist['encoding']) as item:
36 reader = csv.DictReader(item, delimiter=';')
37
38 for row in reader:
39 if(not( row['VariationNo'] in [*Data] )):
40 try:
41 values = [row['VariationNo'], row['Barcode'], int( row['Stock'] ), 0,
42 '','','',
43 row['LocationName'], int( row['Stock'] ), 0,
44 '', 0, 0,
45 '', 0, 0,
46 '', 0, 0,
47 '', 0, 0]
48 except Exception as err:
49 print("ERROR at Locationlist values: line : {0}, Error: {1}\n".format(sys.exc_info()[2].tb_lineno, err))
50 close_script()
51
52 try:
53 Data[row['VariationNo']] = dict(zip(column_names, values))
54 except Exception as err:
55 print("ERROR at Locationlist Dict creation: {0}".format(err))
56 else:
57 try:
58 for n in range(5):
59 if(row['LocationName'] == Data[row['VariationNo']]['location-' + str(n+1)]):
60 print("Double location @ {0} for {1}"
61 .format(row['LocationName'], row['VariationNo']))
62 else:
63 if(not( Data[row['VariationNo']]['location-' + str(n+1)] )):
64 Data[row['VariationNo']]['location-' + str(n+1)] = row['LocationName']
65 Data[row['VariationNo']]['qty-' + str(n+1)] = int( row['Stock'] )
66 Data[row['VariationNo']]['Total'] += int( row['Stock'] )
67 break
68 except Exception as err:
69 print("ERROR @ Locationlist add Location: line: {0}, Error: {1}"
70 .format(sys.exc_info()[2].tb_lineno, err))
71
72 if(export):
73 # Open Storage Report export file
74 with open(export['path'], mode = 'r', encoding = export['encoding']) as item:
75 reader = csv.DictReader(item, delimiter=';')
76
77 for row in reader:
78 if(row['SKU'] in [*Data]):
79 total_new = 0
80 try:
81 for n in range(5):
82 if(row['PLACE_' + str(n+1)] == Data[row['SKU']]['location-' + str(n+1)]):
83 Data[row['SKU']]['qty-' + str(n+1) + '-new'] = int( row['QTY_PLACE_' + str(n+1)] )
84 except Exception as err:
85 print("Error @ assigning locations export: line: {0}, error: {1}".format(sys.exc_info()[2].tb_lineno, err))
86 try:
87 for n in range(5):
88 if(Data[row['SKU']]['location-' + str(n+1)]):
89 total_new += int(Data[row['SKU']]['qty-' + str(n+1) + '-new'])
90 Data[row['SKU']]['Total-NEW'] = total_new
91 except Exception as err:
92 print("Error @ adding to Total-NEW: line: {0}, error: {1}".format(sys.exc_info()[2].tb_lineno, err))
93 close_script()
94
95 elif(exportdata):
96 # Open the storage report export dataset
97 for row in exportdata:
98 row = exportdata[row]
99 if(row['SKU'] in [*Data]):
100 total_new = 0
101 try:
102 for n in range(5):
103 if(row['PLACE_' + str(n+1)] == Data[row['SKU']]['location-' + str(n+1)]):
104 Data[row['SKU']]['qty-' + str(n+1) + '-new'] = int( row['QTY_PLACE_' + str(n+1)] )
105 except Exception as err:
106 print("Error @ assigning locations export: line: {0}, error: {1}".format(sys.exc_info()[2].tb_lineno, err))
107 try:
108 for n in range(5):
109 if(Data[row['SKU']]['location-' + str(n+1)]):
110 total_new += int(Data[row['SKU']]['qty-' + str(n+1) + '-new'])
111 Data[row['SKU']]['Total-NEW'] = total_new
112 Data[row['SKU']]['Total-Total-NEW'] = int(Data[row['SKU']]['Total']) - total_new
113 if( Data[row['SKU']]['Total-Total-NEW'] < 0 ):
114 error_flag = True
115 print("Error the new Total Quantity is bigger than the old one!")
116 print("SKU: {0}, Old: {1}, New: {2}, Difference: {3}"
117 .format(row['SKU'], Data[row['SKU']]['Total'], total_new, Data[row['SKU']]['Total-Total-NEW']))
118 except Exception as err:
119 print("Error @ adding to Total-NEW: line: {0}, error: {1}".format(sys.exc_info()[2].tb_lineno, err))
120 close_script()
121
122 else:
123 print("No Export dataset or file recieved, abort function.")
124 close_script()
125
126 if(plenty_file):
127 # Open the plenty_file
128 with open(plenty_file['path'], mode = 'r', encoding = plenty_file['encoding']) as item:
129 reader = csv.DictReader(item, delimiter=';')
130
131 for row in reader:
132 if(row['VariationNo'] in [*Data]):
133 # check every location
134 try:
135 for n in range(5):
136 if(row['LocationName'] == Data[row['VariationNo']]['location-' + str(n+1) ]):
137 if(not( row['Stock'] == Data[row['VariationNo']]['qty-' + str(n+1) + '-new'] )):
138 error_flag = True
139 print("ERROR: Location: {0}, SKU: {1}, QTY_Export: {2}, QTY_PlentyFile: {3}"
140 .format(row['LocationName'], row['VariationNo'],
141 Data[row['VariationNo']]['qty-' + str(n+1) + '-new'],
142 row['Stock']))
143 Data[row['VariationNo']]['export-plenty-diff?'] += '(' + row['LocationName'] + ":" + str( row['Stock'] ) + ":" + row['VariationNo'] + ');'
144 except Exception as err:
145 print("Error @ plentyfile check every location: line: {0}, err: {1}"
146 .format(sys.exc_info()[2].tb_lineno, err))
147 close_script()
148
149 elif(plenty_data):
150 # Open the plenty_dataset
151 for row in plenty_data:
152 row = plenty_data[row]
153 if(row['VariationNo'] in [*Data]):
154 # check every location
155 try:
156 for n in range(5):
157 if(row['LocationName'] == Data[row['VariationNo']]['location-' + str(n+1) ]):
158 if(not( row['Stock'] == Data[row['VariationNo']]['qty-' + str(n+1) + '-new'] )):
159 error_flag = True
160 print("ERROR: Location: {0}, SKU: {1}, QTY_Export: {2}, QTY_PlentyFile: {3}"
161 .format(row['LocationName'], row['VariationNo'],
162 Data[row['VariationNo']]['qty-' + str(n+1) + '-new'],
163 row['Stock']))
164 Data[row['VariationNo']]['export-plenty-diff?'] += '(' + row['LocationName'] + ":" + str( row['Stock'] ) + ":" + row['VariationNo'] + ');'
165 except Exception as err:
166 print("Error @ plentyfile check every location: line: {0}, err: {1}"
167 .format(sys.exc_info()[2].tb_lineno, err))
168 close_script()
169
170 else:
171 print("No Plenty dataset or file recieved, abort function.")
172 close_script()
173
174 print("\n{0}\n".format(altline))
175 error_flag, Data = compare_picklist(dataset=Data, picklist=pickdata)
176 if(error_flag):
177 print("\n{0}\n".format(line))
178 syncfile.writeNewCsv(dataset=Data, path=folder, header=column_names, name='difference')
179 else:
180 print("\nFiles were compared and didn't show any mistakes.\n")
181
182 def compare_picklist(dataset, picklist):
183 # Compare the location difference with the picklist
184 error_flag = False
185
186 for row in picklist:
187 packages = picklist[row]['packages']
188 dataset[row]['Diff-Picklist'] = dataset[row]['Total-Total-NEW']
189 for package in packages:
190 if(row in [*dataset]):
191 for n in range(5):
192 if(packages[package]['location'] == dataset[row]['location-' + str(n+1)]):
193 if(not(( int( dataset[row]['qty-' + str(n+1)] ) - int( dataset[row]['qty-' + str(n+1) + '-new'] ) ) == int( packages[package]['quantity'] ))):
194 error_flag = True
195 print('ERROR picklist diff: sku: {0}, location: {1}, qty-qty_new = {2}, picklist = {3}'
196 .format(row, packages[package]['location'],
197 int( dataset[row]['qty-' + str(n+1)] ) - int( dataset[row]['qty-' + str(n+1) + '-new'] ),
198 packages[package]['quantity']))
199 dataset[row]['Diff-Picklist'] -= int( packages[package]['quantity'] )
200 if(dataset[row]['Diff-Picklist'] != 0):
201 error_flag = True
202 print("Sku: {0}, Diff-Picklist: {1}".format(row, dataset[row]['Diff-Picklist']))
203
204 return error_flag, dataset
File packages/picklist_creation.py changed (mode: 100644) (index 19dd873..332e2e3)
... ... def picklist_creation(shipmentlist, folder, uploadfolder):
45 45 with open(shipmentlist, mode = 'r') as item: with open(shipmentlist, mode = 'r') as item:
46 46 reader = csv.DictReader(item, delimiter=';') reader = csv.DictReader(item, delimiter=';')
47 47
48 number = 0
48 #number = 0
49 49 for row in reader: for row in reader:
50 if(row['Menge']):
51 number += int( row['Menge'] )
52 shipping_items.append(row['Name'])
50 if(row['QTY']):
51 #number += int( row['QTY'] )
52 shipping_items.append(row['SKU'])
53 53
54 54 items_in_skulist = 0 items_in_skulist = 0
55 55 for skulist in locations: for skulist in locations:
 
... ... def picklist_creation(shipmentlist, folder, uploadfolder):
67 67 reader = csv.DictReader(item,delimiter=';') reader = csv.DictReader(item,delimiter=';')
68 68
69 69 for row in reader: for row in reader:
70 if(row['Name']+'+1' in [*Data]):
71 Data[row['Name']+'+1']['Shipment'] = row['Menge']
70 if(row['SKU']+'+1' in [*Data]):
71 Data[row['SKU']+'+1']['Shipment'] = row['QTY']
72 72
73 73 syncfile.writeNewCsv(dataset=Data,path=uploadfolder, header=column_names,name='picklist') syncfile.writeNewCsv(dataset=Data,path=uploadfolder, header=column_names,name='picklist')
74 74
File packages/plenty_export.py changed (mode: 100644) (index 88471e0..d5898d0)
1 1 import csv import csv
2 2 import sys import sys
3 import re
3 4 from tkinter.filedialog import askopenfilename from tkinter.filedialog import askopenfilename
4 5 from packages import syncfile from packages import syncfile
5 6 import datetime import datetime
6 7
7 def plentyExport(plentyexport, folder):
8 def plentyExport(folder, plentyexport, dataset=''):
8 9 today = datetime.datetime.now() today = datetime.datetime.now()
9 10 todaystr = today.strftime("%d-%m-%Y_%H-%M") todaystr = today.strftime("%d-%m-%Y_%H-%M")
10 11
 
... ... def plentyExport(plentyexport, folder):
15 16 "QUANTITY", "QUANTITY",
16 17 "NUMBER_OF_PLACES", "NUMBER_OF_PLACES",
17 18 "QTY_PLACE_1", "QTY_PLACE_1",
19 "PLACE_1",
18 20 "QTY_PLACE_2", "QTY_PLACE_2",
21 "PLACE_2",
19 22 "QTY_PLACE_3", "QTY_PLACE_3",
23 "PLACE_3",
20 24 "QTY_PLACE_4", "QTY_PLACE_4",
21 "QTY_PLACE_5"]
25 "PLACE_4",
26 "QTY_PLACE_5",
27 "PLACE_5"]
22 28
23 29 with open(plentyexport['filepath'], mode = 'r', encoding=plentyexport['encoding']) as item: with open(plentyexport['filepath'], mode = 'r', encoding=plentyexport['encoding']) as item:
24 30 reader = csv.DictReader(item, delimiter = ";") reader = csv.DictReader(item, delimiter = ";")
 
... ... def plentyExport(plentyexport, folder):
36 42 sys.exit() sys.exit()
37 43 try: try:
38 44 values = [row[ "VariationNo" ], values = [row[ "VariationNo" ],
39 row[ "Barcode" ],
40 stock,
41 1,
42 stock,
43 0,
44 0,
45 0,
46 0
47 ]
45 row[ "Barcode" ],
46 stock,
47 1,
48 stock, row['LocationName'],
49 0, '',
50 0, '',
51 0, '',
52 0, ''
53 ]
48 54 except KeyError: except KeyError:
49 55 print("Expected column names: VariationNo, Barcode, Stock\n") print("Expected column names: VariationNo, Barcode, Stock\n")
50 56 print("Got: {0}".format(", ".join([*row]))) print("Got: {0}".format(", ".join([*row])))
 
... ... def plentyExport(plentyexport, folder):
61 67 try: try:
62 68 Data[row['VariationNo']]['QUANTITY'] = int( Data[row['VariationNo']]['QUANTITY'] ) + row['Stock'] Data[row['VariationNo']]['QUANTITY'] = int( Data[row['VariationNo']]['QUANTITY'] ) + row['Stock']
63 69 Data[row['VariationNo']]['QTY_PLACE_' + str(Data[row['VariationNo']]['NUMBER_OF_PLACES'])] = row['Stock'] Data[row['VariationNo']]['QTY_PLACE_' + str(Data[row['VariationNo']]['NUMBER_OF_PLACES'])] = row['Stock']
70 Data[row['VariationNo']]['PLACE_' + str(Data[row['VariationNo']]['NUMBER_OF_PLACES'])] = row['LocationName']
64 71
65 72 except Exception as exc: except Exception as exc:
66 73 Data[row['VariationNo']]['QUANTITY'] = exc Data[row['VariationNo']]['QUANTITY'] = exc
 
... ... def plentyExport(plentyexport, folder):
69 76 input() input()
70 77 sys.exit() sys.exit()
71 78
72 syncfile.writeNewCsv(dataset=Data,path=folder,header=column_names,name="export_" + todaystr)
73 if __name__ == '__main__':
74 main()
79
80 if(dataset):
81 try:
82 for row in dataset:
83 total = 0
84 packages = dataset[row]['packages']
85 for package in packages:
86 for num in range(5):
87 if(packages[package]['location'] == Data[row]['PLACE_' + str(num+1)]):
88 Data[row]['QTY_PLACE_' + str(num+1)] -= int( packages[package]['quantity'] )
89 total += Data[row]['QTY_PLACE_' + str(num+1)]
90 else:
91 total += Data[row]['QTY_PLACE_' + str(num+1)]
92 Data[row]['QUANTITY'] = total
93
94 except Exception as err:
95 print("ERROR @ dataset part @ Lagerbestands abgleich Datei: line: {0}\nError: {1}\n".format(sys.exc_info()[2].tb_lineno, err))
96 print("Press ENTER to continue...")
97 input()
98 sys.exit()
99
100 syncfile.writeNewCsv(dataset=Data,path=folder,header=column_names,name="export_" + todaystr)
101
102 return Data
File packages/shipment_finish.py changed (mode: 100644) (index 3b1da50..a8f15e4)
... ... import csv
10 10 import chardet import chardet
11 11 import datetime import datetime
12 12 from packages import plenty_export from packages import plenty_export
13 from packages import difference
13 14
14 15 # Define and initialize the line seperators # Define and initialize the line seperators
15 16 line = "#"*70 line = "#"*70
 
... ... altline = "-"*70
18 19 # Define a string containing the day+month of the current date # Define a string containing the day+month of the current date
19 20 todaystr = datetime.datetime.now().strftime("%d-%m") todaystr = datetime.datetime.now().strftime("%d-%m")
20 21
21 def finishShipment(picklist, folder):
22 def finishShipment(picklist, folder, uploadfolder):
22 23 # keys: barcode, packages : { package : {qty, location, packet} } # keys: barcode, packages : { package : {qty, location, packet} }
23 24 # for the inboundplan for loop through all packages # for the inboundplan for loop through all packages
24 25
 
... ... def finishShipment(picklist, folder):
105 106 createInboundPlan(dataset=Data, folder=folder) createInboundPlan(dataset=Data, folder=folder)
106 107 #Reducing the storage location quantity in plentymarkets #Reducing the storage location quantity in plentymarkets
107 108 plenty_Data = {} plenty_Data = {}
108 plenty_Data = reducePlentyMarketsQty(dataset=Data, folder=folder)
109 plenty_Data = reducePlentyMarketsQty(dataset=Data, folder=folder, upload=uploadfolder)
109 110 # sort the pickdata into the packaging list from amazon # sort the pickdata into the packaging list from amazon
110 111 sortIntoPackageList(Data, folder) sortIntoPackageList(Data, folder)
111 112
 
... ... def createInboundPlan(dataset, folder):
192 193 print("{0}\n".format(line)) print("{0}\n".format(line))
193 194 print("No file choosen, skipping inbound plan creation...\n") print("No file choosen, skipping inbound plan creation...\n")
194 195
195 def reducePlentyMarketsQty(dataset, folder):
196 def reducePlentyMarketsQty(dataset, folder, upload):
196 197 root = tkinter.Tk() root = tkinter.Tk()
197 198 root.withdraw() root.withdraw()
198 199
 
... ... def reducePlentyMarketsQty(dataset, folder):
227 228 # Read the Data from the location list into a Data Dictionary # Read the Data from the location list into a Data Dictionary
228 229 Data = {} Data = {}
229 230
230 column_names = ['LocationName', 'LocationID', 'ItemID',
231 'VariationNo', 'VariationString', 'ItemTextName',
232 'Barcode', 'Stock', 'WarehouseID', 'VariationID']
231 column_names = ['LocationName', 'LocationID',
232 'VariationNo', 'Barcode', 'Stock',
233 'WarehouseID', 'VariationID']
233 234
234 235 with open(locationlist['filepath'], mode='r', encoding=locationlist['encoding']) as item: with open(locationlist['filepath'], mode='r', encoding=locationlist['encoding']) as item:
235 236 reader = csv.DictReader(item, delimiter=";") reader = csv.DictReader(item, delimiter=";")
 
... ... def reducePlentyMarketsQty(dataset, folder):
259 260 for package in dataset[pick_row]['packages']: for package in dataset[pick_row]['packages']:
260 261 if(dataset[pick_row]['barcode'] == Data[data_row]['Barcode']\ if(dataset[pick_row]['barcode'] == Data[data_row]['Barcode']\
261 262 and dataset[pick_row]['packages'][package]['location'] == Data[data_row]['LocationName']): and dataset[pick_row]['packages'][package]['location'] == Data[data_row]['LocationName']):
262 #print("OLD: {0}\t".format( Data[data_row]['Stock'] ))
263 #print("SHIPPED: {0}\t".format(int(dataset[pick_row]['packages'][package]['quantity'])))
263 #print("OLD: {0} @ location: {1}\t".format( Data[data_row]['Stock'], Data[data_row]['LocationName']))
264 #print("SHIPPED: {0} @ location: {1}\t".format(int(dataset[pick_row]['packages'][package]['quantity']), dataset[pick_row]['packages'][package]['location']))
264 265
265 266 Data[data_row]['Stock'] = int(Data[data_row]['Stock']) - int(dataset[pick_row]['packages'][package]['quantity']) Data[data_row]['Stock'] = int(Data[data_row]['Stock']) - int(dataset[pick_row]['packages'][package]['quantity'])
266 267
 
... ... def reducePlentyMarketsQty(dataset, folder):
270 271 print("Create a list of the current plentymarkets location quantity\n") print("Create a list of the current plentymarkets location quantity\n")
271 272 print("{0}\n".format(altline)) print("{0}\n".format(altline))
272 273 print("Erstelle eine Liste des derzeitigen Bestands der Plentymarkets Lagerplätze\n") print("Erstelle eine Liste des derzeitigen Bestands der Plentymarkets Lagerplätze\n")
273 plenty_export.plentyExport(plentyexport=locationlist, folder=folder.replace('Report','Upload'))
274 export = plenty_export.plentyExport(folder=folder.replace('Report','Upload'), plentyexport=locationlist, dataset=dataset)
274 275
275 Data_and_fields = {'data':Data, 'fields':column_names}
276 # Compare the new location quantities with the old ones
277 difference.compare_locations(locationlist=locationlist, folder=upload, pickdata=dataset, exportdata=export, plenty_data=Data)
278
279 Data_and_fields = {'data':Data, 'fields':column_names, 'export':export}
276 280 return Data_and_fields return Data_and_fields
277 281 else: else:
278 282 print("{0}\n".format(line)) print("{0}\n".format(line))
File packages/syncfile.py changed (mode: 100644) (index 2cd54e9..dfe86b0)
... ... def writeNewCsv(dataset, path, header, name):
33 33 return output_path return output_path
34 34
35 35
36 def importAmazonOrder(export, reportfolder, folder, orderid, plentyid, report='', stockreport=''):
36 def importFBA_Shipment(export, reportfolder, folder, stockreport=''):
37 37 # Define the headers of the new Sync File # Define the headers of the new Sync File
38 column_names = ['CustomerID','FirstName','LastName','OrderID'
39 ,'OrderType','StoreID','ID_der_Adresse'
40 ,'RowVariationID','RowOrderID','Name'
41 ,'Menge','Währung','Typ','Position','Preis',
42 'adressname', 'town', 'countryid']
43
44 # initialize empty strings which get assigned as soon as the
45 # location is scraped from the report
46 first_name = ''
47 last_name = ''
48 customer_id = ''
49 adress_id = ''
50 adressname = ''
51 town = ''
52 countryid = ''
38 column_names = ['SKU', 'ID', 'QTY']
53 39
54 40 # The beginning of the name of the file # The beginning of the name of the file
55 41 syncname = '' syncname = ''
56 42
57 # Here are the predefined names and ids from the plentymarket system
58 first_names = ['KTW1', 'DTM2']
59 last_names = ['c/o Amazon FC KTW1', '']
60 customer_ids = ['1357', '']
61 adress_ids = ['2517', '']
62 adressnames = ['Stangenallee', '']
63 towns = ['Nohra', '']
64 countryids = ['1', '1']
65
66 # Open the report file and scrape the SKU and the delivered amount
67 if(report and not(stockreport)):
43 if(stockreport):
68 44 # initialize a dictionary to save the necessary data # initialize a dictionary to save the necessary data
69 45 Data = {} Data = {}
70 syncname = 'fba-received'
71 with open(report['filepath'], mode='r', encoding=report['encoding']) as item:
72 reader = csv.DictReader(item, delimiter="\t")
73
74 for position, row in enumerate( reader ):
75 # Check the fullfillment-center-id position of the report file
76 # Then assign the correct contact to the first_name, last_name,
77 # customer_id, adress_id variables
78 if(row['fulfillment-center-id'] == 'KTW1'):
79 first_name = first_names[0]
80 last_name = last_names[0]
81 customer_id = customer_ids[0]
82 adress_id = adress_ids[0]
83 adressname = adressnames[0]
84 town = towns[0]
85 countryid = countryids[0]
86 if(row['fulfillment-center-id'] == 'DTM2'):
87 first_name = first_names[1]
88 last_name = last_names[1]
89 customer_id = customer_ids[1]
90 adress_id = adress_ids[1]
91 adressname = adressnames[1]
92 town = towns[1]
93 countryid = countryids[1]
94
95 # define the values for each column in this case the
96 # magic numbers are 104 (the id of our storage facility)
97 # and 3.1 which is the ID of the special FBA Status on Plenty
98 values = [customer_id, first_name, last_name, orderid,
99 '1', plentyid, adress_id,
100 '', '', row['sku'],
101 row['quantity'], 'EUR', 0, position, '',
102 adressname, town, countryid]
103
104 # combine the values with the header names and name each dict key
105 # after the SKU, that way the sku can be used to get the right
106 # Variation ID
107 Data[row['sku']] = dict(zip(column_names, values))
108
109 # Open the stock report and get the shipment values
110 if(stockreport and not(report)):
111 # initialize a dictionary to save the necessary data
112 Data = {}
113 position = 1
114 syncname = 'fba-shipment'
115
116 first_name = first_names[0]
117 last_name = last_names[0]
118 customer_id = customer_ids[0]
119 adress_id = adress_ids[0]
120 adressname = adressnames[0]
121 town = towns[0]
122 countryid = countryids[0]
46 syncname = 'fba_shipment'
123 47
124 48 with open(stockreport['filepath'], mode = 'r', encoding=stockreport['encoding']) as item: with open(stockreport['filepath'], mode = 'r', encoding=stockreport['encoding']) as item:
125 49 reader = csv.DictReader(item, delimiter=';') reader = csv.DictReader(item, delimiter=';')
 
... ... def importAmazonOrder(export, reportfolder, folder, orderid, plentyid, report=''
141 65 if(int(amount) > 8): if(int(amount) > 8):
142 66 amount = 5 * round(int( amount ) / 5) amount = 5 * round(int( amount ) / 5)
143 67
144 values = [customer_id, first_name, last_name, orderid,
145 '1', plentyid, adress_id,
146 '', '', sku,
147 amount, 'EUR', 0, position, '',
148 adressname, town, countryid]
68 values = [sku, '', amount]
149 69
150 70 Data[sku] = dict(zip(column_names, values)) Data[sku] = dict(zip(column_names, values))
151 71
152 position += 1
153
154 # Open the Export file to get the right Variation ID for each SKU on the List
155 with open(export['filepath'], mode='r', encoding=export['encoding']) as item:
156 reader = csv.DictReader(item, delimiter=";")
157
158 for row in reader:
159 # Check if the row contains one of the SKU from the list
160 if(row['VariationNumber'] in [*Data] and row['VariationID']):
161 Data[row['VariationNumber']]['RowVariationID'] = row['VariationID']
162 Data[row['VariationNumber']]['Name'] = row['VariationNumber']
163 Data[row['VariationNumber']]['Preis'] = row['PurchasePrice']
164 # Write the finished Data dictionary into a new file
165
166 shipment = writeNewCsv(dataset=Data, path=folder, header=column_names, name=syncname)
167 if(stockreport and not(report)):
168 picklist_creation.picklist_creation(shipmentlist=shipment, folder=reportfolder, uploadfolder=folder)
169
170 def importNewOrder(export, folder, orderlist, orderid, producerlist, plentyid):
171
172 column_names = ['CustomerID','FirstName','LastName','OrderID'
173 ,'OrderType','StoreID','ID_der_Adresse'
174 ,'RowVariationID','RowOrderID','Name'
175 ,'Menge','Währung','Typ','Position','Preis',
176 'adressname', 'town', 'countryid']
177
178 Data = {}
179 producer_set = set()
180 producer_dict = dict()
181 order_amount = int()
182 with open(orderlist['filepath'], mode='r', encoding=orderlist['encoding']) as item:
183 reader = csv.DictReader(item, delimiter=';')
184
185 for row in reader:
186 try:
187 if(row['producer'] and row['quantity']):
188 # create a set of producers to determine
189 # the number of orders
190 producer_set.add(row['producer'].lower())
191
192 # if the producer name is not already in the dict
193 # add an empty entry to be filled when the producer list
194 # is opened.
195 if(not(row['producer'].lower() in [ *producer_dict ])):
196 producer_dict[row['producer'].lower()] = {
197 'adress':'',
198 'customerid':'',
199 'firstname':'',
200 'lastname':'',
201 'adressname':'',
202 'town':'',
203 'countryid':''
204 }
72 # Open the Export file to get the right Variation ID for each SKU on the List
73 with open(export['filepath'], mode='r', encoding=export['encoding']) as item:
74 reader = csv.DictReader(item, delimiter=";")
205 75
206 # collect the fixed information and sku + quantity
207 values = ['', '', '', '',
208 '1', plentyid, '',
209 '', '', row['sku'],
210 row['quantity'], 'EUR', '1', '', '']
211
212 Data[row['sku']] = dict(zip(column_names, values))
213
214 except KeyError as err:
215 print('The orderlist has a wrong header!\n',
216 'please make sure that there is a: \n',
217 "'sku', 'quantity', 'producer'\n")
218 print(err)
219 sys.exit()
220
221 with open(export['filepath'], mode='r', encoding=export['encoding']) as item:
222 reader = csv.DictReader(item, delimiter=';')
223
224 # Get the variation id and the price
225 for row in reader:
226 # Check if the row contains one of the SKU from the list
227 if(row['VariationNumber'] in [*Data] and row['VariationID']):
228 Data[row['VariationNumber']]['RowVariationID'] = row['VariationID']
229 Data[row['VariationNumber']]['Name'] = row['VariationNumber']
230 Data[row['VariationNumber']]['Preis'] = row['PurchasePrice']
231
232 with open(producerlist, mode='r') as item:
233 reader = csv.DictReader(item, delimiter=';')
234
235 for row in reader:
236 # fill the in the information for the producer_dict
237 if(row['producer'].lower() in [*producer_dict]):
238 producer_dict[row['producer'].lower()]['adress'] = row['adressid']
239 producer_dict[row['producer'].lower()]['customerid'] = row['customerid']
240 producer_dict[row['producer'].lower()]['firstname'] = row['firstname']
241 producer_dict[row['producer'].lower()]['lastname'] = row['lastname']
242 producer_dict[row['producer'].lower()]['adressname'] = row['adressname']
243 producer_dict[row['producer'].lower()]['town'] = row['town']
244 producer_dict[row['producer'].lower()]['countryid'] = row['countryid']
245
246 # Create a new order ID for each producer in the producer
247 order_ID_Keys = ['producer', 'orderid']
248
249 order_ID_Dict = {}
250 # Make an additional list for order ID's for the writing process
251 order_ID_List = []
252
253 for producer in producer_set:
254 order_ID_List.append(orderid)
255 values = [producer, orderid]
256 order_ID_Dict[producer] = dict(zip(order_ID_Keys, values))
257 orderid = int( orderid ) + 1
258
259 # Open the orderlist again, to fill in the producer details
260 with open(orderlist['filepath'], mode = 'r', encoding=orderlist['encoding']) as item:
261 reader = csv.DictReader(item, delimiter=';')
262
263 for row in reader:
264 Data[row['sku']]['CustomerID'] = producer_dict[row['producer'].lower()]['customerid']
265 Data[row['sku']]['FirstName'] = producer_dict[row['producer'].lower()]['firstname']
266 Data[row['sku']]['LastName'] = producer_dict[row['producer'].lower()]['lastname']
267 Data[row['sku']]['ID_der_Adresse'] = producer_dict[row['producer'].lower()]['adress']
268 Data[row['sku']]['adressname'] = producer_dict[row['producer'].lower()]['adressname']
269 Data[row['sku']]['town'] = producer_dict[row['producer'].lower()]['town']
270 Data[row['sku']]['countryid'] = producer_dict[row['producer'].lower()]['countryid']
271
272 Data[row['sku']]['OrderID'] = order_ID_Dict[row['producer'].lower()]['orderid']
273
274 # Write a separate order for each orderID
275 order_collection = {}
276 for order in order_ID_List:
277 order_collection[order] = dict()
278 position = 0
279 for row in Data:
280 if(Data[row]['OrderID'] == order):
281 order_collection[order][ 'entry' + str( position ) ] = Data[row]
282 order_collection[order]['entry' + str( position )]['Position'] = position
283 position += 1
284
285 if(order in [*order_collection] and order_collection[order]):
286 # Write the order collection into .csv files
287 writeNewCsv(dataset=order_collection[order], path=folder, header=column_names, name=order_collection[order]['entry1']['LastName'] + str(order))
288
289 return orderid
76 for row in reader:
77 # Check if the row contains one of the SKU from the list
78 if(row['VariationNumber'] in [*Data] and row['VariationID']):
79 Data[row['VariationNumber']]['ID'] = row['VariationID']
80 # Write the finished Data dictionary into a new file
81
82 shipment = writeNewCsv(dataset=Data, path=folder, header=column_names, name=syncname)
83 if(stockreport):
84 picklist_creation.picklist_creation(shipmentlist=shipment, folder=reportfolder, uploadfolder=folder)
85 else:
86 print("No storage report recieved, abort function!")
87 print("press ENTER to continue..")
88 input()
89 sys.exit()
290 90
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/initBasti/Complete_Order_Plenty_Update

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/initBasti/Complete_Order_Plenty_Update

Clone this repository using git:
git clone git://git.rocketgit.com/user/initBasti/Complete_Order_Plenty_Update

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main