diff --git a/mo/jobs/protocols.py b/mo/jobs/protocols.py
index fd9d08d5f8d18be9ee4187876f85053407150925..65b6b9e7d8e3970cfd63371be3697d9daf306d6d 100644
--- a/mo/jobs/protocols.py
+++ b/mo/jobs/protocols.py
@@ -178,12 +178,13 @@ def handle_create_protocols(the_job: TheJob):
 #
 
 
-def schedule_process_scans(contest: db.Contest, site: Optional[db.Place], for_user: db.User, tasks: List[db.Task], in_file_names: List[str]) -> int:
+def schedule_process_scans(contest: db.Contest, site: Optional[db.Place], scans_type: str, for_user: db.User, tasks: List[db.Task], in_file_names: List[str]) -> int:
     place = site or contest.place
 
+    scans_desc = "odevzdaných řešení" if scans_type == "solution" else "oprav"
     the_job = TheJob()
     job = the_job.create(db.JobType.process_scans, for_user)
-    job.description = f'Zpracování skenů {contest.round.round_code_short()} {place.name}'
+    job.description = f'Zpracování skenů {scans_desc} {contest.round.round_code_short()} {place.name}'
 
     in_files = []
     num_files = 0
@@ -195,6 +196,7 @@ def schedule_process_scans(contest: db.Contest, site: Optional[db.Place], for_us
     assert in_files
 
     job.in_json = {
+        'type': scans_type,
         'contest_id': contest.contest_id,
         'site_id': site.place_id if site else None,
         'task_ids': [t.task_id for t in tasks],
@@ -396,11 +398,12 @@ def schedule_sort_scans(job_id: int, for_user: db.User) -> int:
     contest = sess.query(db.Contest).options(joinedload(db.Contest.round)).get(job.in_json['contest_id'])
     assert contest is not None
 
+    scans_desc = "oprav" if 'type' in job.in_json and job.in_json['type'] == "feedback" else "odevzdaných řešení"
     job.type = db.JobType.sort_scans
     job.created_at = mo.now
     job.expires_at = None
     job.user = for_user
-    job.description = f'Rozdělení již roztříděných skenů {contest.round.round_code_short()}'
+    job.description = f'Rozdělení již roztříděných skenů {scans_desc} {contest.round.round_code_short()}'
 
     the_job.submit()
     return the_job.job_id
@@ -426,6 +429,7 @@ def handle_sort_scans(the_job: TheJob):
     site_id = job.in_json['site_id']        # type: ignore
     task_ids = job.in_json['task_ids']      # type: ignore
     in_files: List[str] = job.in_json['in_files']  # type: ignore
+    paper_type = db.PaperType.feedback if 'type' in job.in_json and job.in_json['type'] == 'feedback' else db.PaperType.solution
 
     sess = db.get_session()
     contest = sess.query(db.Contest).options(joinedload(db.Contest.round)).get(contest_id)
@@ -473,7 +477,7 @@ def handle_sort_scans(the_job: TheJob):
                 task=task,
                 for_user_obj=user,
                 uploaded_by_obj=job.user,
-                type=db.PaperType.solution,
+                type=paper_type,
                 note='Z hromadného skenování',
             ))
 
@@ -520,15 +524,20 @@ def handle_sort_scans(the_job: TheJob):
                 'task': sol.task.task_id,
             },
         )
+        sols_map[index] = sol
 
     for index in papers:
         paper = papers[index]
         sess.add(paper.paper)
         if index in sols_map:
-            sols_map[index].final_submit_obj = paper.paper
-        elif index in sols_to_create:
-            sols_to_create[index].final_submit_obj = paper.paper
+            if paper_type == db.PaperType.solution:
+                sols_map[index].final_submit_obj = paper.paper
+            else:
+                sols_map[index].final_feedback_obj = paper.paper
 
     sess.commit()
-    job.result = 'Celkem ' + mo.util_format.inflect_number(len(papers), 'roztříděné řešení', 'roztříděná řešení', 'roztříděných řešení')
+    if paper_type == db.PaperType.solution:
+        job.result = 'Celkem ' + mo.util_format.inflect_number(len(papers), 'roztříděné řešení', 'roztříděná řešení', 'roztříděných řešení')
+    else:
+        job.result = 'Celkem ' + mo.util_format.inflect_number(len(papers), 'roztříděná oprava', 'roztříděné opravy', 'roztříděných oprav')
     the_job.expires_in_minutes = config.JOB_EXPIRATION_LONG
diff --git a/mo/web/org_contest.py b/mo/web/org_contest.py
index 60f7c92f80e72d1eb9ad6048f5f69035fce70b16..b76724c92a735585203fb2af482fc0699c71aeec 100644
--- a/mo/web/org_contest.py
+++ b/mo/web/org_contest.py
@@ -1707,6 +1707,7 @@ def org_contest_protocols(ct_id: int, site_id: Optional[int] = None):
 
 class ProcessScansForm(FlaskForm):
     files = wtforms.MultipleFileField('Soubory PDF se skeny', validators=[validators.required()])
+    scans_type = wtforms.RadioField('Typ skenů', choices=[('solution', 'Odevzdaná řešení'), ('feedback', 'Opravená řešení')])
     process_scans = wtforms.SubmitField('Nahrát skeny')
 
 
@@ -1737,7 +1738,7 @@ def org_contest_scans(ct_id: int, site_id: Optional[int] = None):
     if proc_form.validate_on_submit() and proc_form.process_scans.data:
         files = request.files.getlist(proc_form.files.name)
         job_id = mo.jobs.protocols.schedule_process_scans(
-            contest, site, g.user,
+            contest, site, proc_form.scans_type.data, g.user,
             tasks=[t for t in tasks if getattr(proc_form, f'task_{t.task_id}').data],
             in_file_names=[f.stream.name for f in files],
         )
@@ -1747,27 +1748,28 @@ def org_contest_scans(ct_id: int, site_id: Optional[int] = None):
     jobs_query = sess.query(db.Job).filter_by(type=db.JobType.process_scans)
     if not g.user.is_admin:
         jobs_query = jobs_query.filter_by(user=g.user)
-    jobs = []
-    job_tasks: Dict[int, List[db.Task]] = {}
+    jobs: List[Tuple[db.Job, str, List[db.Task]]] = []
     for job in jobs_query.all():
         if 'contest_id' not in job.in_json or job.in_json['contest_id'] != ct_id:
             continue
         if site_id is not None and ('site_id' not in job.in_json or job.in_json['site_id'] != site_id):
             continue
 
-        job_tasks[job.job_id] = []
+        tasks = []
         for task_id in job.in_json['task_ids']:
             if task_id in tasks_map:
-                job_tasks[job.job_id].append(tasks_map[task_id])
+                tasks.append(tasks_map[task_id])
+        scans_type = job.in_json['type'] if 'type' in job.in_json else 'solution'
+        scans_type_names = {'solution': 'Odevzdaná řešení', 'feedback': 'Opravená řešení'}
 
-        jobs.append(job)
+        jobs.append((job, scans_type_names[scans_type], tasks))
 
     return render_template(
         'org_contest_scans.html',
         ctx=ctx,
         proc_form=proc_form,
         proc_task_fields=proc_task_fields,
-        jobs=jobs, job_tasks=job_tasks,
+        jobs=jobs,
     )
 
 
@@ -1784,9 +1786,6 @@ def org_contest_scans_process(ct_id: int, job_id: int, site_id: Optional[int] =
     contest = ctx.contest
     assert contest
 
-    if not ctx.rights.can_upload_feedback():
-        raise werkzeug.exceptions.Forbidden()
-
     sess = db.get_session()
 
     # Získáme job a zkontrolujeme, že je to správný job, máme na něj práva a už doběhl
@@ -1800,6 +1799,18 @@ def org_contest_scans_process(ct_id: int, job_id: int, site_id: Optional[int] =
         flash('Dávka naskenovaných úloh nebyla dosud dokončena.')
         return redirect(ctx.url_for('org_contest_scans'))
 
+    if 'type' in job.in_json and job.in_json['type'] == 'feedback':
+        scans_type = 'feedback'
+        scans_type_name = 'oprava'
+    else:
+        scans_type = 'solution'
+        scans_type_name = 'řešení'
+
+    if scans_type == 'solution' and not ctx.rights.can_upload_solutions():
+        raise werkzeug.exceptions.Forbidden()
+    if scans_type == 'feedback' and not ctx.rights.can_upload_feedback():
+        raise werkzeug.exceptions.Forbidden()
+
     pages = sess.query(db.ScanPage).filter_by(job_id=job_id).order_by('file_nr', 'page_nr').all()
     tasks = sess.query(db.Task).filter(db.Task.task_id.in_(job.in_json['task_ids'])).order_by('code').all()
     pion_query = sess.query(db.Participation).filter(
@@ -1891,14 +1902,14 @@ def org_contest_scans_process(ct_id: int, job_id: int, site_id: Optional[int] =
         for pion in pions:
             index = (task.task_id, pion.user_id)
             if index not in sol_map:
-                warnings.append(f'Chybí řešení úlohy {task.code} {task.name} účastníka {pion.user.full_name()}')
+                warnings.append(f'Chybí {scans_type_name} úlohy {task.code} {task.name} účastníka {pion.user.full_name()}')
 
     if process_form.validate_on_submit() and process_form.process_all.data:
         if len(errors) > 0:
             flash('Nelze zpracovat, dokud kontrola vrací chyby. Nejdříve je opravte.')
             return redirect(self_url)
         mo.jobs.protocols.schedule_sort_scans(job_id, for_user=g.user)
-        flash('Skeny zařazeny ke zpracování, během několika chvil se řešení uloží k soutěžícím.', 'success')
+        flash('Skeny zařazeny ke zpracování, během několika chvil se uloží k soutěžícím.', 'success')
         return redirect(url_for('org_jobs'))
 
     def png_small(page: db.ScanPage) -> str:
@@ -1917,6 +1928,7 @@ def org_contest_scans_process(ct_id: int, job_id: int, site_id: Optional[int] =
         'org_contest_scans_process.html',
         ctx=ctx,
         job=job,
+        scans_type=scans_type,
         tasks=tasks,
         pions=pions,
         pages=pages,
diff --git a/mo/web/templates/org_contest_scans.html b/mo/web/templates/org_contest_scans.html
index 70a5731c4d4bddb3726a2c9f94267e49ba1fca4e..40fbb320cf04cd1ba6419578c545c8f2ce659155 100644
--- a/mo/web/templates/org_contest_scans.html
+++ b/mo/web/templates/org_contest_scans.html
@@ -33,6 +33,14 @@ jejich přiřazení jednotlivým soutěžícím.
 		</div>
 	{% endif %}
 	{{ field(proc_form.files) }}
+	<div class='form-group required'>
+		<label class='control-label col-lg-3'>Typ skenů</label>
+		<div class='col-lg-7'>
+			{% for f in proc_form.scans_type %}
+				<div class="radio"><label for="{{ f.id }}">{{ f(required="required") }}{{ f.label.text }}</label></div>
+			{% endfor %}
+		</div>
+	</div>
 	{{ field(proc_form.process_scans) }}
 </form>
 
@@ -43,16 +51,18 @@ jejich přiřazení jednotlivým soutěžícím.
 	<thead>
 		<tr>
 			<th>Datum
+			<th>Typ skenů
 			<th>Úlohy
 			<th>Stav
 			{% if g.user.is_admin %}<th>Vlastník{% endif %}
 			<th>Akce
 		</tr>
 	</thead>
-	{% for job in jobs %}
+	{% for (job, type, tasks) in jobs %}
 	<tr class="job-{{ job.state.name }}">
 		<td>{{ job.created_at|timeformat }}
-		<td>{% for task in job_tasks[job.job_id] %}{{ task.code }} {% endfor %}
+		<td>{{ type }}
+		<td>{{ tasks|map(attribute='code')|join(', ') }}
 		<td>
 		{% if job.state == JobState.done %}
 			Připraveno k roztřídění
diff --git a/mo/web/templates/org_contest_scans_process.html b/mo/web/templates/org_contest_scans_process.html
index 20540ac1bb1abe80337275d558d713a6c638b756..03af33e3767c2553a215b3c54944205bad61850e 100644
--- a/mo/web/templates/org_contest_scans_process.html
+++ b/mo/web/templates/org_contest_scans_process.html
@@ -5,11 +5,14 @@
 	<script src="{{ asset_url('js/autocomplete.js') }}" type="text/javascript"></script>
 {% endblock %}
 
+{% set for_solutions = scans_type == 'solution' %}
+
+{% set scans_title = 'odevzdaných řešení' if for_solutions else 'oprav' %}
 {% block title %}
-Třídění skenů pro {{ ctx.round.name|lower }} kategorie {{ ctx.round.category }}
+Třídění skenů {{ scans_title }} pro {{ ctx.round.name|lower }} kategorie {{ ctx.round.category }}
 {% endblock %}
 {% block breadcrumbs %}
-{{ ctx.breadcrumbs(action="Třídění skenů") }}
+{{ ctx.breadcrumbs(action="Třídění skenů " + scans_title) }}
 {% endblock %}
 {% block body %}
 
@@ -25,8 +28,8 @@ setTimeout(function () { location.reload(1); }, 10_000);
 
 <p>Napravo můžete klikáním vybírat jednotlivé naskenované stránky a pomocí vrchních políček je přiřazovat jednotlivým úlohám a soutěžícím. Pokud
 je nějaké řešení přes více stránek, musí na sebe navazovat číslování stránek. Až bude vše správně zatříděné, můžete aktuální stav uložit
-tlačítkem <b>[Uložit]</b>. Poté můžete celou dávku odeslat ke zpracování pomocí <b>[Ukončit a zpracovat]</b> (řešení se uloží k soutěžícím,
-tuto akci nelze vzít zpět).
+tlačítkem <b>[Uložit]</b>. Poté můžete celou dávku odeslat ke zpracování pomocí <b>[Ukončit a zpracovat]</b>
+({{ 'řešení' if for_solutions else 'opravy' }} se uloží k soutěžícím, tuto akci nelze vzít zpět).
 
 {% if errors or warnings %}
 <div class="collapsible">